diff -r d189ee25cf9d -r 3533d4323edc emailuis/emailui/src/FreestyleEmailUiMailListModel.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/emailuis/emailui/src/FreestyleEmailUiMailListModel.cpp Wed Sep 01 12:28:57 2010 +0100 @@ -0,0 +1,595 @@ +/* +* Copyright (c) 2007 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: FreestyleEmailUi double line list model and model item implementation. +* +*/ + + +// SYSTEM INCLUDES +// +#include "emailtrace.h" +#include "cfsmailmessage.h" +// + + +// INTERNAL INCLUDES +#include "FreestyleEmailUiAppui.h" +#include "FreestyleEmailUiMailListModel.h" +#include "FreestyleEmailUiUtilities.h" + + +// MODEL ITEM CONSTRUCTION +CFSEmailUiMailListModelItem* CFSEmailUiMailListModelItem::NewL( CFSMailMessage* aMessagePtr, + TModelItemType aModelItemtype ) + { + FUNC_LOG; + CFSEmailUiMailListModelItem* self = CFSEmailUiMailListModelItem::NewLC( aMessagePtr, aModelItemtype ); + CleanupStack::Pop(self); + return self; + } + +CFSEmailUiMailListModelItem* CFSEmailUiMailListModelItem::NewLC( CFSMailMessage* aMessagePtr, + TModelItemType aModelItemtype ) + { + FUNC_LOG; + CFSEmailUiMailListModelItem* self = new (ELeave) CFSEmailUiMailListModelItem( aMessagePtr, aModelItemtype ); + CleanupStack::PushL(self); + return self; + } + +CFSEmailUiMailListModelItem::CFSEmailUiMailListModelItem( CFSMailMessage* aMessagePtr, + TModelItemType aModelItemtype ) + :iMessagePtr( aMessagePtr ), + iModelItemType( aModelItemtype ), + iSeparatorText( 0 ), + iFsTreeListId( 0 ) + { + FUNC_LOG; + } + +CFSEmailUiMailListModelItem::~CFSEmailUiMailListModelItem() + { + FUNC_LOG; + delete iSeparatorText; + if ( iModelItemType == ETypeMailItem ) + { + delete iMessagePtr; + } + else + { + // In case of separator item, the message is not owned. This is just a pointer + // to the first message belonging under this separator. The ownership is on the + // next mail item. + iMessagePtr = NULL; + } + } + + +void CFSEmailUiMailListModelItem::SetSeparatorTextL( const TDesC& aSeparatorText ) + { + FUNC_LOG; + delete iSeparatorText; + iSeparatorText = NULL; + iSeparatorText = aSeparatorText.AllocL(); + } + + + + +//MODEL CLASS CONSTRUCTION +CFSEmailUiMailListModel* CFSEmailUiMailListModel::NewL( CFreestyleEmailUiAppUi* aAppUi, TBool aSearchList ) + { + FUNC_LOG; + CFSEmailUiMailListModel* self = CFSEmailUiMailListModel::NewLC( aAppUi, aSearchList ); + CleanupStack::Pop(self); + return self; + } + +CFSEmailUiMailListModel* CFSEmailUiMailListModel::NewLC( CFreestyleEmailUiAppUi* aAppUi, TBool aSearchList ) + { + FUNC_LOG; + CFSEmailUiMailListModel* self = new (ELeave) CFSEmailUiMailListModel( aAppUi ); + CleanupStack::PushL(self); + self->ConstructL( aSearchList ); + return self; + } + +void CFSEmailUiMailListModel::ConstructL( TBool aSearchList ) + { + FUNC_LOG; + // set default sort criteria + iSortCriteria.iField = EFSMailSortByDate; + iSortCriteria.iOrder = EFSMailDescending; + + if ( !aSearchList ) + { + // Set TLS pointer to point ourselves. This is needed to access our + // data from static (find/sort) functions. + TInt err = UserSvr::DllSetTls( KTlsHandleMailListModel, this ); + } + } + +CFSEmailUiMailListModel::CFSEmailUiMailListModel( CFreestyleEmailUiAppUi* aAppUi ) + : iAppUi( aAppUi ) + { + FUNC_LOG; + } + +CFSEmailUiMailListModel::~CFSEmailUiMailListModel() + { + FUNC_LOG; + iItems.ResetAndDestroy(); + } + +void CFSEmailUiMailListModel::AppendL(MFSListModelItem* aItem) + { + FUNC_LOG; + TInt err; + CFSEmailUiMailListModelItem* newItem = static_cast< CFSEmailUiMailListModelItem* >(aItem); + err = iItems.Append( newItem ); + User::LeaveIfError(err); + + // If mail item was added as first child of a separator, the message pointer + // of the separator has to be reset. + TInt insertionPoint = iItems.Count()-1; + if ( insertionPoint > 0 && + newItem->ModelItemType() == ETypeMailItem && + iItems[insertionPoint-1]->ModelItemType() == ETypeSeparator ) + { + iItems[insertionPoint-1]->SetMessagePtr( &newItem->MessagePtr() ); + } + } + +void CFSEmailUiMailListModel::InsertL( MFSListModelItem* aItem, TInt aIndex ) + { + FUNC_LOG; + TInt err; + CFSEmailUiMailListModelItem* newItem = static_cast< CFSEmailUiMailListModelItem* >(aItem); + err = iItems.Insert( newItem, aIndex); + User::LeaveIfError(err); + + // If mail item was added as first child of a separator, the message pointer + // of the separator has to be reset. + if ( aIndex > 0 && + newItem->ModelItemType() == ETypeMailItem && + iItems[aIndex-1]->ModelItemType() == ETypeSeparator ) + { + iItems[aIndex-1]->SetMessagePtr( &newItem->MessagePtr() ); + } + } + +void CFSEmailUiMailListModel::RemoveAndDestroy(TInt aIndex) + { + FUNC_LOG; + delete Item(aIndex); + iItems.Remove(aIndex); + iItems.Compress(); + + // If the removed item was the first child of a preceeding node item, then the message + // pointer in the node item has to be updated. + if ( aIndex > 0 && iItems[aIndex-1]->ModelItemType() == ETypeSeparator ) + { + if ( aIndex < iItems.Count() && iItems[aIndex]->ModelItemType() == ETypeMailItem ) + { + iItems[aIndex-1]->SetMessagePtr( &iItems[aIndex]->MessagePtr() ); + } + else + { + iItems[aIndex-1]->SetMessagePtr( NULL ); + } + } + } + +MFSListModelItem* CFSEmailUiMailListModel::Item( TInt aIndex ) + { + FUNC_LOG; + TInt numInList = iItems.Count(); + if ( aIndex < 0 || aIndex >= numInList ) + { + return NULL; + } + return iItems[aIndex]; + } + +const MFSListModelItem* CFSEmailUiMailListModel::Item( TInt aIndex ) const + { + FUNC_LOG; + TInt numInList = iItems.Count(); + if ( aIndex < 0 || aIndex >= numInList ) + { + return NULL; + } + return iItems[aIndex]; + } + +TInt CFSEmailUiMailListModel::Count() const + { + FUNC_LOG; + return iItems.Count(); + } + +CFreestyleEmailUiAppUi* CFSEmailUiMailListModel::AppUi() + { + FUNC_LOG; + return iAppUi; + } + +void CFSEmailUiMailListModel::SetSortCriteria( TFSMailSortCriteria aSortCriteria ) + { + FUNC_LOG; + iSortCriteria = aSortCriteria; + } + +TFSMailSortCriteria CFSEmailUiMailListModel::SortCriteria() + { + FUNC_LOG; + return iSortCriteria; + } + +void CFSEmailUiMailListModel::SortL() + { + FUNC_LOG; + TLinearOrder orderFunc( CompareModelItemsL ); + iItems.Sort( orderFunc ); + } + +void CFSEmailUiMailListModel::ReplaceMessagePtr( TInt aIndex, CFSMailMessage* aNewPtr ) + { + FUNC_LOG; + CFSEmailUiMailListModelItem* item = + static_cast< CFSEmailUiMailListModelItem* >( Item(aIndex) ); + + if ( item ) // Safety + { + // Mail type items own the message. Delete the existing message first. + if ( item->ModelItemType() == ETypeMailItem ) + { + delete &item->MessagePtr(); + } + item->SetMessagePtr( aNewPtr ); + + // If the item was the first child of a node, then also message pointer in the parent + // node needs to be updated + if ( aIndex > 0 && + item->ModelItemType() == ETypeMailItem && + iItems[aIndex-1]->ModelItemType() == ETypeSeparator ) + { + iItems[aIndex-1]->SetMessagePtr( aNewPtr ); + } + } + } + +TInt CFSEmailUiMailListModel::GetInsertionPointL( const CFSEmailUiMailListModelItem& aItemToInsert ) const + { + FUNC_LOG; + TInt index; + TLinearOrder orderFunc( CompareModelItemsL ); + TInt ret = iItems.SpecificFindInOrder( &aItemToInsert, index, orderFunc, EArrayFindMode_First ); + return index; + } + +TInt CFSEmailUiMailListModel::GetInsertionPointL( const CFSEmailUiMailListModelItem& aItemToInsert, + TInt& aChildIdx, + TInt& aParentNodeIdx ) const + { + FUNC_LOG; + TInt index = GetInsertionPointL( aItemToInsert ); + + // Find out the parent node + aChildIdx = KErrNotFound; + aParentNodeIdx = KErrNotFound; + if ( index > 0 ) + { + CFSEmailUiMailListModelItem* preceedingItem = iItems[index-1]; + if ( MessagesBelongUnderSameSeparatorL( aItemToInsert.MessagePtr(), + preceedingItem->MessagePtr(), + iSortCriteria.iField ) ) + { + // The new item belongs under the same node as the previous item + // (or the previous item is a node under which the item belongs). + // Find out the previous node item. + aParentNodeIdx = index-1; + while ( aParentNodeIdx >= 0 && iItems[aParentNodeIdx]->ModelItemType() != ETypeSeparator ) + { + aParentNodeIdx--; + } + if ( aParentNodeIdx >= 0 ) + { + aChildIdx = index - aParentNodeIdx - 1; + } + } + } + + return index; + } + +TInt CFSEmailUiMailListModel::CompareModelItemsL( const CFSEmailUiMailListModelItem& aItem1, + const CFSEmailUiMailListModelItem& aItem2 ) + { + FUNC_LOG; + // Retrieve the current sort criteria from model instance in TLS + CFSEmailUiMailListModel* self = + static_cast( UserSvr::DllTls(KTlsHandleMailListModel) ); + TFSMailSortCriteria sortCriteria = self->SortCriteria(); + + TInt value = CompareMessagesL( aItem1.MessagePtr(), + aItem2.MessagePtr(), + sortCriteria.iField ); + + // The logic of the CompareMessagesL matches ascending order. In descending mode it must + // be inverted. + if ( sortCriteria.iOrder == EFSMailDescending ) + { + value = -value; + } + + // If items are equal with the primary sorting criterion and this primary criterion + // is not date sort, then descending date order is used as secondary criterion. + if ( !value && sortCriteria.iField != EFSMailSortByDate ) + { + value = -CompareMessagesL( aItem1.MessagePtr(), + aItem2.MessagePtr(), + EFSMailSortByDate ); + } + + // Some magic is needed in case one of the items (but not both) is a separator + if ( aItem1.ModelItemType() == ETypeSeparator && aItem2.ModelItemType() == ETypeMailItem ) + { + if ( !value ) + { // In case an item is equal to the first item under the node, it's greater than the node + value = -1; + } + else if ( value > 0 ) + { + // In case an item is smaller than the first item under the node, it may still be greater + // than the node (i.e. when the item belongs under this same node). + if ( MessagesBelongUnderSameSeparatorL( aItem1.MessagePtr(), + aItem2.MessagePtr(), + sortCriteria.iField ) ) + { + value = -1; + } + } + } + else if ( aItem1.ModelItemType() == ETypeMailItem && aItem2.ModelItemType() == ETypeSeparator ) + { + if ( !value ) + { // In case an item is equal to the first item under the node, it's greater than the node + value = 1; + } + else if ( value < 0 ) + { + // In case an item is smaller than the first item under the node, it may still be greater + // than the node (i.e. when the item belongs under this same node). + if ( MessagesBelongUnderSameSeparatorL( aItem1.MessagePtr(), + aItem2.MessagePtr(), + sortCriteria.iField ) ) + { + value = 1; + } + } + } + + return value; + } + +// --------------------------------------------------------------------------- +// CompareMessagesL +// Compares given messages using the given sorting mode. Return positive +// value if aMessage1 is greater, negative value if aMessage1 is smaller, and +// 0 if the messages are equal. +// --------------------------------------------------------------------------- +// +TInt CFSEmailUiMailListModel::CompareMessagesL( const CFSMailMessage& aMessage1, + const CFSMailMessage& aMessage2, + TFSMailSortField aSortField ) + { + FUNC_LOG; + // For now, cast away the constness from the argument because CFSMailMessage does + // not declare all its logically constant functions as const. This is dirty and + // should be fixed in the CFSMailMessage interface. + CFSMailMessage& message1 = const_cast( aMessage1 ); + CFSMailMessage& message2 = const_cast( aMessage2 ); + + TInt retVal = 0; + + switch ( aSortField ) + { + case EFSMailSortByFlagStatus: + { + TInt compVal1 = 0; + if ( message1.IsFlagSet(EFSMsgFlag_FollowUpComplete) ) { compVal1 = 1; } + else if ( message1.IsFlagSet(EFSMsgFlag_FollowUp) ) { compVal1 = 2; } + + TInt compVal2 = 0; + if ( message2.IsFlagSet(EFSMsgFlag_FollowUpComplete) ) { compVal2 = 1; } + else if ( message2.IsFlagSet(EFSMsgFlag_FollowUp) ) { compVal2 = 2; } + + retVal = compVal1 - compVal2; + } + break; + case EFSMailSortByPriority: + { + TInt compVal1 = 1; + if ( message1.IsFlagSet(EFSMsgFlag_Low) ) { compVal1 = 0; } + else if ( message1.IsFlagSet(EFSMsgFlag_Important) ) { compVal1 = 2; } + + TInt compVal2 = 1; + if ( message2.IsFlagSet(EFSMsgFlag_Low) ) { compVal2 = 0; } + else if ( message2.IsFlagSet(EFSMsgFlag_Important) ) { compVal2 = 2; } + + retVal = compVal1 - compVal2; + } + break; + case EFSMailSortByRecipient: + { + CFSMailAddress* toAddress1(NULL); + CFSMailAddress* toAddress2(NULL); + + RPointerArray& toArray1 = message1.GetToRecipients(); + RPointerArray& toArray2 = message2.GetToRecipients(); + if ( toArray1.Count() ) + { + toAddress1 = toArray1[0]; + } + if ( toArray2.Count() ) + { + toAddress2 = toArray2[0]; + } + retVal = TFsEmailUiUtility::CompareMailAddressesL( toAddress1, toAddress2 ); + } + break; + case EFSMailSortBySender: + { + CFSMailAddress* fromAddress1 = message1.GetSender(); + CFSMailAddress* fromAddress2 = message2.GetSender(); + retVal = TFsEmailUiUtility::CompareMailAddressesL( fromAddress1, fromAddress2 ); + } + break; + case EFSMailSortByAttachment: + { + TInt compVal1 = 0; + if ( message1.IsFlagSet(EFSMsgFlag_Attachments) ) { compVal1 = 1; } + + TInt compVal2 = 0; + if ( message2.IsFlagSet(EFSMsgFlag_Attachments) ) { compVal2 = 1; } + + retVal = compVal1 - compVal2; + } + break; + case EFSMailSortByUnread: + { + TInt compVal1 = 0; + if ( message1.IsFlagSet(EFSMsgFlag_Read) ) { compVal1 = 1; } + + TInt compVal2 = 0; + if ( message2.IsFlagSet(EFSMsgFlag_Read) ) { compVal2 = 1; } + + retVal = compVal1 - compVal2; + } + break; + case EFSMailSortBySubject: + { + retVal = TFsEmailUiUtility::CompareMailSubjectsL( &message1, &message2 ); + } + break; + case EFSMailSortByDate: + { + TTime time1 = message1.GetDate(); + TTime time2 = message2.GetDate(); + TTimeIntervalSeconds diff; + TInt err = time1.SecondsFrom( time2, diff ); + if ( !err ) + { + // Normal case, no overflow + retVal = diff.Int(); + } + else + { + // Overflow happens if the difference in time stamps is more than roughly 68 years. + // This is a very unlikely special case but handle it properly just in case. + retVal = ( time1 > time2 ? 1 : -1 ); + } + } + break; + default: + { + retVal = 0; + } + break; + } + + return retVal; + } + +TBool CFSEmailUiMailListModel::MessagesBelongUnderSameSeparatorL( + const CFSMailMessage& aMessage1, + const CFSMailMessage& aMessage2, + TFSMailSortField aSortField ) + { + FUNC_LOG; + TBool retVal = EFalse; + + switch ( aSortField ) + { + case EFSMailSortByFlagStatus: + case EFSMailSortByPriority: + case EFSMailSortByRecipient: + case EFSMailSortBySender: + case EFSMailSortByAttachment: + case EFSMailSortByUnread: + case EFSMailSortBySubject: + { + retVal = ( !CompareMessagesL(aMessage1, aMessage2, aSortField) ); + } + break; + case EFSMailSortByDate: + { + TTime time1InHomeTime = aMessage1.GetDate(); + TTime time2InHomeTime = aMessage2.GetDate(); + TLocale currentLocaleSettings; + time1InHomeTime += currentLocaleSettings.UniversalTimeOffset(); + time1InHomeTime += currentLocaleSettings.QueryHomeHasDaylightSavingOn() + ? TTimeIntervalHours(1) : TTimeIntervalHours(0); + time2InHomeTime += currentLocaleSettings.UniversalTimeOffset(); + time2InHomeTime += currentLocaleSettings.QueryHomeHasDaylightSavingOn() + ? TTimeIntervalHours(1) : TTimeIntervalHours(0); + // Check that the day of the year and the year number are the same + TInt day1 = time1InHomeTime.DayNoInYear(); + TInt day2 = time2InHomeTime.DayNoInYear(); + TTimeIntervalYears year1 = time1InHomeTime.YearsFrom(0); + TTimeIntervalYears year2 = time2InHomeTime.YearsFrom(0); + retVal = ( day1 == day2 && year1 == year2 ); + } + break; + default: + { + retVal = EFalse; + } + break; + } + + return retVal; + } + +/** + * CFSEmailUiMailListModel::Reset + */ +void CFSEmailUiMailListModel::Reset() + { + FUNC_LOG; + iItems.ResetAndDestroy(); + iSortCriteria.iField = EFSMailSortByDate; + iSortCriteria.iOrder = EFSMailDescending; + } + +void CFSEmailUiMailListModel::GetItemIdsUnderNodeL( const TFsTreeItemId aNodeId, + RFsTreeItemIdList& aMessageIds ) const + { + FUNC_LOG; + aMessageIds.Reset(); + const TInt count(iItems.Count()); + for (TInt index = 0; index < count; index++) + { + if (iItems[index]->CorrespondingListId() == aNodeId) + { + // Found the node Id, now get all the items under it + while (++index < count && iItems[index]->ModelItemType() == ETypeMailItem) + { + aMessageIds.AppendL(iItems[index]->CorrespondingListId()); + } + break; + } + } + }