// XTPCalendarRecurrencePattern.cpp: implementation of the CXTPCalendarRecurrencePattern class. // // This file is a part of the XTREME CALENDAR MFC class library. // (c)1998-2012 Codejock Software, All Rights Reserved. // // THIS SOURCE FILE IS THE PROPERTY OF CODEJOCK SOFTWARE AND IS NOT TO BE // RE-DISTRIBUTED BY ANY MEANS WHATSOEVER WITHOUT THE EXPRESSED WRITTEN // CONSENT OF CODEJOCK SOFTWARE. // // THIS SOURCE CODE CAN ONLY BE USED UNDER THE TERMS AND CONDITIONS OUTLINED // IN THE XTREME TOOLKIT PRO LICENSE AGREEMENT. CODEJOCK SOFTWARE GRANTS TO // YOU (ONE SOFTWARE DEVELOPER) THE LIMITED RIGHT TO USE THIS SOFTWARE ON A // SINGLE COMPUTER. // // CONTACT INFORMATION: // support@codejock.com // http://www.codejock.com // ///////////////////////////////////////////////////////////////////////////// #include "stdafx.h" #include "Common/XTPPropExchange.h" #include "Common/XTPSmartPtrInternalT.h" #include "Common/XTPVC80Helpers.h" #include "XTPCalendarDefines.h" #include "XTPCalendarUtils.h" #include "XTPCalendarEvents.h" #include "XTPCalendarEvent.h" #include "XTPCalendarData.h" #include "XTPCalendarRecurrencePattern.h" #include "XTPCalendarCustomProperties.h" static LPCTSTR cszRPatt_PatternID = _T("PatternID"); static LPCTSTR cszRPatt_StartTime = _T("StartTime"); static LPCTSTR cszRPatt_DurationMinutes = _T("DurationMinutes"); static LPCTSTR cszRPatt_RecurrenceType = _T("RecurrenceType"); static LPCTSTR cszRPatt_RecurrenceOpt_Data1 = _T("RecurrenceOpt_Data1"); static LPCTSTR cszRPatt_RecurrenceOpt_Data2 = _T("RecurrenceOpt_Data2"); static LPCTSTR cszRPatt_RecurrenceOpt_Data3 = _T("RecurrenceOpt_Data3"); static LPCTSTR cszRPatt_PatternStartDate = _T("PatternStartDate"); static LPCTSTR cszRPatt_UseEndMethod = _T("UseEndMethod"); static LPCTSTR cszRPatt_PatternEndDate = _T("PatternEndDate"); static LPCTSTR cszRPatt_EndAfterOccurrences = _T("EndAfterOccurrences"); static LPCTSTR cszRPatt_MasterEventID = _T("MasterEventID"); //=========================================================================== static const LPCTSTR cszOccRMD_Count = _T("*xtp*OccRMD_Count"); static const LPCTSTR cszOccRMD_StartTime = _T("*xtp*OccRMD_Start"); static const LPCTSTR cszOccRMD_EndTime = _T("*xtp*OccRMD_End"); static const LPCTSTR cszOccRMD_IsReminder = _T("*xtp*OccRMD_IsReminder"); static const LPCTSTR cszOccRMD_NextReminderTime_Snoozed = _T("*xtp*OccRMD_NextReminderTime_Snoozed"); //=========================================================================== #ifdef _DEBUG #define new DEBUG_NEW #undef THIS_FILE static char THIS_FILE[] = __FILE__; #endif //////////////////////////////////////////////////////////////////////////// #define XTP_RECPATT_PROP_SET_DATA_VER 1 //////////////////////////////////////////////////////////////////////////// IMPLEMENT_DYNAMIC(CXTPCalendarRecurrencePattern, CCmdTarget) ///////////////////////////////////////////////////////////////////////////// CXTPCalendarRecurrencePattern::CXTPCalendarRecurrencePattern(CXTPCalendarData* pDataProvider) { ASSERT(pDataProvider); m_ptrDataProvider = pDataProvider; CMDTARGET_ADDREF(m_ptrDataProvider); m_dwPatternID = XTP_CALENDAR_UNKNOWN_RECURRENCE_PATTERN_ID; m_dwMasterEventID = XTP_CALENDAR_UNKNOWN_EVENT_ID; m_dtStartTime = (DATE)0; //.SetStatus(COleDateTime::null); m_nDurationMinutes = 0; m_Options.m_nRecurrenceType = xtpCalendarRecurrenceUnknown; m_dtPatternStartDate = (DATE)0; //.SetStatus(COleDateTime::null); m_pCustomProperties = new CXTPCalendarCustomProperties; m_parExceptionEvents = new CXTPCalendarEvents(); } CXTPCalendarRecurrencePattern::~CXTPCalendarRecurrencePattern() { CMDTARGET_RELEASE(m_ptrDataProvider); CMDTARGET_RELEASE(m_pCustomProperties); CMDTARGET_RELEASE(m_parExceptionEvents); } COleDateTime CXTPCalendarRecurrencePattern::GetStartOccurrenceTime(COleDateTime dtOccurrenceStartDate) const { COleDateTime dtStartOcc = GetStartTime(); dtStartOcc = CXTPCalendarUtils::UpdateDate(dtStartOcc, dtOccurrenceStartDate); return dtStartOcc; } COleDateTime CXTPCalendarRecurrencePattern::GetEndOccurrenceTime(COleDateTime dtOccurrenceStartDate) const { COleDateTimeSpan spDuration = GetDuration(); COleDateTime dtEndOcc = GetStartOccurrenceTime(dtOccurrenceStartDate); dtEndOcc += spDuration; return dtEndOcc; } void CXTPCalendarRecurrencePattern::_GetOccurrenceStartEnd(const COleDateTime& dtOccStartDate, COleDateTime& rdtOccStart, COleDateTime& rdtOccEnd) const { rdtOccStart = GetStartTime(); rdtOccStart = CXTPCalendarUtils::UpdateDate(rdtOccStart, dtOccStartDate); COleDateTimeSpan spDuration = GetDuration(); rdtOccEnd = rdtOccStart + spDuration; } void CXTPCalendarRecurrencePattern::SetPatternStartDate(COleDateTime dtStart) { m_dtPatternStartDate = CXTPCalendarUtils::ResetTime(dtStart); if (m_dtPatternStartDate.GetStatus() == COleDateTime::valid && m_PatternEnd.m_dtPatternEndDate.GetStatus() == COleDateTime::valid && m_dtPatternStartDate > m_PatternEnd.m_dtPatternEndDate) { m_PatternEnd.m_dtPatternEndDate = m_dtPatternStartDate; } } void CXTPCalendarRecurrencePattern::SetPatternEndDate(COleDateTime dtEnd) { m_PatternEnd.m_dtPatternEndDate = CXTPCalendarUtils::ResetTime(dtEnd); m_PatternEnd.m_nUseEnd = xtpCalendarPatternEndDate; if (m_dtPatternStartDate.GetStatus() == COleDateTime::valid && m_PatternEnd.m_dtPatternEndDate.GetStatus() == COleDateTime::valid && m_PatternEnd.m_dtPatternEndDate < m_dtPatternStartDate) { m_dtPatternStartDate = m_PatternEnd.m_dtPatternEndDate; } } BOOL CXTPCalendarRecurrencePattern::GetOccurrences(CXTPCalendarEvents* pEventsArray, COleDateTime dtStart, COleDateTime dtEnd, CXTPCalendarEvent* pMasterEvent) { if (!pMasterEvent || !pEventsArray) { ASSERT(FALSE); return FALSE; } COleDateTime dtStartOcc, dtEndOcc; _GetOccurrenceStartEnd(dtStart, dtStartOcc, dtEndOcc); int nOccDays = CXTPCalendarUtils::GetEventPeriodDays(dtStartOcc, dtEndOcc); ASSERT(nOccDays > 0); nOccDays = max(1, nOccDays); COleDateTime dtStartCheckOcc = dtStart; if (dtStart == dtEnd) { //- check for previous multiday occurrences --- if (nOccDays > 1) { COleDateTimeSpan spCheckMDOcc(nOccDays - 1, 0, 0, 0); dtStartCheckOcc = dtStart - spCheckMDOcc; } } CXTPCalendarDatesArray arDates; GetOccurrencesDates(arDates, dtStartCheckOcc, dtEnd); COleDateTime dtI; int nCount = (int) arDates.GetSize(); for (int i = 0; i < nCount; i++) { dtI = arDates[i]; _GetOccurrenceStartEnd(dtI, dtStartOcc, dtEndOcc); ASSERT(!(CXTPCalendarUtils::ResetTime(dtEndOcc) < CXTPCalendarUtils::ResetTime(dtStart) || CXTPCalendarUtils::ResetTime(dtStartOcc) > CXTPCalendarUtils::ResetTime(dtEnd))); CXTPCalendarEventPtr ptrOccurrenceEvent(FindException(dtStartOcc, dtEndOcc), TRUE); if (ptrOccurrenceEvent) { continue; } else { ptrOccurrenceEvent = pMasterEvent->CloneForOccurrence(dtStartOcc, dtEndOcc); if (ptrOccurrenceEvent && ptrOccurrenceEvent->GetStartTime() < pMasterEvent->GetLastModificationTime()) { ptrOccurrenceEvent->SetReminder(FALSE); } } if (ptrOccurrenceEvent) { m_arOccReminders.UpdateOccIfNeed(ptrOccurrenceEvent, this); pEventsArray->Add(ptrOccurrenceEvent); } } FindExceptions(pEventsArray, dtStart, dtEnd); return TRUE; } void CXTPCalendarRecurrencePattern::GetOccurrencesDates(CXTPCalendarDatesArray& rArDates, COleDateTime dtStart, COleDateTime dtEnd) const { COleDateTime dtStart0 = CXTPCalendarUtils::ResetTime(dtStart); COleDateTime dtEnd0 = CXTPCalendarUtils::SetTime_235959(dtEnd); int nMaxOccCount = INT_MAX; int nOccCount = 0; COleDateTime dtI; COleDateTime dtE1(9999, 1, 1, 0, 0, 0); COleDateTime dtS1 = max(dtStart0, m_dtPatternStartDate); CString s; switch (m_PatternEnd.m_nUseEnd) { case xtpCalendarPatternEndDate: dtE1 = min(m_PatternEnd.m_dtPatternEndDate, dtEnd0); break; case xtpCalendarPatternEndAfterOccurrences: nMaxOccCount = m_PatternEnd.m_nEndAfterOccurrences; dtE1 = dtEnd0; break; case xtpCalendarPatternEndNoDate: dtE1 = dtEnd0; break; default: ASSERT(FALSE); return; } switch (m_Options.m_nRecurrenceType) { case xtpCalendarRecurrenceDaily: { //------------------------------------------------------ // 1. Every N days: // m_nInterval = N, // m_nDayOfWeekMask = xtpCalendarDayAllWeek //------------------------------------------------------ // 2. Every week day (Mo-Fr): // m_nInterval = 1, // m_nDayOfWeekMask = xtpCalendarDayMo_Fr //------------------------------------------------------ int nDays = m_Options.m_Daily.nIntervalDays; if (m_Options.m_Daily.bEveryWeekDayOnly) { ASSERT(nDays == 1); nDays = 1; } COleDateTimeSpan spStep(nDays, 0, 0, 0); if (m_dtPatternStartDate < dtStart0) { //--- shift start date -------------- COleDateTimeSpan spDiff = dtStart0 - m_dtPatternStartDate; int nDiff_Days = (int)CXTPCalendarUtils::GetTotalDays(spDiff); ASSERT(nDiff_Days >= 1); int nJump_Days = (nDiff_Days / nDays) * nDays; spDiff.SetDateTimeSpan(nJump_Days, 0, 0, 0); dtS1 = m_dtPatternStartDate + spDiff; if (dtS1 < dtStart0) { dtS1 += spStep; } //--- calculate occurrences ---------- if (nMaxOccCount != INT_MAX) { nOccCount = nDiff_Days / nDays; if (m_Options.m_Daily.bEveryWeekDayOnly) { int nWDstart = m_dtPatternStartDate.GetDayOfWeek(); int nBasis1_days = (nDiff_Days - (7 - nWDstart)); int nSatDays = nBasis1_days / 7 + ((nBasis1_days % 7) ? 1 : 0); int nBasis6_days = (nDiff_Days - (6 - nWDstart)); int nSunDays = nBasis6_days / 7 + ((nBasis6_days % 7) ? 1 : 0); nOccCount -= nSatDays + nSunDays; } } } else { dtS1 = m_dtPatternStartDate; } for (dtI = dtS1; dtI <= dtE1 && nOccCount < nMaxOccCount; dtI += spStep) { if (m_Options.m_Daily.bEveryWeekDayOnly) { int nWD = dtI.GetDayOfWeek(); // 1-Sunday, 2 - Monday, ... if (nWD == 1 || nWD == 7) { continue; } } ASSERT(dtI >= dtStart0 && dtI <= dtE1); rArDates.Add(dtI); nOccCount++; } } break; case xtpCalendarRecurrenceWeekly: { //------------------------------------------------------ // 1. Every N weeks: // m_nInterval = N, // m_nDayOfWeekMask = <*MASK*> //------------------------------------------------------ int nWeeksStep = m_Options.m_Weekly.nIntervalWeeks; if (nWeeksStep <= 0) { ASSERT(FALSE); nWeeksStep = 1; } COleDateTimeSpan spStep(1, 0, 0, 0); COleDateTimeSpan spStepWeeks((nWeeksStep)*7, 0, 0, 0); int nFirstDayOfWeekIndex = 2; // Always Monday (as in MS Outlook) COleDateTimeSpan spWeek(7, 0, 0, 0); COleDateTime dtPatSW = CXTPCalendarUtils::ShiftDateToWeekBegin(m_dtPatternStartDate, nFirstDayOfWeekIndex); dtPatSW = CXTPCalendarUtils::ResetTime(dtPatSW); COleDateTime dtOccFrame1 = dtPatSW; COleDateTime dtOccFrame2 = dtOccFrame1 + spWeek; if (m_dtPatternStartDate < dtStart0) { int nPatDW = m_dtPatternStartDate.GetDayOfWeek(); int nMask = CXTPCalendarUtils::MakeDaysOfWeekMask_Mo_(nPatDW); COleDateTime dtStart0SW = CXTPCalendarUtils::ShiftDateToWeekBegin(dtStart0, nFirstDayOfWeekIndex); COleDateTimeSpan spDiff = dtStart0SW - dtPatSW; int nDiff_Weeks = (int)spDiff.GetTotalDays() / 7; ASSERT((int)spDiff.GetTotalDays() % 7 == 0); int nJump_Weeks = (nDiff_Weeks / nWeeksStep) * nWeeksStep; spDiff.SetDateTimeSpan(nJump_Weeks * 7, 0, 0, 0); dtOccFrame1 = dtPatSW + spDiff; dtOccFrame2 = dtOccFrame1 + spWeek; //s.Format(_T("nOccCount=%d nMaxOccCount=%d nDiff_Weeks=%d nJump_Weeks=%d nPatDW=%d nMask=%d\n"), nOccCount, nMaxOccCount, nDiff_Weeks, nJump_Weeks, nPatDW, nMask); //TRACE(s); //BOOL a[7]; //for (int i = 1; i <= 7; i++) //{ // int nTestMask = 1 << (i - 1); // if (nTestMask & m_Options.m_Weekly.nDayOfWeekMask) // a[i - 1] = TRUE; // else // a[i - 1] = FALSE; //} int nDW = CXTPCalendarUtils::GetDayOfWeekCount(m_Options.m_Weekly.nDayOfWeekMask); if (nMask) { nOccCount = nDW; nOccCount *= nJump_Weeks / nWeeksStep; } int nSkippedMask = CXTPCalendarUtils::MakeDaysOfWeekMask_Mo_(m_dtPatternStartDate.GetDayOfWeek()); nOccCount -= CXTPCalendarUtils::GetDayOfWeekCount(nSkippedMask & m_Options.m_Weekly.nDayOfWeekMask); int nAddedMask = CXTPCalendarUtils::MakeDaysOfWeekMask_Mo_(dtS1.GetDayOfWeek()); nOccCount += CXTPCalendarUtils::GetDayOfWeekCount(nAddedMask & m_Options.m_Weekly.nDayOfWeekMask); } for (dtI = dtS1; dtI <= dtE1 && nOccCount < nMaxOccCount; dtI += spStep) { if (nWeeksStep > 1 && !(dtOccFrame1 <= dtI && dtI < dtOccFrame2)) { dtOccFrame1 += spStepWeeks; dtOccFrame2 = dtOccFrame1 + spWeek; dtI = dtOccFrame1 - spStep; continue; } int nWD = dtI.GetDayOfWeek(); // 1-Sunday, 2 - Monday, ... int nWDMask = CXTPCalendarUtils::GetDayOfWeekMask(nWD); if (m_Options.m_Weekly.nDayOfWeekMask & nWDMask) { ASSERT(dtI >= dtStart0 && dtI <= dtE1); rArDates.Add(dtI); nOccCount++; } } } break; case xtpCalendarRecurrenceMonthly: { //------------------------------------------------------ // 1. Day N of every M month(s): // m_nDayOfMonth = N, // m_nInterval = M //------------------------------------------------------ int nMInterval = m_Options.m_Monthly.nIntervalMonths; if (nMInterval <= 0) { ASSERT(FALSE); nMInterval = 1; } COleDateTime dtOccFrame1 = CXTPCalendarUtils::ResetTime(m_dtPatternStartDate); CXTPCalendarUtils::UpdateMonthDay(dtOccFrame1, 1); if (m_dtPatternStartDate < dtStart0) { int nPS_Year = m_dtPatternStartDate.GetYear(); int nPS_Month = m_dtPatternStartDate.GetMonth(); int nPS_GlobalMonth = nPS_Year * 12 + nPS_Month; int nS0_Year = dtStart0.GetYear(); int nS0_Month = dtStart0.GetMonth(); int nS0_GlobalMonth = nS0_Year * 12 + nS0_Month; int nMonthDiff = nS0_GlobalMonth - nPS_GlobalMonth; int nJump_Month = (nMonthDiff / nMInterval) * nMInterval; CXTPCalendarUtils::ShiftDate_Month(dtOccFrame1, nJump_Month, 1); nOccCount = nJump_Month / nMInterval; if (m_dtPatternStartDate.GetDay() > m_Options.m_Monthly.nDayOfMonth) nOccCount--; if (dtS1.GetDay() > m_Options.m_Monthly.nDayOfMonth) nOccCount++; } COleDateTime dtOcc(dtOccFrame1); VERIFY(CXTPCalendarUtils::UpdateMonthDay(dtOcc, m_Options.m_Monthly.nDayOfMonth)); if (dtOcc >= dtS1) { dtS1 = dtOcc; } else { dtS1 = dtOcc; CXTPCalendarUtils::ShiftDate_Month(dtS1, m_Options.m_Monthly.nIntervalMonths, m_Options.m_Monthly.nDayOfMonth); } for (dtI = dtS1; dtI <= dtE1 && nOccCount < nMaxOccCount;) { ASSERT(dtI >= dtStart0 && dtI <= dtE1); rArDates.Add(dtI); nOccCount++; CXTPCalendarUtils::ShiftDate_Month(dtI, m_Options.m_Monthly.nIntervalMonths, m_Options.m_Monthly.nDayOfMonth); } } break; case xtpCalendarRecurrenceMonthNth: { //------------------------------------------------------ //~ The I:{First, second, ..., Last> //~ W:{Day, WeekDay, WeekEndDay, Su, Mo, ...} //~ of every M month(s): //------------------------------------------------------ int nMInterval = m_Options.m_MonthNth.nIntervalMonths; if (nMInterval <= 0) { ASSERT(FALSE); nMInterval = 1; } COleDateTime dtOccFrame1 = CXTPCalendarUtils::ResetTime(m_dtPatternStartDate); CXTPCalendarUtils::UpdateMonthDay(dtOccFrame1, 1); if (m_dtPatternStartDate < dtStart0) { int nPS_Year = m_dtPatternStartDate.GetYear(); int nPS_Month = m_dtPatternStartDate.GetMonth(); int nPS_GlobalMonth = nPS_Year * 12 + nPS_Month; int nS0_Year = dtStart0.GetYear(); int nS0_Month = dtStart0.GetMonth(); int nS0_GlobalMonth = nS0_Year * 12 + nS0_Month; int nMonthDiff = nS0_GlobalMonth - nPS_GlobalMonth; int nJump_Month = (nMonthDiff / nMInterval) * nMInterval; CXTPCalendarUtils::ShiftDate_Month(dtOccFrame1, nJump_Month, 1); nOccCount = nJump_Month / nMInterval; COleDateTime dtTestMD(m_dtPatternStartDate); CXTPCalendarUtils::CalcDayOfMonth(dtTestMD, m_Options.m_MonthNth.nWhichDay, m_Options.m_MonthNth.nWhichDayMask); if (m_dtPatternStartDate.GetDay() > dtTestMD.GetDay()) { nOccCount--; } dtTestMD = dtS1; CXTPCalendarUtils::CalcDayOfMonth(dtTestMD, m_Options.m_MonthNth.nWhichDay, m_Options.m_MonthNth.nWhichDayMask); if (dtS1.GetDay() > dtTestMD.GetDay()) { nOccCount++; } } COleDateTime dtOcc(dtOccFrame1); BOOL bRes = CXTPCalendarUtils::CalcDayOfMonth(dtOcc, m_Options.m_MonthNth.nWhichDay, m_Options.m_MonthNth.nWhichDayMask); if (!bRes) { return; } if (dtOcc >= dtS1) { dtS1 = dtOcc; } else { dtS1 = dtOcc; CXTPCalendarUtils::ShiftDate_Month(dtS1, nMInterval); bRes = CXTPCalendarUtils::CalcDayOfMonth(dtS1, m_Options.m_MonthNth.nWhichDay, m_Options.m_MonthNth.nWhichDayMask); if (!bRes) { return; } } for (dtI = dtS1; dtI <= dtE1 && nOccCount < nMaxOccCount;) //for (dtI = dtS1; dtI >= dtStart0 && dtI <= dtE1 && nOccCount < nMaxOccCount;) { ASSERT(dtI >= dtStart0 && dtI <= dtE1); rArDates.Add(dtI); nOccCount++; CXTPCalendarUtils::ShiftDate_Month(dtI, nMInterval); bRes = CXTPCalendarUtils::CalcDayOfMonth(dtI, m_Options.m_MonthNth.nWhichDay, m_Options.m_MonthNth.nWhichDayMask); if (!bRes) { ASSERT(FALSE); break; } } } break; case xtpCalendarRecurrenceYearly: { //------------------------------------------------------ // Every M:{month} D:{Day} // m_nMonthOfYear = M // m_nDayOfMonth = D //------------------------------------------------------ COleDateTime dtOcc(dtS1.GetYear(), m_Options.m_Yearly.nMonthOfYear, 1, 0, 0, 0); CXTPCalendarUtils::UpdateMonthDay(dtOcc, m_Options.m_Yearly.nDayOfMonth); if (dtOcc >= dtS1) { dtS1 = dtOcc; } else { dtS1 = dtOcc; CXTPCalendarUtils::ShiftDate_Year(dtS1, 1, m_Options.m_Yearly.nDayOfMonth); } for (dtI = dtS1; dtI <= dtE1 && nOccCount < nMaxOccCount; CXTPCalendarUtils::ShiftDate_Year(dtI, 1, m_Options.m_Yearly.nDayOfMonth)) { ASSERT(dtI >= dtStart0 && dtI <= dtE1); rArDates.Add(dtI); nOccCount++; } } break; case xtpCalendarRecurrenceYearNth: { //------------------------------------------------------ //~ The I:{First, second, ..., Last> //~ W:{Day, WeekDay, WeekEndDay, Su, Mo, ...} //~ of M:{month} //------------------------------------------------------ COleDateTime dtOcc(dtS1.GetYear(), m_Options.m_YearNth.nMonthOfYear, 1, 0, 0, 0); BOOL bRes = CXTPCalendarUtils::CalcDayOfMonth(dtOcc, m_Options.m_YearNth.nWhichDay, m_Options.m_YearNth.nWhichDayMask); if (!bRes) { return; } if (dtOcc >= dtS1) { dtS1 = dtOcc; } else { dtS1 = dtOcc; CXTPCalendarUtils::ShiftDate_Year(dtS1, 1); bRes = CXTPCalendarUtils::CalcDayOfMonth(dtS1, m_Options.m_YearNth.nWhichDay, m_Options.m_YearNth.nWhichDayMask); if (!bRes) { return; } } for (dtI = dtS1; dtI <= dtE1 && nOccCount < nMaxOccCount;) { ASSERT(dtI >= dtStart0 && dtI <= dtE1); rArDates.Add(dtI); nOccCount++; CXTPCalendarUtils::ShiftDate_Year(dtI, 1); bRes = CXTPCalendarUtils::CalcDayOfMonth(dtI, m_Options.m_YearNth.nWhichDay, m_Options.m_YearNth.nWhichDayMask); if (!bRes) { ASSERT(FALSE); break; } } } } } void CXTPCalendarRecurrencePattern::SetRecurrenceOptions(const XTP_CALENDAR_RECURRENCE_OPTIONS& Options) { m_Options = Options; if (m_Options.m_nRecurrenceType == xtpCalendarRecurrenceDaily && m_Options.m_Daily.bEveryWeekDayOnly) { m_Options.m_Daily.nIntervalDays = 1; } //Prevent wrong user entry (e.g. infinite loop for nIntervalDays = 0 case) if (m_Options.m_nRecurrenceType == xtpCalendarRecurrenceDaily) { m_Options.m_Daily.nIntervalDays = min(max(m_Options.m_Daily.nIntervalDays,1), 999); } if (m_Options.m_nRecurrenceType == xtpCalendarRecurrenceWeekly) { m_Options.m_Weekly.nIntervalWeeks = min(max(m_Options.m_Weekly.nIntervalWeeks,1), 999); } if (m_Options.m_nRecurrenceType == xtpCalendarRecurrenceMonthly) { m_Options.m_Monthly.nIntervalMonths = min(max(m_Options.m_Monthly.nIntervalMonths,1), 999); m_Options.m_Monthly.nDayOfMonth = min(max(m_Options.m_Monthly.nDayOfMonth,1), 31); } if (m_Options.m_nRecurrenceType == xtpCalendarRecurrenceYearly) { m_Options.m_Yearly.nDayOfMonth = min(max(m_Options.m_Yearly.nDayOfMonth,1), 31); } } void CXTPCalendarRecurrencePattern::SetPatternEnd(const XTP_CALENDAR_PATTERN_END& patternEnd) { m_PatternEnd = patternEnd; if (m_PatternEnd.m_nUseEnd == xtpCalendarPatternEndDate) { m_PatternEnd.m_dtPatternEndDate = CXTPCalendarUtils::ResetTime(m_PatternEnd.m_dtPatternEndDate); if (m_dtPatternStartDate.GetStatus() == COleDateTime::valid && m_PatternEnd.m_dtPatternEndDate.GetStatus() == COleDateTime::valid && m_PatternEnd.m_dtPatternEndDate < m_dtPatternStartDate) { m_dtPatternStartDate = m_PatternEnd.m_dtPatternEndDate; } } } CXTPCalendarRecurrencePatternPtr CXTPCalendarRecurrencePattern::ClonePattern() { if (!m_ptrDataProvider) { ASSERT(FALSE); return NULL; } DWORD dwPatternID = GetPatternID(); DWORD dwMasterID = GetMasterEventID(); CXTPCalendarRecurrencePatternPtr ptrNew = m_ptrDataProvider->CreateNewRecurrencePattern(); if (!ptrNew) { ASSERT(FALSE); return NULL; } ptrNew->SetPatternID(dwPatternID); ptrNew->SetMasterEventID(dwMasterID); if (!ptrNew->Update(this)) { ASSERT(FALSE); return NULL; } return ptrNew; } BOOL CXTPCalendarRecurrencePattern::Update(CXTPCalendarRecurrencePattern* pNewData) { if (pNewData == this) { return TRUE; } COleDateTime dtTmp = pNewData->GetStartTime(); SetStartTime(dtTmp); int nTmp = pNewData->GetDurationMinutes(); SetDurationMinutes(nTmp); const XTP_CALENDAR_RECURRENCE_OPTIONS& rOpt = pNewData->GetRecurrenceOptions(); SetRecurrenceOptions(rOpt); dtTmp = pNewData->GetPatternStartDate(); SetPatternStartDate(dtTmp); const XTP_CALENDAR_PATTERN_END& rPatternEnd = pNewData->GetPatternEnd(); SetPatternEnd(rPatternEnd); RemoveAllExceptions(); CXTPCalendarEventsPtr ptrExceptions = pNewData->GetExceptions(); int nCount = XTP_SAFE_GET1(ptrExceptions, GetCount(), 0); for (int i = 0; i < nCount; i++) { CXTPCalendarEvent* pException = ptrExceptions->GetAt(i, FALSE); if (pException) { if (!SetException(pException)) { ASSERT(FALSE); } } } ASSERT(pNewData->GetCustomProperties()); pNewData->GetCustomProperties()->CopyTo(m_pCustomProperties); m_arOccReminders.RemoveAll(); m_arOccReminders.Append(pNewData->m_arOccReminders); return TRUE; } BOOL CXTPCalendarRecurrencePattern::SetException(CXTPCalendarEvent* pException, CXTPCalendarEvent* pMasterEventToUpdate) { if (!pException) { ASSERT(FALSE); return FALSE; } DWORD dwExcID = pException->GetEventID(); CXTPCalendarEvent* pExc = m_parExceptionEvents->FindEvent(dwExcID); if (!pExc) { COleDateTime dtStartOcc = pException->GetRException_StartTimeOrig(); COleDateTime dtEndOcc = pException->GetRException_EndTimeOrig(); pExc = FindException(dtStartOcc, dtEndOcc); } BOOL bRes = TRUE; if (pExc) { pExc->SetEventID(pException->GetEventID()); bRes = pExc->Update(pException); ASSERT(bRes); } else { CXTPCalendarEventPtr ptrExc = pException->CloneEvent(); m_parExceptionEvents->Add(ptrExc); pExc = ptrExc; } pExc->SetRecurrencePatternID(m_dwPatternID); if (pMasterEventToUpdate) { pMasterEventToUpdate->UpdateStartEndDatesFromPatternExceptions(); } return bRes; } BOOL CXTPCalendarRecurrencePattern::RemoveException(CXTPCalendarEvent* pException, CXTPCalendarEvent* pMasterEventToUpdate) { if (!pException) { ASSERT(FALSE); return FALSE; } int nRState = pException->GetRecurrenceState(); ASSERT(nRState == xtpCalendarRecurrenceOccurrence || nRState == xtpCalendarRecurrenceException); COleDateTime dtStartOcc, dtEndOcc; if (nRState == xtpCalendarRecurrenceOccurrence) { dtStartOcc = pException->GetStartTime(); dtEndOcc = pException->GetEndTime(); } else { if (nRState == xtpCalendarRecurrenceException) { dtStartOcc = pException->GetRException_StartTimeOrig(); dtEndOcc = pException->GetRException_EndTimeOrig(); } else { return FALSE; } } int nCount = m_parExceptionEvents->GetCount(); int nFExcIdx = FindExceptionIndex(dtStartOcc, dtEndOcc); if (nFExcIdx >= 0 && nFExcIdx < nCount) { m_parExceptionEvents->RemoveAt(nFExcIdx); if (pMasterEventToUpdate) { pMasterEventToUpdate->UpdateStartEndDatesFromPatternExceptions(); } return TRUE; } return FALSE; } void CXTPCalendarRecurrencePattern::RemoveAllExceptions() { m_parExceptionEvents->RemoveAll(); m_arOccReminders.RemoveAll(); } CXTPCalendarEvent* CXTPCalendarRecurrencePattern::FindException(COleDateTime dtStartOrig, COleDateTime dtEndOrig) const { int nCount = m_parExceptionEvents->GetCount(); int nFExcIdx = FindExceptionIndex(dtStartOrig, dtEndOrig); if (nFExcIdx >= 0 && nFExcIdx < nCount) { CXTPCalendarEvent* pEvent = m_parExceptionEvents->GetAt(nFExcIdx, FALSE); return pEvent; } return NULL; } int CXTPCalendarRecurrencePattern::FindExceptionIndex(COleDateTime dtStartOrig, COleDateTime dtEndOrig) const { COleDateTime dtTmpS, dtTmpE; int nCount = m_parExceptionEvents->GetCount(); for (int i = 0; i < nCount; i++) { CXTPCalendarEvent* pEvent = m_parExceptionEvents->GetAt(i, FALSE); ASSERT(pEvent->GetRecurrenceState() == xtpCalendarRecurrenceException); dtTmpS = pEvent->GetRException_StartTimeOrig(); dtTmpE = pEvent->GetRException_EndTimeOrig(); if (CXTPCalendarUtils::IsEqual(dtTmpS, dtStartOrig) && CXTPCalendarUtils::IsEqual(dtTmpE, dtEndOrig)) { return i; } } return -1; } BOOL CXTPCalendarRecurrencePattern::FindExceptions(CXTPCalendarEvents* pEvents, COleDateTime dtStart, COleDateTime dtEnd) const { dtStart = CXTPCalendarUtils::ResetTime(dtStart); dtEnd = CXTPCalendarUtils::ResetTime(dtEnd); COleDateTime dtTmpS, dtTmpE; int nCount = m_parExceptionEvents->GetCount(); for (int i = 0; i < nCount; i++) { CXTPCalendarEvent* pEvent = m_parExceptionEvents->GetAt(i, FALSE); ASSERT(pEvent->GetRecurrenceState() == xtpCalendarRecurrenceException); dtTmpS = CXTPCalendarUtils::ResetTime(pEvent->GetStartTime()); dtTmpE = CXTPCalendarUtils::ResetTime(pEvent->GetEndTime()); if (dtStart <= dtTmpS && dtTmpS <= dtEnd || dtStart <= dtTmpE && dtTmpE <= dtEnd || dtTmpS <= dtStart && dtStart <= dtTmpE || dtTmpS <= dtEnd && dtEnd <= dtTmpE) { if (!pEvent->IsRExceptionDeleted()) { pEvents->Add(pEvent); } } } return TRUE; } CXTPCalendarEventsPtr CXTPCalendarRecurrencePattern::GetExceptions() { CXTPCalendarEventsPtr ptrExceptions = new CXTPCalendarEvents(); if (!ptrExceptions) { return NULL; } int nCount = m_parExceptionEvents->GetCount(); for (int i = 0; i < nCount; i++) { CXTPCalendarEvent* pEvent = m_parExceptionEvents->GetAt(i, FALSE); ASSERT(pEvent); CXTPCalendarEventPtr ptrEventCopy = pEvent ? pEvent->CloneEvent() : NULL; if (ptrEventCopy) { ptrExceptions->Add(ptrEventCopy); } } return ptrExceptions; } CXTPCalendarEventPtr CXTPCalendarRecurrencePattern::GetMasterEvent() const { if (!m_ptrDataProvider) { ASSERT(FALSE); return NULL; } CXTPCalendarEventPtr ptrMEvent = m_ptrDataProvider->GetEvent(m_dwMasterEventID); ASSERT(ptrMEvent); return ptrMEvent; } void CXTPCalendarRecurrencePattern::SetPatternID(DWORD dwID) { m_dwPatternID = dwID; int nCount = m_parExceptionEvents->GetCount(); for (int i = 0; i < nCount; i++) { CXTPCalendarEvent* pEvent = m_parExceptionEvents->GetAt(i, FALSE); ASSERT(pEvent); if (pEvent) { pEvent->SetRecurrencePatternID(m_dwPatternID); } } } void CXTPCalendarRecurrencePattern::DoPropExchange(CXTPPropExchange* pPX) { long nVersion = XTP_RECPATT_PROP_SET_DATA_VER; PX_Long(pPX, _T("Version"), nVersion, XTP_RECPATT_PROP_SET_DATA_VER); PX_DWord(pPX, cszRPatt_PatternID, m_dwPatternID, XTP_CALENDAR_UNKNOWN_RECURRENCE_PATTERN_ID); PX_DateTime(pPX, cszRPatt_StartTime, m_dtStartTime); PX_Int(pPX, cszRPatt_DurationMinutes, m_nDurationMinutes, 0); PX_Enum(pPX, cszRPatt_RecurrenceType, m_Options.m_nRecurrenceType, xtpCalendarRecurrenceUnknown); PX_Int(pPX, cszRPatt_RecurrenceOpt_Data1, m_Options.m_YearNth.nWhichDay, 0); PX_Int(pPX, cszRPatt_RecurrenceOpt_Data2, m_Options.m_YearNth.nWhichDayMask, 0); PX_Int(pPX, cszRPatt_RecurrenceOpt_Data3, m_Options.m_YearNth.nMonthOfYear, 0); PX_DateTime(pPX, cszRPatt_PatternStartDate, m_dtPatternStartDate); PX_Enum(pPX, cszRPatt_UseEndMethod, m_PatternEnd.m_nUseEnd, xtpCalendarPatternEndNoDate); if (m_PatternEnd.m_nUseEnd == xtpCalendarPatternEndDate) { PX_DateTime(pPX, cszRPatt_PatternEndDate, m_PatternEnd.m_dtPatternEndDate); } else if (m_PatternEnd.m_nUseEnd == xtpCalendarPatternEndAfterOccurrences) { PX_Int(pPX, cszRPatt_EndAfterOccurrences, m_PatternEnd.m_nEndAfterOccurrences, 1); } PX_DWord(pPX, cszRPatt_MasterEventID, m_dwMasterEventID, XTP_CALENDAR_UNKNOWN_EVENT_ID); //------------------------------------------------------------------------ if (pPX->IsStoring()) { VERIFY( m_arOccReminders.Save(m_pCustomProperties, this) ); } //------------------------------------------- m_pCustomProperties->DoPropExchange(pPX); //------------------------------------------- if (pPX->IsLoading()) { VERIFY( m_arOccReminders.Load(m_pCustomProperties) ); } m_arOccReminders.ClearProperties(m_pCustomProperties); } void CXTPCalendarRecurrencePattern::SetOccReminder( CXTPCalendarEvent* pOccEvent, int nbIsReminder, DATE dtNextReminderTime_Snoozed) { ASSERT(pOccEvent); if (!pOccEvent) { return; } int nIndex = m_arOccReminders.Find(pOccEvent); if (nIndex < 0) { CXTPCalendarReminderForOccurrence tmpData; tmpData.m_dtOccurrenceStartTime = pOccEvent->GetStartTime(); tmpData.m_dtOccurrenceEndTime = pOccEvent->GetEndTime(); nIndex = (int)m_arOccReminders.Add(tmpData); } CXTPCalendarReminderForOccurrence* pOccRmdData; pOccRmdData = &m_arOccReminders[nIndex]; if (nbIsReminder != xtpCalendarRmdPrm_DontChange) { pOccRmdData->m_nbIsReminder = nbIsReminder; } if (dtNextReminderTime_Snoozed != xtpCalendarRmdPrm_DontChange) { pOccRmdData->m_dtNextReminderTime_Snoozed = dtNextReminderTime_Snoozed; } m_arOccReminders.UpdateOccIfNeedEx(pOccEvent, pOccRmdData, this); if (pOccRmdData->m_nbIsReminder == xtpCalendarRmdPrm_Default && pOccRmdData->m_dtNextReminderTime_Snoozed == xtpCalendarRmdPrm_Default) { m_arOccReminders.RemoveAt(nIndex); } } //////////////////////////////////////////////////////////////////////////// CXTPCalendarReminderForOccurrence::CXTPCalendarReminderForOccurrence() { m_dtOccurrenceStartTime = 0; m_dtOccurrenceEndTime = 0; m_nbIsReminder = xtpCalendarRmdPrm_Default; m_dtNextReminderTime_Snoozed = (DATE)xtpCalendarRmdPrm_Default; } CXTPCalendarReminderForOccurrence::~CXTPCalendarReminderForOccurrence() { } const CXTPCalendarReminderForOccurrence& CXTPCalendarReminderForOccurrence::operator=( const CXTPCalendarReminderForOccurrence& rSrc) { m_dtOccurrenceStartTime = rSrc.m_dtOccurrenceStartTime; m_dtOccurrenceEndTime = rSrc.m_dtOccurrenceEndTime; m_nbIsReminder = rSrc.m_nbIsReminder; m_dtNextReminderTime_Snoozed = rSrc.m_dtNextReminderTime_Snoozed; return *this; } //=========================================================================== CXTPCalendarReminderForOccurrenceArray::CXTPCalendarReminderForOccurrenceArray() { } CXTPCalendarReminderForOccurrenceArray::~CXTPCalendarReminderForOccurrenceArray() { } int CXTPCalendarReminderForOccurrenceArray::Find(CXTPCalendarEvent* pOccEvent) const { ASSERT(pOccEvent); if (!pOccEvent) { return -1; } ASSERT(pOccEvent->GetRecurrenceState() == xtpCalendarRecurrenceOccurrence); DATE dtOcctartTime = pOccEvent->GetStartTime(); DATE dtOccEndTime = pOccEvent->GetEndTime(); int nFIndex = Find(dtOcctartTime, dtOccEndTime); return nFIndex; } int CXTPCalendarReminderForOccurrenceArray::Find(DATE dtOccStartTime, DATE dtOccEndTime) const { int nCount = (int)GetSize(); for (int i = 0; i < nCount; i++) { const CXTPCalendarReminderForOccurrence& rData = m_pData[i]; if (CXTPCalendarUtils::IsEqual(dtOccStartTime, rData.m_dtOccurrenceStartTime) && CXTPCalendarUtils::IsEqual(dtOccEndTime, rData.m_dtOccurrenceEndTime)) { return i; } } return -1; } int CXTPCalendarReminderForOccurrenceArray::UpdateOccIfNeed( CXTPCalendarEvent* pOccEvent, CXTPCalendarRecurrencePattern* pPattern) { if (!pOccEvent || !pPattern) { ASSERT(FALSE); return 0; } int nFIndex = Find(pOccEvent); if (nFIndex < 0) { return 0; } CXTPCalendarReminderForOccurrence* pOccRmdData; pOccRmdData = &ElementAt(nFIndex); int nRes = UpdateOccIfNeedEx(pOccEvent, pOccRmdData, pPattern); return nRes; } int CXTPCalendarReminderForOccurrenceArray::UpdateOccIfNeedEx( CXTPCalendarEvent* pOccEvent, const CXTPCalendarReminderForOccurrence* pOccRmdData, CXTPCalendarRecurrencePattern* pPattern) { if (!pOccEvent || !pOccRmdData || !pPattern) { ASSERT(FALSE); return 0; } // IsReminder if (pOccRmdData->m_nbIsReminder == xtpCalendarRmdPrm_Default) { CXTPCalendarEventPtr ptrMasterEvent = pPattern->GetMasterEvent(); if (!ptrMasterEvent) { ASSERT(FALSE); return 0; } pOccEvent->SetReminder(ptrMasterEvent->IsReminder()); } else if (pOccRmdData->m_nbIsReminder != xtpCalendarRmdPrm_DontChange) { pOccEvent->SetReminder(pOccRmdData->m_nbIsReminder != 0); } // MinutesBeforeStart_Snoozed if (pOccRmdData->m_dtNextReminderTime_Snoozed == xtpCalendarRmdPrm_Default) { pOccEvent->GetCustomProperties()->RemoveProperty(cszEventCustProp_NextReminderTime_Snoozed); } else if (pOccRmdData->m_dtNextReminderTime_Snoozed != xtpCalendarRmdPrm_DontChange) { VERIFY( pOccEvent->GetCustomProperties()->SetProperty( cszEventCustProp_NextReminderTime_Snoozed, pOccRmdData->m_dtNextReminderTime_Snoozed) ); } return 1; } BOOL CXTPCalendarReminderForOccurrenceArray::Load(CXTPCalendarCustomProperties* pProps) { ASSERT(pProps); if (!pProps) { return FALSE; } RemoveAll(); COleVariant varCount; BOOL bExists = pProps->GetProperty(cszOccRMD_Count, varCount); if (!bExists) { return TRUE; } COleVariant varValue; CString strPropNameI; int nCount = (int)(long)_variant_t(varCount); for (int i = 0; i < nCount; i++) { CXTPCalendarReminderForOccurrence rmdData; _PropName(cszOccRMD_StartTime, i, strPropNameI); if (pProps->GetProperty(strPropNameI, varValue)) { rmdData.m_dtOccurrenceStartTime = (DATE)_variant_t(varValue); } _PropName(cszOccRMD_EndTime, i, strPropNameI); if (pProps->GetProperty(strPropNameI, varValue)) { rmdData.m_dtOccurrenceEndTime = (DATE)_variant_t(varValue); } _PropName(cszOccRMD_IsReminder, i, strPropNameI); if (pProps->GetProperty(strPropNameI, varValue)) { rmdData.m_nbIsReminder = (long)_variant_t(varValue); } _PropName(cszOccRMD_NextReminderTime_Snoozed, i, strPropNameI); if (pProps->GetProperty(strPropNameI, varValue)) { rmdData.m_dtNextReminderTime_Snoozed = (DATE)_variant_t(varValue); } Add(rmdData); } return TRUE; } BOOL CXTPCalendarReminderForOccurrenceArray::Save(CXTPCalendarCustomProperties* pProps, CXTPCalendarRecurrencePattern* pPattern) { ASSERT(pProps); if (!pProps) { return FALSE; } ClearProperties(pProps); CString strPropNameI; int nCount = (int)GetSize(); int i; //=========================================================================== if (pPattern) { for (i = nCount-1; i >= 0; i--) { const CXTPCalendarReminderForOccurrence& rData = ElementAt(i); int nFExcIdx = pPattern->FindExceptionIndex(rData.m_dtOccurrenceStartTime, rData.m_dtOccurrenceEndTime); if (nFExcIdx >= 0) { RemoveAt(i); } } } //=========================================================================== nCount = (int)GetSize(); for (i = 0; i < nCount; i++) { const CXTPCalendarReminderForOccurrence& rData = ElementAt(i); _PropName(cszOccRMD_StartTime, i, strPropNameI); VERIFY( pProps->SetProperty(strPropNameI, COleDateTime(rData.m_dtOccurrenceStartTime)) ); _PropName(cszOccRMD_EndTime, i, strPropNameI); VERIFY( pProps->SetProperty(strPropNameI, COleDateTime(rData.m_dtOccurrenceEndTime)) ); _PropName(cszOccRMD_IsReminder, i, strPropNameI); VERIFY( pProps->SetProperty(strPropNameI, (long)rData.m_nbIsReminder) ); if (rData.m_dtNextReminderTime_Snoozed != xtpCalendarRmdPrm_Default) { ASSERT(rData.m_dtNextReminderTime_Snoozed >= xtpCalendarDateTime_min && rData.m_dtNextReminderTime_Snoozed <= xtpCalendarDateTime_max); _PropName(cszOccRMD_NextReminderTime_Snoozed, i, strPropNameI); VERIFY( pProps->SetProperty(strPropNameI, COleDateTime(rData.m_dtNextReminderTime_Snoozed)) ); } } BOOL bRes = pProps->SetProperty(cszOccRMD_Count, (long)nCount); ASSERT(bRes); return bRes; } void CXTPCalendarReminderForOccurrenceArray::ClearProperties(CXTPCalendarCustomProperties* pProps) { ASSERT(pProps); if (!pProps) { return; } COleVariant varCount; BOOL bExists = pProps->GetProperty(cszOccRMD_Count, varCount); if (!bExists) { return; } CString strPropNameI; int nCount = (int)(long)_variant_t(varCount); for (int i = 0; i < nCount; i++) { _PropName(cszOccRMD_StartTime, i, strPropNameI); pProps->RemoveProperty(strPropNameI); _PropName(cszOccRMD_EndTime, i, strPropNameI); pProps->RemoveProperty(strPropNameI); _PropName(cszOccRMD_IsReminder, i, strPropNameI); pProps->RemoveProperty(strPropNameI); _PropName(cszOccRMD_NextReminderTime_Snoozed, i, strPropNameI); pProps->RemoveProperty(strPropNameI); } pProps->RemoveProperty(cszOccRMD_Count); } void CXTPCalendarReminderForOccurrenceArray::_PropName(LPCTSTR pcszProp, int nIndex, CString& rstrPropNameI) { TCHAR szIndex[64]; ITOT_S(nIndex, szIndex, 64, 10); rstrPropNameI = pcszProp; rstrPropNameI += _T("["); rstrPropNameI += szIndex; rstrPropNameI += _T("]"); } COleDateTimeSpan CXTPCalendarRecurrencePattern::GetDuration() const { return CXTPCalendarUtils::Minutes2Span(m_nDurationMinutes); }