diff -r 12af337248b1 -r bd7edf625bdd calendarui/editors/src/CalenEditorDataHandler.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/calendarui/editors/src/CalenEditorDataHandler.cpp Wed Sep 01 12:32:31 2010 +0100 @@ -0,0 +1,3076 @@ +/* +* Copyright (c) 2006 Nokia Corporation and/or its subsidiary(-ies). +* All rights reserved. +* This component and the accompanying materials are made available +* under the terms of "Eclipse Public License v1.0" +* which accompanies this distribution, and is available +* at the URL "http://www.eclipse.org/legal/epl-v10.html". +* +* Initial Contributors: +* Nokia Corporation - initial contribution. +* +* Contributors: +* +* Description: CalenEditorDataHandler implements all logic of checking and +* validating editor data +* +*/ + +// System includes +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include // TCalenInstanceId +#include + +// User includes +#include "CalenEditorDataHandler.h" +#include "CalendarPrivateCRKeys.h" +#include "CalenDefaultEditors.hrh" +#include "calenentryutil.h" +#include "CleanupResetAndDestroy.h" +#include "calenattachmentutils.h" +#include "calenattachmentinfo.h" +#include "calenattachmentmodel.h" +#include "calenunifiededitor.h" +#include "calendarui_debug.h" + +// Constants +#define KNoOfDaysInWeek 7 + +// ======== LOCAL FUNCTIONS ======== + +// ----------------------------------------------------------------------------- +// IsTimed +// Returns ETrue if the entry is timed (i.e. an appointment or a +// reminder), EFalse otherwise. +// (other items were commented in a header). +// ----------------------------------------------------------------------------- +// +static TBool IsTimed( CCalEntry::TType aEntryType ) + { + TRACE_ENTRY_POINT; + + TRACE_EXIT_POINT; + return aEntryType == CCalEntry::EAppt || aEntryType == CCalEntry::EReminder; + } + +// ----------------------------------------------------------------------------- +// ForeverDate +// Returns the date which Calendar presumes to be infinitely far in the future. +// Used, for example, when setting a repeating meeting to repeat forever. +// (other items were commented in a header). +// ----------------------------------------------------------------------------- +// +static TTime ForeverDate() + { + TRACE_ENTRY_POINT; + + TTime forever = TTime( TDateTime( ECalenForeverYear, static_cast( ECalenForeverMonth ), + ECalenForeverDay, 0, 0, 0, 0) ); + + TRACE_EXIT_POINT; + return forever; + } + +// ----------------------------------------------------------------------------- +// IsForeverDate +// Returns ETrue if the given date is considered by Calendar to be infinitely +// far in the future. +// (other items were commented in a header). +// ----------------------------------------------------------------------------- +// +static TBool IsForeverDate( const TTime& aDate ) + { + TRACE_ENTRY_POINT; + + TRACE_EXIT_POINT; + return CalenDateUtils::OnSameDay( aDate, ForeverDate() ); + } + +// ======== MEMBER FUNCTIONS ======== + +// --------------------------------------------------------------------------- +// CCalenEditorDataHandler::CCalenEditorDataHandler +// Constructor. +// (other items were commented in a header). +// --------------------------------------------------------------------------- +// +CCalenEditorDataHandler::CCalenEditorDataHandler( + CCalEntry& aEntry, + const TCalTime& aInstanceDateTime, + TBool aIsCreatingNew, + TInt aMaxTextEditorLength, + MCalenServices& aServices, + RArray& aCollectionIds) + : iEntry( aEntry ), + iInstanceDateTime( aInstanceDateTime ), + iIsCreatingNew( aIsCreatingNew ), + iMaxTextEditorLength( aMaxTextEditorLength ), + iServices( aServices ) , iCalendarFieldEdited(EFalse) , iCollectionIds(aCollectionIds) + { + TRACE_ENTRY_POINT; + TRACE_EXIT_POINT; + } + +// --------------------------------------------------------------------------- +// CCalenEditorDataHandler::ConstructL +// Two phase construction. +// (other items were commented in a header). +// --------------------------------------------------------------------------- +// +void CCalenEditorDataHandler::ConstructL() + { + TRACE_ENTRY_POINT; + + iEntryType = iEntry.EntryTypeL(); + iRecurrenceId = iEntry.RecurrenceIdL(); + + iOriginal = CCalenEntryUtil::NewL( iEntry, iInstanceDateTime ); + iEdited = CCalenEntryUtil::NewL( iEntry, iInstanceDateTime ); + if ( IsCreatingNew() ) + { + SetDefaultNewValuesL( *iOriginal ); // modify iOriginal also + SetDefaultNewValuesL( *iEdited ); + } + else + { + iInstanceId = iServices.Context().InstanceId(); + } + + + TRACE_EXIT_POINT; + } + +// --------------------------------------------------------------------------- +// CCalenEditorDataHandler::NewL +// Two-phased constructor. +// (other items were commented in a header). +// --------------------------------------------------------------------------- +// +CCalenEditorDataHandler* CCalenEditorDataHandler::NewL( + CCalEntry& aEntry, + const TCalTime& aInstanceDateTime, + TBool aIsCreatingNew, + TInt aMaxTextEditorLength, + MCalenServices& aServices, + RArray& aCollectionIds) + { + TRACE_ENTRY_POINT; + + CCalenEditorDataHandler* self = new( ELeave ) CCalenEditorDataHandler( + aEntry, aInstanceDateTime, aIsCreatingNew, aMaxTextEditorLength, aServices,aCollectionIds ); + CleanupStack::PushL( self ); + self->ConstructL(); + CleanupStack::Pop( self ); + + TRACE_EXIT_POINT; + return self; + } + +// --------------------------------------------------------------------------- +// CCalenEditorDataHandler::~CCalenEditorDataHandler +// Destructor. +// (other items were commented in a header). +// --------------------------------------------------------------------------- +// +CCalenEditorDataHandler::~CCalenEditorDataHandler() + { + TRACE_ENTRY_POINT; + + delete iOriginal; + delete iEdited; + + TRACE_EXIT_POINT; + } + +// --------------------------------------------------------------------------- +// CCalenEditorDataHandler::Edited +// Returns the CCalenEditorDataHandler used to represent the entry the +// user is currently editing. +// (other items were commented in a header). +// --------------------------------------------------------------------------- +// +CCalenEntryUtil& CCalenEditorDataHandler::Edited() + { + TRACE_ENTRY_POINT; + + TRACE_EXIT_POINT; + return *iEdited; + } + +// --------------------------------------------------------------------------- +// CCalenEditorDataHandler::Original +// Returns the CCalenEditorDataHandler used to represent the entry the +// user originally opened. +// (other items were commented in a header). +// --------------------------------------------------------------------------- +// +const CCalenEntryUtil& CCalenEditorDataHandler::Original() const + { + TRACE_ENTRY_POINT; + + TRACE_EXIT_POINT; + return *iOriginal; + } + +// --------------------------------------------------------------------------- +// CCalenEditorDataHandler::Entry +// Returns the entry this object is wrapping round. +// (other items were commented in a header). +// --------------------------------------------------------------------------- +// +CCalEntry& CCalenEditorDataHandler::Entry() + { + TRACE_ENTRY_POINT; + + TRACE_EXIT_POINT; + return iEntry; + } + +// --------------------------------------------------------------------------- +// CCalenEditorDataHandler::InstanceDateTime +// Returns the instance date/time. +// (other items were commented in a header). +// --------------------------------------------------------------------------- +// +const TCalTime& CCalenEditorDataHandler::InstanceDateTime() const + { + TRACE_ENTRY_POINT; + + TRACE_EXIT_POINT; + return iInstanceDateTime; + } + + +// --------------------------------------------------------------------------- +// CCalenEditorDataHandler::ShouldSaveOrDeleteOrDoNothingL +// Works out whether the entry should be deleted, saved, +// or whether no action should be taken. +// (other items were commented in a header). +// --------------------------------------------------------------------------- +// +CCalenEditorDataHandler::TAction CCalenEditorDataHandler::ShouldSaveOrDeleteOrDoNothingL() + { + TRACE_ENTRY_POINT; + + if ( !IsEditedL() ) + { + // Not edited at all OR + // Only added space characters to text fields but not + // edited the non-text fields + // no need to save the note + TRACE_EXIT_POINT; + return CCalenEditorDataHandler::EActionNothing; + } + + // *************** THE NOTE IS EDITED ******************** + if(IsCreatingNew()) + { + // ************* EDITED NEW NOTE ********* + + // Subject && Location && Description are text fields + // If text fields as a whole is not empty, we can save the note + // If text fields as a whole IS empty, we can STILL save the note + // since we edited "non-text" fields + if ( !NonTextFieldsEditedL() && AreTextFieldsEmptyL() ) + { + TRACE_EXIT_POINT; + return CCalenEditorDataHandler::EActionNothing; + } + else + { + TRACE_EXIT_POINT; + return CCalenEditorDataHandler::EActionSave; + } + } + + // **************** EDITED OLD NOTE ********** + + if(AreTextFieldsClearedL() && !NonTextFieldsEditedL()) // subject && location && description + { + // ***** OLD NOTE + TEXT FIELDS EMPTIED + NON-TEXT FIELDS NOT EDITED + // Even if user may have edited non-text fields, + // delete the note + TRACE_EXIT_POINT; + return CCalenEditorDataHandler::EActionDelete; + } + + // ****** OLD NOTE WITH TEXT FIELDS ********** + // Save the note, since the text fields contain something + TRACE_EXIT_POINT; + return CCalenEditorDataHandler::EActionSave; + } + +// --------------------------------------------------------------------------- +// CCalenEditorDataHandler::NonTextFieldsEdited +// Returns ETrue if any of the non-text fields (e.g. time fields) of the entry +// have been edited, EFalse otherwise. +// (other items were commented in a header). +// --------------------------------------------------------------------------- +// +TBool CCalenEditorDataHandler::NonTextFieldsEditedL() + { + TRACE_ENTRY_POINT; + + TRACE_EXIT_POINT; + + return ( IsStartDateTimeEdited() || + IsEndDateTimeEdited() || + IsAlarmEditedL() || + IsRepeatRuleEdited() || + IsPriorityEdited() || + IsCalendarEditedL() || + IsAttachmentExists() || + IsEventTypeEdited() || + AllDayFieldEdited() ); + } + +// --------------------------------------------------------------------------- +// CCalenEditorDataHandler::TextFieldsEdited +// Returns ETrue if any of the text fields (e.g. summary) of the entry +// have been edited, EFalse otherwise. +// (other items were commented in a header). +// --------------------------------------------------------------------------- +// +TBool CCalenEditorDataHandler::TextFieldsEdited() + { + TRACE_ENTRY_POINT; + + TRACE_EXIT_POINT; + + return ( IsSummaryEdited() || IsLocationEdited() || IsDescriptionEdited() + || IsAttachmentExists() ); + } + +// --------------------------------------------------------------------------- +// CCalenEditorDataHandler::IsEditedL +// Returns ETrue if the entry has been modified, EFalse otherwise. +// (other items were commented in a header). +// --------------------------------------------------------------------------- +// +TBool CCalenEditorDataHandler::IsEditedL() + { + TRACE_ENTRY_POINT; + + TRACE_EXIT_POINT; + + return ( TextFieldsEdited() || NonTextFieldsEditedL() ); + } + +// --------------------------------------------------------------------------- +// CCalenEditorDataHandler::WriteChangesToEntryL +// Read the data from the form and set it to the entry. +// (other items were commented in a header). +// --------------------------------------------------------------------------- +// +void CCalenEditorDataHandler::WriteChangesToEntryL( CalCommon::TRecurrenceRange aRepeatTarget ) + { + TRACE_ENTRY_POINT; + + // This is only place where we can read data from CCalEntry. + // E.g. when we calculate alarm offset, we need to read start time from + // entry, not anymore from CCalenEntryUtil. + TBool isNew = IsCreatingNew(); + + WriteStartAndEndTimesToEntryL( aRepeatTarget ); + + if ( isNew || IsSummaryEdited() ) + { + TPtrC summary = Edited().Summary(); + // If text is considered empty, always store as KNullDesC + if ( CalenAgendaUtils::IsEmptyText( summary ) ) + { + summary.Set( KNullDesC ); + } + iEntry.SetSummaryL( summary ); + } + + if ( isNew || IsLocationEdited() ) + { + TPtrC location = Edited().Location(); + // If text is considered empty, always store as KNullDesC + if ( CalenAgendaUtils::IsEmptyText( location ) ) + { + location.Set(KNullDesC); + } + iEntry.SetLocationL( location ); + } + + if ( isNew || IsDescriptionEdited() ) + { + iEntry.SetDescriptionL( Edited().Description() ); + } + + // Repeat rule has to be modified in both cases + if ( isNew || IsStartDateTimeEdited() || IsRepeatRuleEdited() ) + { + + if ( aRepeatTarget == CalCommon::EThisOnly) + { + iEntry.ClearRepeatingPropertiesL(); + } + else if ( Edited().IsRepeating() ) + { + // If we're an RDate (with repeat type ERepeatOther), don't try to set an RRule, + // but don't clear the repeat properties either. + if( Edited().RepeatType() != ERepeatOther ) + { + TCalRRule rrule; + + CalculateRepeatRuleL( Edited().StartDateTime(), + Edited().RepeatType(), + Edited().RepeatUntilDateTime(), + rrule ); + + iEntry.SetRRuleL( rrule ); + + // As there should not be any rdates, remove any that are + // still present + RArray emptyList; + CleanupClosePushL( emptyList ); + iEntry.SetRDatesL( emptyList ); + CleanupStack::PopAndDestroy(); // emptyList + } + else + { + // The repeat type is ERepeatOther, therefore as + // it is impossible to create an entry of type + // ERepeat other using the editor either the repeat until + // date or the start date must have been changed + + // The start date/time has changed, in order for the + // series to maintain its pattern, any rDates and if + // present rRule are moved by the same offset + // The repeat type is ERepeatOther, so check if we have any rdates + RArray rDateList; + CleanupClosePushL( rDateList ); + iEntry.GetRDatesL( rDateList ); + TInt count = rDateList.Count(); + + if ( count == 0 ) + { + // There are no rdates so the new until and start date can be applied + // directly. + TCalRRule rrule; + + CalculateRepeatRuleL( Edited().StartDateTime(), + Edited().RepeatType(), + Edited().RepeatUntilDateTime(), + rrule ); + + iEntry.SetRRuleL( rrule ); + } + else + { + // There are rDates which need to be checked. + if ( IsStartDateTimeEdited() ) + { + // Need to shift any rdates + TTime editedStart = iEdited->StartDateTime(); + TTime origStart = iOriginal->StartDateTime(); + TTimeIntervalMicroSeconds offSet = editedStart.MicroSecondsFrom( origStart ); + for ( TInt index = 0; index < count; index++ ) + { + TCalTime& rDateTime = rDateList[ index ]; + TTime shiftedTime = rDateTime.TimeUtcL(); + TDateTime before = shiftedTime.DateTime(); + shiftedTime += offSet; + TDateTime after = shiftedTime.DateTime(); + rDateTime.SetTimeUtcL( shiftedTime ); + } + } + + // Check and fix the rDates and rRules match the + // repeat until date and time. + TTime untilTime = Edited().RepeatUntilDateTime(); + + // Remove any rdates that are after the the repeat until date + TInt count = rDateList.Count(); + if ( count > 0 ) + { + TInt index = count - 1; + do + { + TTime lastRDate = CalenDateUtils::BeginningOfDay( rDateList[ index ].TimeLocalL() ); + TDateTime before = lastRDate.DateTime(); + if ( lastRDate > untilTime ) + { + rDateList.Remove( index-- ); + } + else + { + index = KErrNotFound; + } + } while ( index != KErrNotFound ); + } + + // Need to check if the end date of the + // rrule needs adjusting if it exists. + TCalRRule rRule; + if ( iEntry.GetRRuleL( rRule ) ) + { + count = rDateList.Count(); + if ( count > 0 ) + { + // There still exists some rdates, so only need to trim + // the rrule if it exists + TTime lastRDate = CalenDateUtils::BeginningOfDay( rDateList[ count - 1 ].TimeLocalL() ); + const TTime& origUntilDate = Original().RepeatUntilDateTime(); + TTime startDT = rRule.DtStart().TimeLocalL(); + + if ( lastRDate <= origUntilDate && startDT > lastRDate ) + { + if ( startDT < untilTime) + { + if( origUntilDate != untilTime) + { + ApplyUntilDateToRRuleL( rRule, untilTime); + iEntry.SetRRuleL( rRule ); + } + } + else + { + // The repeat start is after the until date + // so remove any repeat information. + iEntry.ClearRepeatingPropertiesL(); + + // If the entry date has been moved past the until + // date, need to swap the an rDate for the entry. + TTime startTime = iEntry.StartTimeL().TimeLocalL(); + + if ( startTime > untilTime ) + { + // Find the duration of the entry + TTime endTime = iEntry.EndTimeL().TimeLocalL(); + TTimeIntervalMinutes duration; + //startTime.MinutesFrom( endTime, duration );// for bug: CMCA-745CZ4 + endTime.MinutesFrom( startTime, duration ); + + // Choose the first rDate as the new start time + TCalTime newStartTime = rDateList[ 0 ]; + endTime = newStartTime.TimeLocalL() + duration; + + // FIXME. + // If there is only one rDate left, the agenda model + // will crash if it is deleted. + if ( count != 0 ) + { + rDateList.Remove( 0 ); + } + + TCalTime newEndTime; + if ( UseFloatingTimeL() ) + { + newEndTime.SetTimeLocalFloatingL( endTime ); + } + else + { + newEndTime.SetTimeLocalL( endTime ); + } + + iEntry.SetStartAndEndTimeL( newStartTime, newEndTime ); + } + } + } + } + } + + iEntry.SetRDatesL( rDateList ); + } + + CleanupStack::PopAndDestroy(); // rDateList + } + } + else + { + iEntry.ClearRepeatingPropertiesL(); + + // FIXME As the entry is supposedly not repeating + // any rDates should be removed. Unforunately this + // is not possible at the moment because removing the + // rdates will cause the agenda model to panic + // + // RArray emptyList; + // CleanupClosePushL( emptyList ); + // iEntry.SetRDatesL( emptyList ); + // CleanupStack::PopAndDestroy(); // emptyList + } + } + + if ( isNew || IsAlarmEditedL() && ( iEntry.StatusL() != CCalEntry::ETodoCompleted ) ) + { + if ( Edited().IsAlarmActivated() ) + { + CCalAlarm* alarm = iEntry.AlarmL(); + if ( ! alarm ) + { + alarm = CCalAlarm::NewL(); + } + CleanupStack::PushL( alarm ); + + TTime startTime; + // ToDos don't have start time! + // If we have undated todo with alarm that was taken care earlier + if (iEntry.EntryTypeL()==CCalEntry::ETodo) + { + startTime = iEntry.EndTimeL().TimeLocalL(); + } + + // if this is not todo we use starttime normal way + else + { + startTime = iEntry.StartTimeL().TimeLocalL(); + } + TCalRRule ignore; + TBool isRepeating = iEntry.GetRRuleL( ignore ); + + // If edit alarm of repeating entry, we have to nudge start + // time to instance date + if ( isRepeating && aRepeatTarget == CalCommon::EThisAndAll ) + { + // nudge to instance date; + TTime instanceDate = iInstanceDateTime.TimeLocalL(); + if ( IsStartDateTimeEdited() ) + { + instanceDate = Edited().EventDateTime(); + } + + TDateTime insDt = instanceDate.DateTime(); + TDateTime startDt = startTime.DateTime(); + // Don't use individual setters (SetMonth, SetDay ...), because + // they break if month doesn't contain old date + // or date to be set. + startDt.Set( insDt.Year(), insDt.Month(), insDt.Day(), + startDt.Hour(), startDt.Minute(), + startDt.Second(), startDt.MicroSecond() ); + startTime = TTime( startDt ); + } + + TTime alarmTime = Edited().AlarmDateTime(); + + TTimeIntervalMinutes offset; + startTime.MinutesFrom( alarmTime, offset ); + alarm->SetTimeOffset( offset ); + + iEntry.SetAlarmL( alarm ); + CleanupStack::PopAndDestroy( alarm ); + } + else + { + iEntry.SetAlarmL( NULL ); + } + } + + if ( isNew || IsPriorityEdited() ) + { + // map priority here + TUint priority = 0; + switch ( Edited().Priority() ) + { + case CCalenEntryUtil::ETodoPriorityHigh: + priority = 1; + break; + case CCalenEntryUtil::ETodoPriorityNormal: + priority = 2; + break; + case CCalenEntryUtil::ETodoPriorityLow: + priority = 3; + break; + default: + ASSERT( EFalse ); + break; + } + iEntry.SetPriorityL( priority ); + } + + if ( isNew ) + { + CCalEntry::TReplicationStatus status = CCalEntry::EOpen; + switch ( Edited().SynchType() ) + { + case CCalenEntryUtil::ESynchPrivate: + status = CCalEntry::EPrivate; + break; + case CCalenEntryUtil::ESynchPublic: + status = CCalEntry::EOpen; + break; + case CCalenEntryUtil::ESynchNoSync: + status = CCalEntry::ERestricted; + break; + default: + ASSERT( EFalse ); + break; + } + iEntry.SetReplicationStatusL( status ); + } + + /** + * S60 settings for new entries + */ + if ( IsCreatingNew() ) + { + if ( Edited().EntryType() == CCalEntry::EAnniv ) + { + TCalRRule rrule( TCalRRule::EYearly ); + rrule.SetDtStart( iEntry.StartTimeL() ); + rrule.SetInterval( 1 ); + rrule.SetCount( 0 ); + iEntry.SetRRuleL( rrule ); + } + } + + // check for attachments + if( isNew || IsAttachmentExists() ) + { + // TODO: Uncomment this when enabling attachment support + AddAttachementsToEntryL(); + } + TRACE_EXIT_POINT; + } + +// ----------------------------------------------------------------------------- +// CCalenEditorDataHandler::ApplyUntilDateToRRuleL +// Applies an new until date to a rrule +// (other items were commented in a header). +// ----------------------------------------------------------------------------- +// +void CCalenEditorDataHandler::ApplyUntilDateToRRuleL( TCalRRule& aRRule, const TTime& aRepeatUntil ) const + { + TRACE_ENTRY_POINT; + // note repeat is set to forever. + if ( ! CalenDateUtils::IsValidDay( aRepeatUntil ) || IsForeverDate( aRepeatUntil ) ) + { + // Set repeat to forever: + aRRule.SetCount( 0 ); + } + else + { + // Change repeatuntil so that hours, minutes, ... are same as in start date + TDateTime dtUntil = aRepeatUntil.DateTime(); + TDateTime dtStart= iEntry.StartTimeL().TimeLocalL().DateTime(); + + dtUntil.Set( dtUntil.Year(), dtUntil.Month(), dtUntil.Day(), + dtStart.Hour(), dtStart.Minute(), dtStart.Second(), dtStart.MicroSecond() ); + + TCalTime calRepeatUntil; + if ( UseFloatingTimeL() ) + { + calRepeatUntil.SetTimeLocalFloatingL( TTime( dtUntil ) ); + } + else + { + calRepeatUntil.SetTimeLocalL( TTime( dtUntil ) ); + } + aRRule.SetUntil( calRepeatUntil ); + } + TRACE_EXIT_POINT; + } +// ----------------------------------------------------------------------------- +// CCalenEditorDataHandler::DurationGreaterThanRepeatIntervalError +// Returns ETrue if the duration of instances of the meeting is greater than +// the repeat period of the series, EFalse otherwise. +// (other items were commented in a header). +// ----------------------------------------------------------------------------- +// +TBool CCalenEditorDataHandler::DurationGreaterThanRepeatIntervalError() + { + TRACE_ENTRY_POINT; + TBool isError = EFalse; + switch( Edited().RepeatType() ) + { + case ERepeatDaily: + case ERepeatWorkdays: + { + TTime startDate = Edited().StartDateTime(); + TTime endDate = Edited().EndDateTime(); + // In case AllDay event end at "12:00am" 24/06/2009. + // editor should display EndDate as 23/06/2009. + if(Edited().IsAllDayEvent()) + { + if( endDate > startDate ) + { + endDate -= TTimeIntervalDays( 1 ); + if( endDate < startDate ) + { + endDate = startDate; + } + } + } + TTimeIntervalDays durDays = endDate.DaysFrom( startDate); + isError = durDays >= TTimeIntervalDays(1); + break; + } + case ERepeatWeekly: + { + TTimeIntervalDays durDays = Edited().EndDateTime().DaysFrom( Edited().StartDateTime() ); + isError = durDays >= TTimeIntervalDays(7); + break; + } + case ERepeatBiWeekly: + { + TTimeIntervalDays durDays = Edited().EndDateTime().DaysFrom( Edited().StartDateTime() ); + isError = durDays >= TTimeIntervalDays(14); + break; + } + case ERepeatMonthly: + { + TTimeIntervalMonths durMonths = Edited().EndDateTime().MonthsFrom( Edited().StartDateTime() ); + isError = durMonths >= TTimeIntervalMonths(1); + break; + } + case ERepeatYearly: + { + TTimeIntervalYears durYears = Edited().EndDateTime().YearsFrom( Edited().StartDateTime() ); + isError = durYears >= TTimeIntervalYears(1); + break; + } + case ERepeatOther: + { + isError = EFalse; + break; + } + default: + // Not repeating, no error + isError = EFalse; + break; + } + TRACE_EXIT_POINT; + return isError; + } + +// ----------------------------------------------------------------------------- +// CCalenEditorDataHandler::CheckErrorsForThisAndAllL +// Check for errors in the form. This function should only be called when the +// user has decided they want to edit "This and all" from a prompt, or when the +// entry is not repeating. +// (other items were commented in a header). +// ----------------------------------------------------------------------------- +// +CCalenEditorDataHandler::TError CCalenEditorDataHandler::CheckErrorsForThisAndAllL() + { + TRACE_ENTRY_POINT; + + // From UI spec: + // 5 checks is specified, only one not needed in this phase is 2.1 + + const TTime start = Edited().StartDateTime(); + const TTime end = Edited().EndDateTime(); + + // 1) Check that entry doesn't end earlier than it starts + if ( end < start ) + { + TRACE_EXIT_POINT; + return CCalenEditorDataHandler::EFormErrStopTimeEarlierThanStartTime; + } + + // 2) Repeating entry checks: + if ( Edited().IsRepeating() ) + { + // 2.1) Check that repeat until date is a) later than start date (for new notes) + // b) not before start date (for existing notes) + TTime repeatUntilDay = CalenDateUtils::BeginningOfDay( + Edited().RepeatUntilDateTime() ); + + TTime repeatStartDay; + + // if new note or old note isnt repeating + // edited.repeatUntil date must be greater than edited.start date + // else + // if IsRepeatRuleEdited or IsStartDateTimeEdited + // //(either one above will make a new rule in which edited.startdate is the start date) + // edited.repeatUntil must be greater than edited.start date + // else + // edited.repeatUntil must be greater than start date on disk + + TCalRRule rrule; + if( IsCreatingNew() || + !Entry().GetRRuleL(rrule) || + IsRepeatRuleEdited() || + IsStartDateTimeEdited() ) + { + // We don't have an rrule so we can't get the rrule start date, + // or user has edited a field that will cause new start date to be used in the new rule. + // Use the edited entry's start date. + repeatStartDay = CalenDateUtils::BeginningOfDay( start ); + } + else + { + // original rrule is valid and new rule will not be created + repeatStartDay = CalenDateUtils::BeginningOfDay( rrule.DtStart().TimeLocalL() ); + } + + if ( repeatUntilDay <= repeatStartDay ) + { + TRACE_EXIT_POINT; + return CCalenEditorDataHandler::EFormErrRepeatUntilEarlierThanNote; + } + + if( DurationGreaterThanRepeatIntervalError() ) + { + TRACE_EXIT_POINT; + return CCalenEditorDataHandler::EFormErrDurationGreaterThanRepeatInterval; + } + // 3) Alarm field checks: + TRACE_EXIT_POINT; + return CheckAlarmFieldsForErrorsL(ETrue); // user editing the series + } + + // 3) Alarm field checks: + TRACE_EXIT_POINT; + return CheckAlarmFieldsForErrorsL(); // user editing the series + } + +// ----------------------------------------------------------------------------- +// CCalenEditorDataHandler::CheckErrorsForThisOnlyL +// Check for errors in the form. This function should only be called when the +// user has decided they want to edit "This only" from a prompt, or when the +// entry is an exception. +// (other items were commented in a header). +// ----------------------------------------------------------------------------- +// +CCalenEditorDataHandler::TError CCalenEditorDataHandler::CheckErrorsForThisOnlyL( CCalEntry& /*aParentEntry*/, + CCalInstanceView& /*aInstanceView*/ ) + { + TRACE_ENTRY_POINT; + + const TTime start = Edited().StartDateTime(); + const TTime end = Edited().EndDateTime(); + + // Check that entry doesn't end earlier than it starts + if ( end < start ) + { + TRACE_EXIT_POINT; + return CCalenEditorDataHandler::EFormErrStopTimeEarlierThanStartTime; + } + + TError error = CheckForOutOfSequenceL(); + + if( error != EFormErrNone ) + { + return error; + } + + // Alarm field checks: + TRACE_EXIT_POINT; + return CheckAlarmFieldsForErrorsL(); // user creating/editing an exception + } + +// ----------------------------------------------------------------------------- +// CCalenEditorDataHandler::CheckForOutOfSequenceL +// Checks to see if the instance is being moved out of sequence, relative to +// the rest of the series. +// (other items were commented in a header). +// ----------------------------------------------------------------------------- +// +CCalenEditorDataHandler::TError CCalenEditorDataHandler::CheckForOutOfSequenceL() + { + TRACE_ENTRY_POINT; + // Only perform the checks when we change the start or end time of the meeting. + if( !IsStartDateTimeEdited() && !IsEndDateTimeEdited() ) + { + TRACE_EXIT_POINT; + return EFormErrNone; + } + + TCalTime prevStart, prevEnd; + + GetPreviousInstanceTimeL( prevStart, prevEnd ); + + if( prevStart.TimeLocalL() != Time::NullTTime() ) + { + // Does the exception end on/after prevEnd? + if( Edited().StartDateTime() < prevEnd.TimeLocalL() ) + { + // Does the exception start after prevStart? + if( Edited().StartDateTime() > prevStart.TimeLocalL() ) + { + TRACE_EXIT_POINT; + return EFormErrOverlapsExistingInstance; + } + else + { + // Does the exception finish after prevStart? + if( Edited().EndDateTime() > prevStart.TimeLocalL() ) + { + TRACE_EXIT_POINT; + return EFormErrOverlapsExistingInstance; + } + else + { + TRACE_EXIT_POINT; + return EFormErrOutOfSequence; + } + } + } + + // Does the exception start on the same day as prevStart? + if( CalenDateUtils::OnSameDay( Edited().StartDateTime(), prevStart.TimeLocalL() ) ) + { + TRACE_EXIT_POINT; + return EFormErrInstanceAlreadyExistsOnThisDay; + } + } + + TCalTime nextStart, nextEnd; + + GetNextInstanceTimeL( nextStart, nextEnd ); + + if( nextStart.TimeLocalL() != Time::NullTTime() ) + { + // Does the exception finish on/before nextStart? + if( Edited().EndDateTime() > nextStart.TimeLocalL() ) + { + // Does the exception finish before nextFinish? + if( Edited().EndDateTime() < nextEnd.TimeLocalL() ) + { + TRACE_EXIT_POINT; + return EFormErrOverlapsExistingInstance; + } + else + { + // Does the exception start before nextFinish? + if( Edited().StartDateTime() < nextEnd.TimeLocalL() ) + { + TRACE_EXIT_POINT; + return EFormErrOverlapsExistingInstance; + } + else + { + TRACE_EXIT_POINT; + return EFormErrOutOfSequence; + } + } + } + + // Does the exception start on the same day as nextStart? + if( CalenDateUtils::OnSameDay( Edited().StartDateTime(), nextStart.TimeLocalL() ) ) + { + TRACE_EXIT_POINT; + return EFormErrInstanceAlreadyExistsOnThisDay; + } + } + + // Does the series have any rdates + RArray rDateArray; + CleanupClosePushL( rDateArray ); + Entry().GetRDatesL( rDateArray ); + TInt rDateCount = rDateArray.Count(); + + if ( rDateCount > 0 ) + { + // If the series has rdates, check that the exception + // does not overlap or start on the same day + const TTime& startDateTime = Edited().StartDateTime(); + const TTime& endDateTime = Edited().EndDateTime(); + TTimeIntervalMinutes duration; + startDateTime.MinutesFrom( endDateTime, duration ); + + for ( TInt index = 0; index < rDateCount; index++ ) + { + const TTime& rDate = rDateArray[ index ].TimeLocalL(); + + if ( !CalenDateUtils::OnSameDay( Original().StartDateTime(), rDate ) ) + { + // Does the exception start or end on the same day as a rdate. + if ( CalenDateUtils::OnSameDay( startDateTime, rDate ) ) + { + CleanupStack::PopAndDestroy(); // rDateArray + TRACE_EXIT_POINT; + return EFormErrInstanceAlreadyExistsOnThisDay; + } + + // Does the exception overlap an rdate? + TTime rDateEnd = rDate + duration; + if ( endDateTime > rDateEnd && startDateTime < rDate ) + { + CleanupStack::PopAndDestroy(); // rDateArray + TRACE_EXIT_POINT; + return EFormErrOverlapsExistingInstance; + } + } + } + } + + CleanupStack::PopAndDestroy(); // rDateArray + TRACE_EXIT_POINT; + return EFormErrNone; + } + +// ----------------------------------------------------------------------------- +// CCalenEditorDataHandler::GetPreviousInstanceTimeL +// Gets the start and end times of the previous instance. Sets both to a null +// time if there is no instance before the current one +// (other items were commented in a header). +// ----------------------------------------------------------------------------- +// +void CCalenEditorDataHandler::GetPreviousInstanceTimeL( TCalTime& aPreviousStartTime, + TCalTime& aPreviousEndTime ) + { + TRACE_ENTRY_POINT; + + aPreviousStartTime.SetTimeLocalL( Time::NullTTime() ); + aPreviousEndTime.SetTimeLocalL( Time::NullTTime() ); + + RPointerArray entries; + CleanupResetAndDestroyPushL(entries); + + EntryViewL().FetchL( Entry().UidL(), entries ); + + TCalTime currentInstanceDate = Entry().RecurrenceIdL(); + if( currentInstanceDate.TimeUtcL() == Time::NullTTime() ) + { + // We must be creating a new exception. Calculate the recurrence id. + TTimeIntervalMinutes timeOfDay = CalenDateUtils::TimeOfDay( entries[0]->StartTimeL().TimeLocalL() ); + TTime beginningOfDay = CalenDateUtils::BeginningOfDay( InstanceDateTime().TimeLocalL() ); + currentInstanceDate.SetTimeLocalL( beginningOfDay + timeOfDay ); + } + + TCalRRule rrule; + if( entries[0]->GetRRuleL(rrule) ) + { + TCalenRepeatIndex repeatIndex = CalenNoteDataUtil::RepeatIndexL( *entries[0] ); + TBool keepLooking = ETrue; + RArray exdates; + CleanupClosePushL( exdates ); + entries[0]->GetExceptionDatesL(exdates); + + // Needed for case ERepeatOther + TCalRRule::TType type( rrule.Type() ); + TInt repeatInterval( rrule.Interval() ); + TCalTime start, end; + TTime previousInstanceTime = Time::NullTTime(); + + while( keepLooking ) + { + // Subtract the repeat interval of the parent. + switch( repeatIndex ) + { + case ERepeatDaily: + currentInstanceDate.SetTimeLocalL( currentInstanceDate.TimeLocalL()-TTimeIntervalDays(1) ); + break; + case ERepeatWeekly: + currentInstanceDate.SetTimeLocalL( currentInstanceDate.TimeLocalL()-TTimeIntervalDays(7) ); + break; + case ERepeatBiWeekly: + currentInstanceDate.SetTimeLocalL( currentInstanceDate.TimeLocalL()-TTimeIntervalDays(14) ); + break; + case ERepeatMonthly: + currentInstanceDate.SetTimeLocalL( currentInstanceDate.TimeLocalL()-TTimeIntervalMonths(1) ); + break; + case ERepeatYearly: + currentInstanceDate.SetTimeLocalL( currentInstanceDate.TimeLocalL()-TTimeIntervalYears(1) ); + break; + case ERepeatWorkdays: + currentInstanceDate.SetTimeLocalL(currentInstanceDate.TimeLocalL()-TTimeIntervalDays(7) ); + case ERepeatOther: + /* This case includes repeating events like: 3 days every week, 3rd weekday of everymonth + that does not fall in any cases above but still have repeating rule*/ + + // Check if the current entry being edited is child entry + // If yes, then put back the child entry time to currentInstanceDate + if(Entry().RecurrenceIdL().TimeUtcL() != Time::NullTTime() ) + { + TTimeIntervalMinutes timeOfDay = CalenDateUtils::TimeOfDay( Entry().StartTimeL().TimeLocalL() ); + TTime beginningOfDay = CalenDateUtils::BeginningOfDay( InstanceDateTime().TimeLocalL() ); + currentInstanceDate.SetTimeLocalL( beginningOfDay + timeOfDay ); + } + + switch( type ) + { + case TCalRRule::EDaily: + start.SetTimeLocalL( currentInstanceDate.TimeLocalL()-TTimeIntervalDays(1 * repeatInterval)); + break; + case TCalRRule::EWeekly: + start.SetTimeLocalL( currentInstanceDate.TimeLocalL()-TTimeIntervalDays(7 * repeatInterval)); + break; + case TCalRRule::EMonthly: + // Add 7 days of buffer to cover the cases were gap b/w two instances of the event + // can go beuong 30 days. Ex: Every third wednesday of every month + start.SetTimeLocalL( currentInstanceDate.TimeLocalL()-TTimeIntervalMonths(repeatInterval)-TTimeIntervalDays(7 * repeatInterval)); + break; + case TCalRRule::EYearly: + // Add 7 days of buffer to cover the cases were gap b/w two instances of the event + // can go beuong 365 days. Ex: Every third wednesday of September of every year + start.SetTimeLocalL( currentInstanceDate.TimeLocalL()-TTimeIntervalYears(repeatInterval)-TTimeIntervalDays(7 * repeatInterval)); + break; + } + + end.SetTimeLocalL(CalenDateUtils::BeginningOfDay(currentInstanceDate.TimeLocalL())); + previousInstanceTime = GetPreviousInstanceForRepeatOtherL(*entries[0], CalCommon::TCalTimeRange( start, end)); + currentInstanceDate.SetTimeLocalL( previousInstanceTime); + break; + default: + case ERepeatNotRepeated: + keepLooking = EFalse; + break; + } + + // Is currentInstanceDate before parent dt start? + if( currentInstanceDate.TimeLocalL() < entries[0]->StartTimeL().TimeLocalL() ) + { + // There are no instances before the exception + keepLooking = EFalse; + } + else + { + // Is there an exdate on currentInstanceDate? + TBool isExdateOnDay = EFalse; + for(TInt i=0; iRecurrenceIdL().TimeLocalL() == currentInstanceDate.TimeLocalL() ) + { + // This child is the previous instance. + aPreviousStartTime = entries[j]->StartTimeL(); + aPreviousEndTime = entries[j]->EndTimeL(); + keepLooking = EFalse; + } + } + break; + } + } + + if( !isExdateOnDay ) + { + // The instance exists and hasn't been deleted or made into an exception. + // Use information from the parent to set the start/end times. + aPreviousStartTime = currentInstanceDate; + + TTimeIntervalMinutes duration; + TTime start = entries[0]->StartTimeL().TimeLocalL(); + TTime end = entries[0]->EndTimeL().TimeLocalL(); + end.MinutesFrom( start, duration ); + aPreviousEndTime.SetTimeLocalL( currentInstanceDate.TimeLocalL() + duration ); + keepLooking = EFalse; + } + } + } + CleanupStack::PopAndDestroy( &exdates ); + } + + CleanupStack::PopAndDestroy(&entries); + + TRACE_EXIT_POINT; + } + +// ----------------------------------------------------------------------------- +// CCalenEditorDataHandler::GetNextInstanceTimeL +// Gets the start and end times of the next instance. Sets both to a null +// time if there is no instance after the current one. +// (other items were commented in a header). +// ----------------------------------------------------------------------------- +// +void CCalenEditorDataHandler::GetNextInstanceTimeL( TCalTime& aNextStartTime, + TCalTime& aNextEndTime ) + { + TRACE_ENTRY_POINT; + + aNextStartTime.SetTimeLocalL( Time::NullTTime() ); + aNextEndTime.SetTimeLocalL( Time::NullTTime() ); + + RPointerArray entries; + CleanupResetAndDestroyPushL(entries); + + EntryViewL().FetchL( Entry().UidL(), entries ); + + TCalTime currentInstanceDate = Entry().RecurrenceIdL(); + if( currentInstanceDate.TimeUtcL() == Time::NullTTime() ) + { + // We must be creating a new exception. Calculate the recurrence id. + TTimeIntervalMinutes timeOfDay = CalenDateUtils::TimeOfDay( entries[0]->StartTimeL().TimeLocalL() ); + TTime beginningOfDay = CalenDateUtils::BeginningOfDay( InstanceDateTime().TimeLocalL() ); + currentInstanceDate.SetTimeLocalL( beginningOfDay + timeOfDay ); + } + + TCalRRule rrule; + if( entries[0]->GetRRuleL(rrule) ) + { + TCalenRepeatIndex repeatIndex = CalenNoteDataUtil::RepeatIndexL( *entries[0] ); + TBool keepLooking = ETrue; + RArray exdates; + CleanupClosePushL( exdates ); + entries[0]->GetExceptionDatesL(exdates); + + // Needed for case ERepeatOther + TCalRRule::TType type( rrule.Type() ); + TInt repeatInterval( rrule.Interval() ); + TCalTime start, end; + TTime nextInstanceTime = Time::NullTTime(); + + while( keepLooking ) + { + // Subtract the repeat interval of the parent. + switch( repeatIndex ) + { + case ERepeatDaily: + currentInstanceDate.SetTimeLocalL( currentInstanceDate.TimeLocalL()+TTimeIntervalDays(1) ); + break; + case ERepeatWeekly: + currentInstanceDate.SetTimeLocalL( currentInstanceDate.TimeLocalL()+TTimeIntervalDays(7) ); + break; + case ERepeatBiWeekly: + currentInstanceDate.SetTimeLocalL( currentInstanceDate.TimeLocalL()+TTimeIntervalDays(14) ); + break; + case ERepeatMonthly: + currentInstanceDate.SetTimeLocalL( currentInstanceDate.TimeLocalL()+TTimeIntervalMonths(1) ); + break; + case ERepeatYearly: + currentInstanceDate.SetTimeLocalL( currentInstanceDate.TimeLocalL()+TTimeIntervalYears(1) ); + break; + case ERepeatWorkdays: + currentInstanceDate.SetTimeLocalL(currentInstanceDate.TimeLocalL()+TTimeIntervalDays(7) ); + break; + case ERepeatOther: + { + /* This case includes repeating events like: 3 days every week, 3rd weekday of everymonth + that does not fall in any cases above but still have repeating rule*/ + + // Check if the current entry being edited is child entry + // If yes, then put back the child entry time to currentInstanceDate + if(Entry().RecurrenceIdL().TimeUtcL() != Time::NullTTime()) + { + TTimeIntervalMinutes timeOfDay = CalenDateUtils::TimeOfDay( Entry().StartTimeL().TimeLocalL() ); + TTime beginningOfDay = CalenDateUtils::BeginningOfDay( InstanceDateTime().TimeLocalL() ); + currentInstanceDate.SetTimeLocalL( beginningOfDay + timeOfDay ); + } + switch( type ) + { + case TCalRRule::EDaily: + end.SetTimeLocalL( currentInstanceDate.TimeLocalL()+TTimeIntervalDays(1 * repeatInterval)); + break; + case TCalRRule::EWeekly: + end.SetTimeLocalL( currentInstanceDate.TimeLocalL()+TTimeIntervalDays(7 * repeatInterval)); + break; + case TCalRRule::EMonthly: + // Add 7 days of buffer to cover the cases were gap b/w two instances of the event + // can go beuong 30 days. Ex: Every third wednesday of every month + end.SetTimeLocalL( currentInstanceDate.TimeLocalL()+TTimeIntervalMonths(repeatInterval)+TTimeIntervalDays(7 * repeatInterval)); + break; + case TCalRRule::EYearly: + // Add 7 days of buffer to cover the cases were gap b/w two instances of the event + // can go beuong 365 days. Ex: Every third wednesday of September of every year + end.SetTimeLocalL( currentInstanceDate.TimeLocalL()+TTimeIntervalYears(repeatInterval)+TTimeIntervalDays(7 * repeatInterval)); + break; + } + + start.SetTimeLocalL(CalenDateUtils::BeginningOfDay(currentInstanceDate.TimeLocalL()+TTimeIntervalDays(1))); + + nextInstanceTime = GetNextInstanceForRepeatOtherL(*entries[0], CalCommon::TCalTimeRange( start, end)); + currentInstanceDate.SetTimeLocalL( nextInstanceTime); + break; + } + case ERepeatNotRepeated: + keepLooking = EFalse; + break; + default: + break; + } + + // Is currentInstanceDate after parent dt end? + if( currentInstanceDate.TimeLocalL() > rrule.Until().TimeLocalL() ) + { + // There are no instances before the exception + keepLooking = EFalse; + } + else + { + // Is there an exdate on currentInstanceDate? + TBool isExdateOnDay = EFalse; + for(TInt i=0; iRecurrenceIdL().TimeLocalL() == currentInstanceDate.TimeLocalL() ) + { + // This child is the previous instance. + aNextStartTime = entries[j]->StartTimeL(); + aNextEndTime = entries[j]->EndTimeL(); + keepLooking = EFalse; + } + } + break; + } + } + + if( !isExdateOnDay ) + { + // The instance exists and hasn't been deleted or made into an exception. + // Use information from the parent to set the start/end times. + aNextStartTime = currentInstanceDate; + + TTimeIntervalMinutes duration; + TTime start = entries[0]->StartTimeL().TimeLocalL(); + TTime end = entries[0]->EndTimeL().TimeLocalL(); + end.MinutesFrom( start, duration ); + aNextEndTime.SetTimeLocalL( currentInstanceDate.TimeLocalL() + duration ); + keepLooking = EFalse; + } + } + } + CleanupStack::PopAndDestroy( &exdates ); + } + + CleanupStack::PopAndDestroy(&entries); + + TRACE_EXIT_POINT; + } + +// ----------------------------------------------------------------------------- +// CCalenEditorDataHandler::CheckAlarmFieldsForErrorsL +// Check the alarm fields for errors. Returns the error if found, or +// EFormErrNone if no error found. +// (other items were commented in a header). +// ----------------------------------------------------------------------------- +// +CCalenEditorDataHandler::TError CCalenEditorDataHandler::CheckAlarmFieldsForErrorsL(TBool aSeries) + { + TRACE_ENTRY_POINT; + + TError error = EFormErrNone; + // If alarm not active, no check + if ( Edited().IsAlarmActivated() ) + { + TTime alarm = Edited().AlarmDateTime(); + TTime startTime = Edited().StartDateTime(); + TTime now(Time::NullTTime()); + now.HomeTime(); + + if(IsAlarmInAcceptablePeriod(error, alarm, startTime)) + { + // ask form (not iEntry) if it is repeating + // if we launched an old exception, then form has no repeat field + // if creating a new exception, then form has repeat field + TBool EventIsInFuture = (IsTimedEntry() && (startTime > now)) || + (!IsTimedEntry() && (startTime.DaysFrom(now) >= TTimeIntervalDays(0))); + if( !aSeries && (alarm < now) && ( iEdited->EntryType()!=CCalEntry::EAnniv ) ) + { + // dont let non-repeating future entries have alarms in past + error = EFormErrAlarmTimePast; + } + /* otherwise, save alarm to past: + * new/old non-repeating & in past + * repeating new/old repeating in past/future + */ + } + } + + TRACE_EXIT_POINT; + return error; + } + +// ----------------------------------------------------------------------------- +// CCalenEditorDataHandler::IsAlarmInAcceptablePeriod +// Check alarm time validity +// (other items were commented in a header). +// ----------------------------------------------------------------------------- +// +TBool CCalenEditorDataHandler::IsAlarmInAcceptablePeriod(TError& aError, +const TTime& aAlarmTime, const TTime& aStartTime) const + { + TRACE_ENTRY_POINT; + + TTime upperLimit = IsTimedEntry() ? + aStartTime : + CalenDateUtils::BeginningOfDay(aStartTime) + TTimeIntervalDays(1); + TTime lowerLimit = aStartTime - TTimeIntervalDays(31); + TBool acceptable = ETrue; + if(aAlarmTime < lowerLimit) + { + acceptable = EFalse; + aError = EFormErrAlarmDateTooManyDaysBeforeNote; + } + else if(aAlarmTime > upperLimit) + { + acceptable = EFalse; + aError = EFormErrAlarmTimeLaterThanNote; + } + + TRACE_EXIT_POINT; + return acceptable; + } + +// ----------------------------------------------------------------------------- +// CCalenEditorDataHandler::ForceValidValuesL +// Force the form to use valid values. This function gets called when the user +// forces the application to exit, for example by pressing the off button. +// We attempt to save as much data as possible from the form. +// (other items were commented in a header). +// ----------------------------------------------------------------------------- +// +void CCalenEditorDataHandler::ForceValidValuesL( CalCommon::TRecurrenceRange aRepeatTarget ) + { + TRACE_ENTRY_POINT; + + // Checks and valid values are + // from S60 Calendar UI spec, chapter 4.6.4.1 Exiting Note Editor: + + // We modify values as we go, thus we can't pre-read fields. + // E.g. when start time is needed in check, it should be always read + // from model, because previous check might have modified it. + + // 2) if ends earlier than starts + if ( Edited().EndDateTime() < Edited().StartDateTime() ) + { + // a) (for new entry) set end time equal to start time + if ( IsCreatingNew() ) + { + Edited().SetEndDateTimeL( Edited().StartDateTime() ); + } + // b) (for existing entries) replace with original end time + else + { + Edited().SetEndDateTimeL( Original().EndDateTime() ); + // if still wrong, replace start time also + Edited().SetStartDateTimeL( Original().StartDateTime() ); + } + } // END end < start check + + // 1) Alarm validation + if ( Edited().IsAlarmActivated() ) + { + TTime now; + now.HomeTime(); + + // Note: all three checks are done for each alarm. + // Alarm time is read always again, because modifications are done after each check + + // 1.1) a) if alarm more than 31 days before start, alarm is set 31 days before start + // b) (for todos) - "" - before due date + TTime maxAlarmBeforeStart = Edited().StartDateTime() - TTimeIntervalDays( 31 ); + if ( Edited().AlarmDateTime() < maxAlarmBeforeStart ) + { + Edited().SetAlarmDateTimeL( maxAlarmBeforeStart ); + } + + // 1.2) a) (meetings) if alarm later than start, set it to default alarm time + // b) (for todos & annivs) - "" - later than due date + TTime atLatest = Edited().StartDateTime(); + if(!IsTimedEntry()) + { + atLatest += TTimeIntervalDays(1); + atLatest -= TTimeIntervalSeconds(1); + // at Latest for ToDo/Anniv would be DD @ 23:59:59 + } + if( Edited().AlarmDateTime() > atLatest ) + { + SetDefaultAlarmDateTimeL(); + } + + // 1.3) if alarm is set to passed time, + if ( Edited().AlarmDateTime() < now ) + { + // a) it is set off for + // - new/old NON-REPEATING entries + if (!Edited().IsRepeating() && Edited().EntryType() != CCalEntry::EAnniv ) + { + Edited().SetAlarmOffL(); + } + // b) (for existing entries) it is saved, unless it has failed 1.1 or 1.2 + else + { + // keep, if it is repeating entry, since repeating entries + // can have alarms in past + } + } + } // END alarm checks + + // 3) Repeat rule checks + if ( Edited().IsRepeating() ) + { + // if repeating note's repeat until date + // a) (for new entries) isn't one day later than start date + // b) (for existing entries) is before start date + // set it to one year later from start date + TTime repeatUntilDay = CalenDateUtils::BeginningOfDay( + Edited().RepeatUntilDateTime() ); + TTime startDay = CalenDateUtils::BeginningOfDay( Edited().StartDateTime() ); + if ( ( IsCreatingNew() && repeatUntilDay <= startDay) || + (! IsCreatingNew() && repeatUntilDay < startDay) ) + { + Edited().SetRepeatUntilDateTimeL( startDay + TTimeIntervalYears(1) ); + } + } + + // 4) if due date of todo is passed or invalid, save it with entered date + // no operation (this is just clarification in UI spec + + + // 5) Check for out of sequence for exceptions + // 6) Check for duration > rpt interval for series + + if( aRepeatTarget == CalCommon::EThisOnly ) + { + TError error = CheckForOutOfSequenceL(); + + if( error != EFormErrNone ) + { + // Revert start/end times + Edited().SetStartDateTimeL( Original().StartDateTime() ); + Edited().SetEndDateTimeL( Original().EndDateTime() ); + } + } + else if( aRepeatTarget == CalCommon::EThisAndAll ) + { + if( DurationGreaterThanRepeatIntervalError() ) + { + // Revert start/end times + Edited().SetStartDateTimeL( Original().StartDateTime() ); + Edited().SetEndDateTimeL( Original().EndDateTime() ); + } + } + + TRACE_EXIT_POINT; + } + +// ----------------------------------------------------------------------------- +// CCalenEditorDataHandler::IsOldEntry +// Returns ETrue if the entry was already created previously so that +// we are now editing it, returns EFalse otherwise. +// (other items were commented in a header). +// ----------------------------------------------------------------------------- +// +TBool CCalenEditorDataHandler::IsOldEntry() + { + TRACE_ENTRY_POINT; + + TRACE_EXIT_POINT; + return !IsCreatingNew(); + } + +// ----------------------------------------------------------------------------- +// CCalenEditorDataHandler::AreTextFieldsEmptyL +// Returns ETrue if summary && location && description text fields are all empty +// Returns EFalse otherwise. +// (other items were commented in a header). +// ----------------------------------------------------------------------------- +// +TBool CCalenEditorDataHandler::AreTextFieldsEmptyL() + { + TRACE_ENTRY_POINT; + + // We have to consider texts empty, if only summary is empty, in case + // location is not supported, because location can still exists in database. + TBool summaryAndDescriptionEmpty = CalenAgendaUtils::IsEmptyText( iEdited->Summary() ) && + CalenAgendaUtils::IsEmptyText( iEdited->Description() ); + + if(LocationEnabledInUiL()) + { + TRACE_EXIT_POINT; + return summaryAndDescriptionEmpty && CalenAgendaUtils::IsEmptyText( iEdited->Location() ); + } + + TRACE_EXIT_POINT; + + return summaryAndDescriptionEmpty; // and location is empty implicitly + } + +// ----------------------------------------------------------------------------- +// CCalenEditorDataHandler::AreTextFieldsClearedL +// Returns ETrue if the user cleared the text in the location and summary fields. +// (other items were commented in a header). +// ----------------------------------------------------------------------------- +// +TBool CCalenEditorDataHandler::AreTextFieldsClearedL() + { + TRACE_ENTRY_POINT; + + if ( CalenAgendaUtils::IsEmptyText( iEdited->Summary() ) && + CalenAgendaUtils::IsEmptyText( iEdited->Description() ) ) + { + if ( LocationEnabledInUiL() ) + { + if ( CalenAgendaUtils::IsEmptyText( iEdited->Location() ) ) + { + if ( IsSummaryEmptied() || IsDescriptionEmptied() || IsLocationEmptied() ) + { + TRACE_EXIT_POINT; + return ETrue; + } + } + } + + else if ( IsSummaryEmptied() || IsDescriptionEmptied() ) + { + TRACE_EXIT_POINT; + return ETrue; + } + } + + TRACE_EXIT_POINT; + + return EFalse; + } + +// ----------------------------------------------------------------------------- +// CCalenEditorDataHandler::IsDescriptionEmptied +// Returns ETrue, the description was not empty in original && is empty +// in the edited note +// Returns EFalse, otherwise +// (other items were commented in a header). +// ----------------------------------------------------------------------------- +TBool CCalenEditorDataHandler::IsDescriptionEmptied() + { + TRACE_ENTRY_POINT; + + if( !CalenAgendaUtils::IsEmptyText( iOriginal->Description() ) && + CalenAgendaUtils::IsEmptyText( iEdited->Description() ) ) + { + TRACE_EXIT_POINT; + return ETrue; + } + + TRACE_EXIT_POINT; + + return EFalse; + } + +// ----------------------------------------------------------------------------- +// CCalenEditorDataHandler::IsSummaryEmptied +// Returns ETrue, the summary was not empty in original && is empty +// in the edited note +// Returns EFalse, otherwise +// (other items were commented in a header). +// ----------------------------------------------------------------------------- +TBool CCalenEditorDataHandler::IsSummaryEmptied() + { + TRACE_ENTRY_POINT; + + if( !CalenAgendaUtils::IsEmptyText( iOriginal->Summary() ) && + CalenAgendaUtils::IsEmptyText( iEdited->Summary() ) ) + { + TRACE_EXIT_POINT; + return ETrue; + } + + TRACE_EXIT_POINT; + return EFalse; + } + +// ----------------------------------------------------------------------------- +// CCalenEditorDataHandler::IsLocationEmptied +// Returns ETrue, the summary was not empty in original && is empty +// in the edited note +// Returns EFalse, otherwise +// (other items were commented in a header). +// ----------------------------------------------------------------------------- +TBool CCalenEditorDataHandler::IsLocationEmptied() + { + TRACE_ENTRY_POINT; + + if( !CalenAgendaUtils::IsEmptyText( iOriginal->Location() ) && + CalenAgendaUtils::IsEmptyText( iEdited->Location() ) ) + { + TRACE_EXIT_POINT; + return ETrue; + } + + TRACE_EXIT_POINT; + return EFalse; + } + +// ----------------------------------------------------------------------------- +// CCalenEditorDataHandler::IsEntryTypeEdited +// Returns ETrue if the 'Entry Type' is edited, EFalse otherwise. +// (other items were commented in a header). +// ----------------------------------------------------------------------------- +// +TBool CCalenEditorDataHandler::IsEntryTypeEdited() + { + TRACE_ENTRY_POINT; + + TRACE_EXIT_POINT; + return iOriginal->EntryType() != iEdited->EntryType(); + } + +// ----------------------------------------------------------------------------- +// CCalenEditorDataHandler::IsStartDateTimeEdited +// Returns ETrue if the start date/time has been edited, EFalse otherwise. +// (other items were commented in a header). +// ----------------------------------------------------------------------------- +// +TBool CCalenEditorDataHandler::IsStartDateTimeEdited() + { + TRACE_ENTRY_POINT; + + TBool isEdited = EFalse; + + TTime orgTTime = iOriginal->StartDateTime(); + TTime editedTTime = iEdited->StartDateTime(); + + // Get TDateTime of Original & Edited entries. + TDateTime orgDT = orgTTime.DateTime(); + TDateTime editedDT = editedTTime.DateTime(); + + if( !IsTimedEntry() && IsCreatingNew() ) + { + // For non-timed event,on while creating new entry. + // check for only Date field change only. + if( orgDT.Day() != editedDT.Day() || + orgDT.Month() != editedDT.Month() || + orgDT.Year() != editedDT.Year() ) + { + isEdited = ETrue; + } + } + else + { + + // For timed entry, check for Entry Date/Time Changes + if( orgTTime != editedTTime ) + { + isEdited = ETrue; + } + } + + TRACE_EXIT_POINT; + return isEdited; + + //return iOriginal->StartDateTime() != iEdited->StartDateTime(); + + } + +// ----------------------------------------------------------------------------- +// CCalenEditorDataHandler::IsEndDateTimeEdited +// Returns ETrue if the end date/time has been edited, EFalse otherwise. +// (other items were commented in a header). +// ----------------------------------------------------------------------------- +// +TBool CCalenEditorDataHandler::IsEndDateTimeEdited() + { + TRACE_ENTRY_POINT; + TBool isEdited = EFalse; + + TTime orgTTime = iOriginal->EndDateTime(); + TTime editedTTime = iEdited->EndDateTime(); + + // Get TDateTime of Original & Edited entries. + TDateTime orgDT = orgTTime.DateTime(); + TDateTime editedDT = editedTTime.DateTime(); + + if( !IsTimedEntry() && IsCreatingNew() ) + { + + // For non-timed event, check for only Date field change. + // No need to check for Entry time change. + if( orgDT.Day() != editedDT.Day() || + orgDT.Month() != editedDT.Month() || + orgDT.Year() != editedDT.Year() ) + { + isEdited = ETrue; + } + } + else + { + // For timed entry, check for Entry Date/Time Changes + if( orgTTime != editedTTime ) + { + isEdited = ETrue; + } + } + + TRACE_EXIT_POINT; + return isEdited; + + //return iOriginal->EndDateTime() != iEdited->EndDateTime(); + } + +// ----------------------------------------------------------------------------- +// CCalenEditorDataHandler::IsDescriptionEdited +// Returns ETrue if the description has been modified, EFalse otherwise. +// (other items were commented in a header). +// ----------------------------------------------------------------------------- +// +TBool CCalenEditorDataHandler::IsDescriptionEdited() + { + TRACE_ENTRY_POINT; + + TRACE_EXIT_POINT; + return iOriginal->Description() != iEdited->Description(); + } + +// ----------------------------------------------------------------------------- +// CCalenEditorDataHandler::IsPriorityEdited +// Returns ETrue if the priotity has been modified, EFalse otherwise. +// (other items were commented in a header). +// ----------------------------------------------------------------------------- +// +TBool CCalenEditorDataHandler::IsPriorityEdited() + { + TRACE_ENTRY_POINT; + + TRACE_EXIT_POINT; + return iOriginal->Priority() != iEdited->Priority(); + } + +// ----------------------------------------------------------------------------- +// CCalenEditorDataHandler::IsCalendarEditedL +// Returns ETrue if the calendar filed has been edited, EFalse otherwise. +// (other items were commented in a header). +// ----------------------------------------------------------------------------- +// +TBool CCalenEditorDataHandler::IsCalendarEditedL() + { + TRACE_ENTRY_POINT; + + TRACE_EXIT_POINT; + return iCalendarFieldEdited; + } + +// ----------------------------------------------------------------------------- +// CCalenEditorDataHandler::IsSummaryEdited +// Returns ETrue if the summary has been edited, EFalse otherwise. +// (other items were commented in a header). +// ----------------------------------------------------------------------------- +// +TBool CCalenEditorDataHandler::IsSummaryEdited() + { + TRACE_ENTRY_POINT; + + // For summary and location, we compare only that part of text + // that fits to editor. If it's equal, then we don't go and unnecessarily + // truncate existing data. + TRACE_EXIT_POINT; + return ( iOriginal->Summary().Left( iMaxTextEditorLength ) + != iEdited->Summary().Left( iMaxTextEditorLength ) ); + } + +// ----------------------------------------------------------------------------- +// CCalenEditorDataHandler::IsLocationEdited +// Returns ETrue if the location has been edited, EFalse otherwise. +// (other items were commented in a header). +// ----------------------------------------------------------------------------- +// +TBool CCalenEditorDataHandler::IsLocationEdited() + { + TRACE_ENTRY_POINT; + + // For summary and location, we compare only that part of text + // that fits to editor. If it's equal, then we don't go and unnecessarily + // truncate existing data. + TRACE_EXIT_POINT; + return ( iOriginal->Location().Left( iMaxTextEditorLength ) + != iEdited->Location().Left( iMaxTextEditorLength ) ); + } + +// ----------------------------------------------------------------------------- +// CCalenEditorDataHandler::IsAlarmEditedL +// Returns ETrue if the alarm has been edited, EFalse otherwise. +// (other items were commented in a header). +// ----------------------------------------------------------------------------- +// +TBool CCalenEditorDataHandler::IsAlarmEditedL() + { + TRACE_ENTRY_POINT; + + if ( iOriginal->IsAlarmActivated() && iEdited->IsAlarmActivated() ) + { + // The alarm must be flagged as changed if the interval between the entry start + // and the alarm is different. (We can't just compare alarm times directly because + // the start date of the entry may have changed.) + TTimeIntervalMinutes origOffset, editedOffset; + + User::LeaveIfError( iOriginal->AlarmDateTime().MinutesFrom( iOriginal->StartDateTime(), origOffset ) ); + User::LeaveIfError( iEdited->AlarmDateTime().MinutesFrom( iEdited->StartDateTime(), editedOffset ) ); + + TBool alarmTimeChanged = origOffset != editedOffset; + + TRACE_EXIT_POINT; + return alarmTimeChanged; + } + else + { + TBool statusChanged = iOriginal->IsAlarmActivated() != iEdited->IsAlarmActivated(); + + TRACE_EXIT_POINT; + return statusChanged; + } + } + +// ----------------------------------------------------------------------------- +// CCalenEditorDataHandler::IsRepeatRuleEdited +// Returns ETrue if the repeat rule has been edited, EFalse otherwise. +// (other items were commented in a header). +// ----------------------------------------------------------------------------- +// +TBool CCalenEditorDataHandler::IsRepeatRuleEdited() + { + TRACE_ENTRY_POINT; + + if ( iOriginal->IsRepeating() && iEdited->IsRepeating() ) + { + TBool repeatTypeChanged = iOriginal->RepeatType() != iEdited->RepeatType(); + TBool untilDateChanged = iOriginal->RepeatUntilDateTime() != iEdited->RepeatUntilDateTime(); + + TRACE_EXIT_POINT; + return repeatTypeChanged || untilDateChanged; + } + else + { + TBool statusChanged = iOriginal->IsRepeating() != iEdited->IsRepeating(); + + TRACE_EXIT_POINT; + return statusChanged; + } + } + +// ----------------------------------------------------------------------------- +// CCalenEditorDataHandler::LocationEnabledInUiL +// Returns ETrue if the location is a valid field for this type of entry +// (i.e. meetings & reminders only), EFalse otherwise. +// (other items were commented in a header). +// ----------------------------------------------------------------------------- +// +TBool CCalenEditorDataHandler::LocationEnabledInUiL() + { + TRACE_ENTRY_POINT; + + // Location is enabled for timed entries (i.e. an appointment or a + // reminder) + TRACE_EXIT_POINT; + return IsTimedEntry(); + } + +// ----------------------------------------------------------------------------- +// CCalenEditorDataHandler::IsTimedEntry +// Returns ETrue if this is a timed entry, EFalse otherwise. +// (other items were commented in a header). +// ----------------------------------------------------------------------------- +// +TBool CCalenEditorDataHandler::IsTimedEntry() const + { + TRACE_ENTRY_POINT; + + TRACE_EXIT_POINT; + return IsTimed( iEdited->EntryType() ); + } + +// ----------------------------------------------------------------------------- +// CCalenEditorDataHandler::IsCreatingNew +// Returns ETrue if the user is creating a new entry, EFalse if the user is +// editing an existing entry. +// (other items were commented in a header). +// ----------------------------------------------------------------------------- +// +TBool CCalenEditorDataHandler::IsCreatingNew() const + { + TRACE_ENTRY_POINT; + + TRACE_EXIT_POINT; + return iIsCreatingNew; + } + +// ----------------------------------------------------------------------------- +// CCalenEditorDataHandler::IsStartDateChanged +// Returns ETrue if the start date of the entry has been changed, +// EFalse otherwise. Note that this function only checks the date, +// and not the time of day, of the entry. +// (other items were commented in a header). +// ----------------------------------------------------------------------------- +// +TBool CCalenEditorDataHandler::IsStartDateChanged() + { + TRACE_ENTRY_POINT; + + TRACE_EXIT_POINT; + return ! CalenDateUtils::OnSameDay( iOriginal->StartDateTime(), + iEdited->StartDateTime() ); + } + +// ----------------------------------------------------------------------------- +// CCalenEditorDataHandler::IsAttachmentExists +// Returns ETrue calendar entry is having attachments +// (other items were commented in a header). +// ----------------------------------------------------------------------------- +// +TBool CCalenEditorDataHandler::IsAttachmentExists() + { + TRACE_ENTRY_POINT; + TBool attachmentExits(EFalse); + + // TODO: Uncomment this when enabling attachment support + + if( (iOriginal->AttachmentCount() != iEdited->AttachmentCount()) + || iServices.GetAttachmentData()->NumberOfItems() ) + { + attachmentExits = ETrue; + } + + TRACE_EXIT_POINT; + return attachmentExits; + } + +// ----------------------------------------------------------------------------- +// CCalenEditorDataHandler::IsEventTypeEdited +// (other items were commented in a header). +// ----------------------------------------------------------------------------- +// +TBool CCalenEditorDataHandler::IsEventTypeEdited() + { + TRACE_ENTRY_POINT; + TBool fieldModified = EFalse; + + // Check EventType field is edited for already saved Entry + // For Newly created entry, ignore the EventTypeChange + if( (iOriginal->EntryType() != iEdited->EntryType()) && !IsCreatingNew() ) + { + fieldModified = ETrue; + } + + TRACE_EXIT_POINT; + return fieldModified; + } + +// ----------------------------------------------------------------------------- +// CCalenEditorDataHandler::AllDayFieldEdited +// Returns ETrue if `All day event' line has been modified. +// (other items were commented in a header). +// ----------------------------------------------------------------------------- +// +TBool CCalenEditorDataHandler::AllDayFieldEdited() + { + TRACE_ENTRY_POINT; + TBool fieldModified = EFalse; + + if( iOriginal->IsAllDayEvent() != iEdited->IsAllDayEvent() ) + { + fieldModified = ETrue; + } + + TRACE_EXIT_POINT; + return fieldModified; + } + +// ----------------------------------------------------------------------------- +// CCalenEditorDataHandler::SetDefaultAlarmDateTimeL +// Sets the alarm to the default value. This function is called after the user +// forces an exit, e.g. when pressing the power off button. +// (other items were commented in a header). +// ----------------------------------------------------------------------------- +// +void CCalenEditorDataHandler::SetDefaultAlarmDateTimeL() + { + TRACE_ENTRY_POINT; + + /** + * Default alarm time is UI spec logic. + * It is important that this is here, because there shouldn't be UI spec logic in CCalenEntryUtil, + * only in CCalenEditorDataHandler. + * + * It is still a bit odd, that other set methods are in CCalenEntryUtil but not here. + * Maybe I add wrapper set methods for edited data also in *Handler. + * e.g. void SetAlarmOffL() { Edited().SetAlarmOffL(); } + * + */ + TTime defaultAlarm; + + if ( IsTimedEntry() ) + { + TTimeIntervalMinutes defaultOffsetM( 15 ); // FIXME: should be read from central repository + defaultAlarm = Edited().StartDateTime() - defaultOffsetM; + } + else + { + TTimeIntervalHours defaultOffsetH( -8 ); + defaultAlarm = CalenDateUtils::BeginningOfDay( Edited().EventDateTime() ) - defaultOffsetH; + } + + Edited().SetAlarmOnL( defaultAlarm ); + + TRACE_EXIT_POINT; + } + +// ----------------------------------------------------------------------------- +// CCalenEditorDataHandler::CalculateRepeatRuleL +// Calculates the repeat rule for a given entry. +// (other items were commented in a header). +// ----------------------------------------------------------------------------- +// +void CCalenEditorDataHandler::CalculateRepeatRuleL( + const TTime& aStartDateTime, + TCalenRepeatIndex aNewRepeatType, + const TTime& aRepeatUntil, + TCalRRule& aRRule) + { + TRACE_ENTRY_POINT; + + // FIXME: This is copy-pasted from old implementation. + // This should be rewritten with care. + //If it ain't broke...? + TTime startDateTime = aStartDateTime; + TTime repeatUntil = aRepeatUntil; + + TCalRRule origRRule; + TBool hasOrigRRule = iEntry.GetRRuleL( origRRule ); + + TBool isStartDateSame = CalenDateUtils::OnSameDay( + Original().StartDateTime(), Edited().StartDateTime() ); + + // + // If repeat type is changed, repeat start date + // is replaced to the date of "Start date" field. + // + if ( hasOrigRRule && + isStartDateSame && + aNewRepeatType == Original().RepeatType() ) + { + aRRule = origRRule; // keep the original setting... + // until date changed? + } + else if (aNewRepeatType == ERepeatOther) + { + // prevRptDef is ALWAYS non-NULL in this block, + // because previous note's repeat ALWAYS exists. + aRRule = origRRule; + TCalTime repeatStart; + if ( UseFloatingTimeL() ) + { + repeatStart.SetTimeLocalFloatingL( startDateTime ); + } + else + { + repeatStart.SetTimeLocalL( startDateTime ); + } + aRRule.SetDtStart( repeatStart ); + } + else + { + TCalTime repeatSt; + if ( UseFloatingTimeL() ) + { + repeatSt.SetTimeLocalFloatingL( startDateTime ); + } + else + { + repeatSt.SetTimeLocalL( startDateTime ); + } + aRRule.SetDtStart( repeatSt ); + aRRule.SetInterval( 1 ); + + TInt interval(1); + + switch ( aNewRepeatType ) + { + case (ERepeatDaily): // Daily + { + aRRule.SetType( TCalRRule::EDaily ); + break; + } + case (ERepeatWeekly): // Weekly + { + if (hasOrigRRule && origRRule.Type() == TCalRRule::EDaily && + origRRule.Interval() == 7) + { + // If old note's repeat is every 7 days, + // set repeat to every 7 days to Agenda, NOT every week. + // + aRRule.SetType( TCalRRule::EDaily ); + interval = 7; + } + else + { + aRRule.SetType( TCalRRule::EWeekly ); + + RArray dayArray; + CleanupClosePushL( dayArray ); + dayArray.Append( aRRule.DtStart().TimeLocalL().DayNoInWeek() ); + aRRule.SetByDay( dayArray ); + CleanupStack::PopAndDestroy( &dayArray ); + } + break; + } + case (ERepeatBiWeekly): // Bi-weekly + { + if ( hasOrigRRule && origRRule.Type() == TCalRRule::EDaily && + origRRule.Interval() == 14) + { + // If old note's repeat is every 14 days, + // set repeat to every 14 days to Agenda, NOT every 2 weeks. + // + aRRule.SetType( TCalRRule::EDaily ); + interval = 14; + } + else + { + aRRule.SetType( TCalRRule::EWeekly ); + interval = 2; + + RArray dayArray; + CleanupClosePushL( dayArray ); + dayArray.Append( aRRule.DtStart().TimeLocalL().DayNoInWeek() ); + aRRule.SetByDay( dayArray ); + CleanupStack::PopAndDestroy( &dayArray ); + } + break; + } + case ERepeatWorkdays:// Weekly + { + TLocale iLocale; + TUint iWorkDays = iLocale.WorkDays(); + if (hasOrigRRule && origRRule.Type() == TCalRRule::EDaily + && origRRule.Interval() == 7) + { + // If old note's repeat is every 7 days, + // set repeat to every 7 days to Agenda, NOT every week. + // + aRRule.SetType(TCalRRule::EDaily); + interval = 7; + } + else + { + aRRule.SetType(TCalRRule::EWeekly); + RArray dayArray; + CleanupClosePushL(dayArray); + TInt fixedNum = 1; + TInt ruleday = 0; + for (TInt i =0 ; i < KNoOfDaysInWeek ; i++) + { + ruleday = fixedNum << i; + if(iWorkDays & ruleday) + { + dayArray.Append((TDay)i); + aRRule.SetByDay(dayArray); + } + } + CleanupStack::PopAndDestroy(&dayArray); + } + break; + + } + case (ERepeatMonthly): + // Monthly By Position + { + aRRule.SetType( TCalRRule::EMonthly ); + + RArray dateArray; + CleanupClosePushL( dateArray ); + dateArray.Append( aRRule.DtStart().TimeLocalL().DayNoInMonth() ); + aRRule.SetByMonthDay( dateArray ); + CleanupStack::PopAndDestroy( &dateArray ); + + break; + } + case (ERepeatYearly): // Yearly + { + // If old note's repeat is every 12 months, + // set repeat to every 12 months to Agenda, NOT every year. + // + if ( hasOrigRRule && origRRule.Type() == TCalRRule::EMonthly && + origRRule.Interval() == 12) + { + aRRule.SetType( TCalRRule::EMonthly ); + interval = 12; + } + else + { + aRRule.SetType( TCalRRule::EYearly ); + } + break; + } + default: // Repeat item is "Other" + { + interval = aRRule.Interval(); // To copy previous note's interval + break; + } + } + + aRRule.SetInterval( interval ); + } + + // If repeat until is max date of Calendar, + // note repeat is set to forever. + if ( ! CalenDateUtils::IsValidDay(repeatUntil) || IsForeverDate( repeatUntil ) ) + { + // Set repeat to forever: + aRRule.SetCount( 0 ); + } + else + { + // Change repeatuntil so that hours, minutes, ... are same as in start date + TDateTime dtUntil = repeatUntil.DateTime(); + TDateTime dtStart= iEntry.StartTimeL().TimeLocalL().DateTime(); + + dtUntil.Set( dtUntil.Year(), dtUntil.Month(), dtUntil.Day(), + dtStart.Hour(), dtStart.Minute(), dtStart.Second(), dtStart.MicroSecond()); + + TCalTime calRepeatUntil; + if ( UseFloatingTimeL() ) + { + calRepeatUntil.SetTimeLocalFloatingL( TTime( dtUntil ) ); + } + else + { + calRepeatUntil.SetTimeLocalL( TTime( dtUntil ) ); + } + aRRule.SetUntil( calRepeatUntil ); + } + + TRACE_EXIT_POINT; + } + +// ----------------------------------------------------------------------------- +// CCalenEditorDataHandler::SetDefaultNewValuesL +// Sets the default values when creating a new entry. +// (other items were commented in a header). +// ----------------------------------------------------------------------------- +// +void CCalenEditorDataHandler::SetDefaultNewValuesL(CCalenEntryUtil& aData) + { + TRACE_ENTRY_POINT; + + // FIXME: I don't know if this method should be needed or not. + // It's a bit unclear that if pass entry that is new, + // should we set default data in caller (application) or + // here. + // But to replicate old editor functionality, we set in editor + // side + + ASSERT( IsCreatingNew() ); + // start time and end time are taken from passed entry. + // They have to be initialized outside of editors + + // subject, location, description are by default empty + + // repeat rule is by default off + + // alarm is by default off + + // synch value + aData.SetSynchTypeL( CCalenEntryUtil::ESynchPrivate ); + + // priority + aData.SetPriorityL( CCalenEntryUtil::ETodoPriorityNormal ); + + TRACE_EXIT_POINT; + } + +// ----------------------------------------------------------------------------- +// CCalenEditorDataHandler::IsChildL +// Returns ETrue if the entry is a child, EFalse otherwise. +// (other items were commented in a header). +// ----------------------------------------------------------------------------- +// +TBool CCalenEditorDataHandler::IsChildL() const + { + TRACE_ENTRY_POINT; + + TRACE_EXIT_POINT; + return iRecurrenceId.TimeUtcL() != Time::NullTTime(); + } + +// ----------------------------------------------------------------------------- +// CCalenEditorDataHandler::UseFloatingTimeL +// Returns ETrue if the entry should use floating time, EFalse otherwise. +// (other items were commented in a header). +// ----------------------------------------------------------------------------- +// +TBool CCalenEditorDataHandler::UseFloatingTimeL() const + { + TRACE_ENTRY_POINT; + + // Use floating time based on type + // This way we ensure that entries _created_ with Calendar Editors + // are consistent regarding time mode. + // + TRACE_EXIT_POINT; + return (! IsTimedEntry()); // use floating time for non-timed entries + + } + +// ----------------------------------------------------------------------------- +// CCalenEditorDataHandler::WriteStartAndEndTimesToEntryL +// Writes the start and end times from the form to the CCalEntry. +// (other items were commented in a header). +// ----------------------------------------------------------------------------- +// +void CCalenEditorDataHandler::WriteStartAndEndTimesToEntryL( CalCommon::TRecurrenceRange aRepeatTarget ) + { + TRACE_ENTRY_POINT; + + const TBool unDatedTodo=(iEntry.EntryTypeL()==CCalEntry::ETodo) && (iEntry.EndTimeL().TimeLocalL()==Time::NullTTime()); + const TBool unDatedTodoWithAlarm=unDatedTodo && (Edited().IsAlarmActivated()); + + TBool shouldWriteBothTimes = + // For new entries + IsCreatingNew() || + // When creating exception entry (exception entry's time are read from editors) + ( aRepeatTarget != CalCommon::EThisAndAll ) || + // when repeating entry is modified to be non-repeating (entry's times are read from editors) + ( Original().IsRepeating() && ! Edited().IsRepeating() ); + + if ( shouldWriteBothTimes || IsStartDateTimeEdited() || IsEndDateTimeEdited() || unDatedTodoWithAlarm ) + { + + TCalTime entryStart; + TCalTime entryEnd; + + // 1) Initialize entryStart and entryEnd with original times. + // But for todos we need to be careful, because there might not be them originally + // In S60 UI, todos don't have starttime, so we use same for both start and end time. + if (iEntry.EntryTypeL()==CCalEntry::ETodo) + { + // if undated todo and it has alarm activated + if ((iEntry.EndTimeL().TimeLocalL()==Time::NullTTime()) && (Edited().IsAlarmActivated())) + { + entryStart.SetTimeLocalL(Edited().EventDateTime()); + entryEnd.SetTimeLocalL(Edited().EventDateTime()); + shouldWriteBothTimes=ETrue; // Ensures that times are written later + } + else + { + entryStart = iEntry.EndTimeL(); + entryEnd = iEntry.EndTimeL(); + } + } + else + { + entryStart = iEntry.StartTimeL(); + entryEnd = iEntry.EndTimeL(); + } + + // 2) Decide start time + if ( shouldWriteBothTimes || IsStartDateTimeEdited() ) + { + TTime newStart = Edited().StartDateTime(); + TDateTime newStartDT = newStart.DateTime(); + // Special case: + // In case c) only start time (hh:mm) of d) existing a) repeating b) series is edited, + // we need to preserve start date (dd.mm.yyy), because start date in editors + // is instance's start date. + // (Example of problematic cases that brings up, when UI represents + // two different concepts in same start date field) + // a) repeating b) series is edited c) only start time is edited d) existing + if ( Edited().IsRepeating() && aRepeatTarget == CalCommon::EThisAndAll && ! IsStartDateChanged() && ! IsCreatingNew() ) + { + TDateTime oldDt = iEntry.StartTimeL().TimeLocalL().DateTime(); + TDateTime editDt = newStart.DateTime(); + newStart = TTime( TDateTime( oldDt.Year(), oldDt.Month(), oldDt.Day(), + editDt.Hour(), editDt.Minute(), + oldDt.Second(), oldDt.MicroSecond() ) ); + } + else if ( ( Edited().RepeatType() == ERepeatOther ) && IsStartDateChanged() ) + { + // The start time might have been disguised because the entry + // consists of an rRule and rDates. The actual start time + // needs to be extracted + RArray rDates; + CleanupClosePushL( rDates ); + + iEntry.GetRDatesL( rDates ); + if ( rDates.Count() > 0 ) + { + // As we have rDates, check to see if there is an + // rRule + TCalRRule rRule; + if ( iEntry.GetRRuleL( rRule ) ) + { + TTime firstRDate = rDates[ 0 ].TimeLocalL(); + TTime rRuleStart = iEntry.StartTimeL().TimeLocalL(); + TTime lastRDate = rDates[ rDates.Count() - 1 ].TimeLocalL(); + TTimeIntervalMinutes startDateShiftInterval = 0; + if ( firstRDate < rRuleStart ) + { + // The first rDate is before the start of the rRule + // So adjust the start date accordingly + + User::LeaveIfError( newStart.MinutesFrom( firstRDate, startDateShiftInterval ) ); + } + else + { + // The start of the rRule comes first + User::LeaveIfError( newStart.MinutesFrom( rRuleStart, startDateShiftInterval ) ); + } + + newStart = rRuleStart + startDateShiftInterval; + + TInt rRuleCount = rRule.Count(); + rRule.SetCount( rRuleCount ); + + TCalTime rRuleDT; + if ( UseFloatingTimeL() ) + { + rRuleDT.SetTimeLocalFloatingL( newStart ); + } + else + { + rRuleDT.SetTimeLocalL( newStart ); + } + + rRule.SetDtStart( rRuleDT ); + + // For rearranging the recurrence pattern for an + // entry having rdates + + TInt repeatType = rRule.Type(); + switch ( repeatType ) + { + case ( TCalRRule::EWeekly ): //For Weekly repeating event + { + RArray dayArray; + CleanupClosePushL( dayArray ); + dayArray.Append( rRule.DtStart().TimeLocalL().DayNoInWeek() ); + rRule.SetByDay( dayArray ); + CleanupStack::PopAndDestroy( &dayArray ); + + // Adjust the RepeatUntil date + ApplyUntilDateToRRuleL( rRule, Edited().RepeatUntilDateTime() ); + break; + } + case ( TCalRRule::EMonthly ):// For Monthly repeating event + { + RArray dateArray; + CleanupClosePushL( dateArray ); + dateArray.Append( rRule.DtStart().TimeLocalL().DayNoInMonth() ); + rRule.SetByMonthDay( dateArray ); + CleanupStack::PopAndDestroy( &dateArray ); + + // Adjust the RepeatUntil date + ApplyUntilDateToRRuleL( rRule, Edited().RepeatUntilDateTime() ); + break; + } + + case ( TCalRRule::EDaily ):// For Daily repeating event + { + TCalRRule curRule; + iEntry.GetRRuleL( curRule ); + + // Adjust the RepeatUntil date + if(lastRDate < curRule.Until().TimeLocalL() ) + { + ApplyUntilDateToRRuleL( rRule, Edited().RepeatUntilDateTime() ); + } + + + break; + } + default: + { + break; + } + } + + + iEntry.SetRRuleL( rRule ); + } + } + + CleanupStack::PopAndDestroy(); // rDates + } + + if ( UseFloatingTimeL() ) + { + entryStart.SetTimeLocalFloatingL( newStart ); + } + else + { + entryStart.SetTimeLocalL( newStart ); + } + } + + // 3) Decide end time + if ( shouldWriteBothTimes || IsEndDateTimeEdited() ) + { + TTime start = Edited().StartDateTime(); + TTime end = Edited().EndDateTime(); + + // For all-day events, we add 24 hours to end date + // to make it full day long + /*if ( iEntryType == CCalEntry::EEvent) + { + end += TTimeIntervalDays(1); + }*/ + + TTimeIntervalMinutes duration; + TInt err = end.MinutesFrom( start, duration ); + ASSERT( err == KErrNone ); + ASSERT( duration >= TTimeIntervalMinutes(0) ); + + if ( UseFloatingTimeL() ) + { + entryEnd.SetTimeLocalFloatingL( entryStart.TimeLocalL() + duration ); + } + else + { + entryEnd.SetTimeLocalL( entryStart.TimeLocalL() + duration ); + } + } + + // If EntryType is changed as Non-timed/Timed entry, along with StartTime(AND/OR)EndTime, + // Both Start/End time TimeMode should be changed accordingly. + if ( UseFloatingTimeL() ) + { + entryStart.SetTimeLocalFloatingL( entryStart.TimeLocalL() ); + entryEnd.SetTimeLocalFloatingL( entryEnd.TimeLocalL() ); + } + else + { + entryStart.SetTimeLocalL( entryStart.TimeLocalL() ); + entryEnd.SetTimeLocalL( entryEnd.TimeLocalL() ); + } + + // Write times to entry + iEntry.SetStartAndEndTimeL( entryStart, entryEnd ); + } + + TRACE_EXIT_POINT; + } + +//---------------------------------------------------------------------- +// CCalenEditorDataHandler::ResetOriginalDataL +// Reset original editor data +// (other items were commented in a header). +//---------------------------------------------------------------------- +void CCalenEditorDataHandler::ResetOriginalDataL() + { + TRACE_ENTRY_POINT; + + if(iOriginal) + { + delete iOriginal; + iOriginal=NULL; + } + iOriginal = CCalenEntryUtil::NewL( iEntry, iInstanceDateTime ); + + TRACE_EXIT_POINT; + } + +//---------------------------------------------------------------------- +// CCalenEditorDataHandler::EntryViewL +// Temp function to get to work +// (other items were commented in a header). +//---------------------------------------------------------------------- +CCalEntryView& CCalenEditorDataHandler::EntryViewL() + { + TRACE_ENTRY_POINT; + + CCalEntryView* entryView = iServices.EntryViewL(CurrentDbCollectionId()); + + TRACE_EXIT_POINT; + return *entryView; + } + +//---------------------------------------------------------------------- +// CCalenEditorDataHandler::InstanceViewL +// Temp function to get to work +// (other items were commented in a header). +//---------------------------------------------------------------------- +CCalInstanceView& CCalenEditorDataHandler::InstanceViewL() + { + TRACE_ENTRY_POINT; + + CCalInstanceView* instanceView = iServices.InstanceViewL(iCollectionIds); + + TRACE_EXIT_POINT; + return *instanceView; + } + +// ----------------------------------------------------------------------------- +// CCalenEditorDataHandler::GetPreviousInstanceForRepeatOtherL() +// Returns the time of the previous instance of a particular repeating entry +// (other items were commented in a header). +// ----------------------------------------------------------------------------- +// +TTime CCalenEditorDataHandler::GetPreviousInstanceForRepeatOtherL(CCalEntry& aEntry, const CalCommon::TCalTimeRange& timeRange) + { + RPointerArray allInstances; + CleanupResetAndDestroyPushL( allInstances ); + + TInt filter; + // Get the entry type to be filtered + switch(aEntry.EntryTypeL()) + { + case CCalEntry::EAppt: + filter = CalCommon::EIncludeAppts; + break; + case CCalEntry::ETodo: + filter = CalCommon::EIncludeCompletedTodos | CalCommon::EIncludeIncompletedTodos; + break; + case CCalEntry::EEvent: + filter = CalCommon::EIncludeEvents; + break; + case CCalEntry::EReminder: + filter = CalCommon::EIncludeReminder; + break; + case CCalEntry::EAnniv: + filter = CalCommon::EIncludeAnnivs; + break; + default: + filter = CalCommon::EIncludeAll; + break; + }; + + iServices.InstanceViewL()->FindInstanceL( allInstances, + (CalCommon::TCalViewFilterFlags)filter, + timeRange); + + TTime previousTime = Time::NullTTime(); + + for( TInt i = allInstances.Count() - 1; i >= 0; i-- ) + { + if( allInstances[i]->Entry().UidL() == aEntry.UidL() ) + { + TRACE_EXIT_POINT; + previousTime = allInstances[i]->Time().TimeLocalL(); + break; + } + } + + CleanupStack::PopAndDestroy( &allInstances ); + return previousTime; + } + +// ----------------------------------------------------------------------------- +// CCalenEditorDataHandler::GetNextInstanceForRepeatOtherL() +// Returns the time of the next instance of a particular repeating entry +// (other items were commented in a header). +// ----------------------------------------------------------------------------- +// +TTime CCalenEditorDataHandler::GetNextInstanceForRepeatOtherL( CCalEntry& aEntry, const CalCommon::TCalTimeRange& timeRange ) + { + RPointerArray allInstances; + CleanupResetAndDestroyPushL( allInstances ); + + TInt filter; + // Get the entry type to be filtered + switch( aEntry.EntryTypeL() ) + { + case CCalEntry::EAppt: + filter = CalCommon::EIncludeAppts; + break; + case CCalEntry::ETodo: + filter = CalCommon::EIncludeCompletedTodos | CalCommon::EIncludeIncompletedTodos; + break; + case CCalEntry::EEvent: + filter = CalCommon::EIncludeEvents; + break; + case CCalEntry::EReminder: + filter = CalCommon::EIncludeReminder; + break; + case CCalEntry::EAnniv: + filter = CalCommon::EIncludeAnnivs; + break; + default: + filter = CalCommon::EIncludeAll; + break; + }; + + iServices.InstanceViewL()->FindInstanceL( allInstances, + ( CalCommon::TCalViewFilterFlags )filter, + timeRange); + + TTime nextTime = Time::NullTTime(); + + TInt i( 0 ); + for( ; i < allInstances.Count(); i++ ) + { + if( allInstances[i]->Entry().UidL() == aEntry.UidL() ) + { + TRACE_EXIT_POINT; + nextTime = allInstances[i]->Time().TimeLocalL(); + break; + } + } + + CleanupStack::PopAndDestroy( &allInstances ); + return nextTime; + } + +// ----------------------------------------------------------------------------- +// CCalenEditorDataHandler::SetEntryType +// add attachments to the entry as ccalattachments +// ----------------------------------------------------------------------------- +// +void CCalenEditorDataHandler::SetEntryType( CCalEntry::TType aNewEntryType ) + { + iEntryType = aNewEntryType; + } + +// ----------------------------------------------------------------------------- +// CCalenEditorDataHandler::AddAttachementsToEntryL +// add attachments to the entry as ccalattachments +// ----------------------------------------------------------------------------- +// +void CCalenEditorDataHandler::AddAttachementsToEntryL() + { + TRACE_ENTRY_POINT; + + // Get the list of attachments from model and add them to entry + RPointerArray attachmentInfoList; + iServices.GetAttachmentData()->GetAttachmentListL( attachmentInfoList ); + TInt attachmentCount = iEntry.AttachmentCountL(); + TInt modelCount = attachmentInfoList.Count(); + + if( attachmentCount > 0 && modelCount == 0 ) + { + // This means that all the attachments in the entry have been deleted + // by the user. So remove all attchments from the entry + for( int index = 0 ; index < attachmentCount ; index++ ) + { + iEntry.DeleteAttachmentL( *iEntry.AttachmentL( 0 ) ); + } + + // No attachments need to be added at this point, we can simply return. + attachmentInfoList.Close(); + TRACE_EXIT_POINT; + return; + } + + // Delete all the attachmenst of the entry which are not present in the model + TInt currentIndex = 0; + for( TInt index=0; index < attachmentCount; index++ ) + { + CCalAttachment* attachment = iEntry.AttachmentL( currentIndex ); + TBool matchFound = EFalse; + for( int modelIndex = 0 ; modelIndex < modelCount ; modelIndex++ ) + { + CCalenAttachmentInfo* attachmentInfo = attachmentInfoList[ modelIndex ]; + if( attachmentInfo->StoreType() == + CCalenAttachmentInfo::ECalenAttachmentFetchedFromEntry ) + { + // We are only interested in those attachments which have been + // fetched from the entry because any newly added attachments + // will not yet be updated in the entry + RFile attachmentHandle; + CleanupClosePushL( attachmentHandle ); + attachment->FileAttachment()->FetchFileHandleL( attachmentHandle ); + + TFileName systemFileName; + attachmentHandle.FullName( systemFileName ); + + if( systemFileName == attachmentInfo->SystemFileName() ) + { + // There is an attachment in the model which matches the + // attachment in the entry. So do not delete it + matchFound = ETrue; + CleanupStack::PopAndDestroy( &attachmentHandle ); + break; + } + CleanupStack::PopAndDestroy( &attachmentHandle ); + } + } + if( !matchFound ) + { + // There is no matching attachment in the model. Delete it from entry + // This will compress the attachment array. Hence do not increment the currentIndex + iEntry.DeleteAttachmentL( *iEntry.AttachmentL( currentIndex ) ); + } + else + { + // Move to the next attachment + currentIndex++; + } + } + + // Now add the remaining newly added attachments to the entry + for( TInt index=0 ; index< modelCount ; index++ ) + { + CCalenAttachmentInfo* attachmentInfo = attachmentInfoList[ index ]; + if( attachmentInfo->StoreType() == CCalenAttachmentInfo::ECalenNewAttachment ) + { + TParsePtrC fileNameParser(attachmentInfo->FileName()); + RFile fileHandle; + CEikonEnv* eikonEnv = CEikonEnv::Static(); + RFs& fs = eikonEnv->FsSession(); + User::LeaveIfError(fs.ShareProtected()); + + TInt err = fileHandle.Open( fs, attachmentInfo->FileName(), EFileWrite ); + if( KErrNone != err ) + { + attachmentInfoList.Close(); + TRACE_EXIT_POINT; + return; + } + CleanupClosePushL(fileHandle); + + CCalAttachment* attachment = CCalAttachment::NewFileL(fileHandle); + CleanupStack::PopAndDestroy(&fileHandle); + + CleanupStack::PushL( attachment ); + // Sets the label for the attachment + attachment->SetLabelL( fileNameParser.NameAndExt() ); + // Sets mime type for the attachment + attachment->SetMimeTypeL( attachmentInfo->DataType().Des8() ); + attachment->SetAttribute(CCalAttachment::EExportInline); + iEntry.AddAttachmentL( *attachment ); + CleanupStack::Pop( attachment ); + } + } + attachmentInfoList.Close(); + + TRACE_EXIT_POINT; + } + + +// ----------------------------------------------------------------------------- +// CCalenEditorDataHandler::SetCalendarFieldEditedL +// Set if calendar is been edited or not with previous and current collection id +// ----------------------------------------------------------------------------- +// +void CCalenEditorDataHandler::SetCalendarFieldEditedL(TBool aEdited , + TCalCollectionId aPreviousDbColId, + TCalCollectionId aCurrentDbColId) + { + TRACE_ENTRY_POINT + iCalendarFieldEdited = aEdited; + iPreviousDbColId = aPreviousDbColId; + iCurrentDbColId = aCurrentDbColId; + TRACE_EXIT_POINT + } + +// ----------------------------------------------------------------------------- +// CCalenEditorDataHandler::PreviousDbCollectionId +// Get previous collection id +// ----------------------------------------------------------------------------- +// +TCalCollectionId CCalenEditorDataHandler::PreviousDbCollectionId() + { + TRACE_ENTRY_POINT + TRACE_EXIT_POINT + return iPreviousDbColId; + } + +// ----------------------------------------------------------------------------- +// CCalenEditorDataHandler::CurrentDbCollectionId +// Get current collection id +// ----------------------------------------------------------------------------- +// +TCalCollectionId CCalenEditorDataHandler::CurrentDbCollectionId() + { + TRACE_ENTRY_POINT + TRACE_EXIT_POINT + return iCurrentDbColId; + } + +// ----------------------------------------------------------------------------- +// CCalenEditorDataHandler::CalenInstanceId +// Returns the instance id. +// (other items were commented in a header). +// ----------------------------------------------------------------------------- +// +const TCalenInstanceId CCalenEditorDataHandler::CalenInstanceId() + { + TRACE_ENTRY_POINT; + + TRACE_EXIT_POINT; + + return iInstanceId; + } + +// End of file --Don't remove this.