emailuis/nmailuiengine/src/nmmessagelistmodel.cpp
changeset 18 578830873419
child 20 ecc8def7944a
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/emailuis/nmailuiengine/src/nmmessagelistmodel.cpp	Fri Apr 16 14:51:52 2010 +0300
@@ -0,0 +1,549 @@
+/*
+* Copyright (c) 2009 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:
+*
+*/
+
+#include "nmuiengineheaders.h"
+
+
+/*!
+    \class NmMessageListModel
+    \brief The NmMessageListModel class represents data model for mailbox list.
+    @alpha
+
+    The NmMessageListModel class uses NmMessageEnvelope class to represent data returned in its'
+    data method to get all information needed for one list row for a widget by calling the method
+    once.
+*/
+
+/*!
+	Constructor
+ */
+NmMessageListModel::NmMessageListModel(NmDataManager &dataManager, QObject *parent)
+:QStandardItemModel(parent),
+mDataManager(dataManager),
+mDividersActive(false),
+mParentPtr(NULL)
+{
+    // Check for setting whether dividers are active
+    // mDividersActive = ...
+    // update also the test cases
+}
+
+/*!
+	Destructor
+ */
+NmMessageListModel::~NmMessageListModel()
+{
+    clear();
+}
+
+/*!
+    Returns data specified by \a index. Only Qt::DisplayRole is supported in \a role.
+    The refresh method must have been called before this method can return any real data.
+ */
+
+QVariant NmMessageListModel::data(const QModelIndex &index, int role) const
+{
+    QVariant qVariant;
+    if (index.isValid() && Qt::DisplayRole == role) {
+    	NmMessageListModelItem *item = static_cast<NmMessageListModelItem*>(itemFromIndex(index));
+        qVariant = QVariant::fromValue(item);
+    }
+    return qVariant;
+}
+
+/*!
+    This refreshes the data of the model.
+ */
+void NmMessageListModel::refresh(
+                    const NmId mailboxId, 
+                    const NmId folderId,
+                    const QList<NmMessageEnvelope*> &messageEnvelopeList)
+{
+    // Store current mailbox and folder id
+    mCurrentMailboxId = mailboxId;
+    mCurrentFolderId = folderId;
+    // clear the model
+    clear();
+    // Add items
+    NmMessageEnvelope* insertedMessage(NULL);
+    int parentCount(0);
+    int childCount(0);
+    for (int i(0); i < messageEnvelopeList.count(); i++) {
+        NmMessageEnvelope* nextMessage = messageEnvelopeList[i];
+        if (mDividersActive && !messagesBelongUnderSameDivider(
+        		insertedMessage, nextMessage)) {
+            insertDividerIntoModel(nextMessage, parentCount);
+            parentCount++;
+            childCount = 0;
+        }
+        insertMessageIntoModel(nextMessage, childCount, false);
+        insertedMessage = nextMessage;
+        childCount++;
+    }
+    //NMLOG(QString("nmailuiengine: model row count = %1").arg(rowCount()));
+}
+
+/*!
+    insertDividerIntoModel. Function inserts divider into model.
+ */
+void NmMessageListModel::insertDividerIntoModel(
+    NmMessageEnvelope *messageForDivider,
+    int parentRow)
+{
+    mParentPtr = createTitleDividerItem(messageForDivider);
+    insertRow(parentRow,mParentPtr);
+    mParentPtr->callEmitDataChanged();
+}
+
+/*!
+    Function inserts message into model. Message can be inserted
+    either to root or to parent. If parent is zero, item is added into root.
+ */
+void NmMessageListModel::insertMessageIntoModel(
+		NmMessageEnvelope *messageEnvelope, int childRow, bool emitSignal)
+{
+    NmMessageListModelItem *mailItem = createMessageItem(messageEnvelope);
+    if (mParentPtr) {
+        // Add under parent divider
+        mParentPtr->insertRow(childRow, mailItem);
+    }
+    else {
+        // No dividers, add under root item
+        insertRow(childRow,mailItem);
+    }
+    if (emitSignal) {
+        mailItem->callEmitDataChanged();
+    }
+}
+
+/*!
+    Function checks whether the messages can be drawn under same title divider
+    Check is done depending of the current sorting criteria
+ */
+bool NmMessageListModel::messagesBelongUnderSameDivider(
+    const NmMessageEnvelope *message1,
+    const NmMessageEnvelope *message2) const
+{
+    bool retVal(false);
+    // First check pointer validity
+    if (message1 && message2) {
+        // Switch here for other sort modes 
+        // Date based comparison
+        QDateTime sentTime1 = message1->sentTime().toLocalTime();
+        QDateTime sentTime2 = message2->sentTime().toLocalTime();
+        if ( sentTime1.date() == sentTime2.date()) {
+            retVal = true;
+        }
+    }
+    return retVal;
+}
+
+
+/*!
+    Function handles message events
+ */
+void NmMessageListModel::handleMessageEvent(
+    NmMessageEvent event,
+    const NmId &folderId,
+    const QList<NmId> &messageIds)
+{
+    NmId mailboxId = mCurrentMailboxId;
+
+    if (folderId == 0) {
+        // const cast is used here because also the input parameter has to be changed
+        const_cast<NmId&>(folderId) = mDataManager.getStandardFolderId(mailboxId, NmFolderInbox);
+        NmUiStartParam *startParam = new NmUiStartParam(NmUiViewMessageList, mailboxId, folderId);
+        emit setNewParam(startParam);
+    }
+    if (mCurrentFolderId == 0) {
+        // Folder id was not known at time mailbox opened
+        // and we know subscription is valid only for current 
+        // mailbox, because of events.
+        mCurrentFolderId = folderId; 
+    }
+    // Handle events if they concern currently displayed folder
+    if (mCurrentFolderId == folderId) {
+        NMLOG(QString("nmailuiengine: handleMessageEvent"));
+        if (NmMessageChanged == event) {
+            for (int a(0); a < messageIds.count(); a++) {
+                updateMessageEnvelope(mailboxId, folderId,messageIds[a]);
+            }
+        }
+        else if (NmMessageCreated == event) {
+            for (int a(0); a < messageIds.count(); a++) {
+                if(!itemFromModel(messageIds[a])) {
+                    insertNewMessageIntoModel(mailboxId, folderId, messageIds[a]);
+                }
+            }
+        } else {
+            for (int a(0); a < messageIds.count(); a++) {
+                removeMessageFromModel(messageIds[a]);
+            }
+        }
+    }
+}
+
+/*!
+    Function inserts new message into correct position to model.
+    A new title divider is created if it is needed.
+ */
+void NmMessageListModel::insertNewMessageIntoModel(
+    const NmId &mailboxId,
+    const NmId &folderId,
+    const NmId &msgId)
+{
+    NMLOG(QString("NmMessageListModel::insertNewMessageIntoModel"));
+    // envelope ownership is here
+    NmMessageEnvelope *newMsgEnve = mDataManager.envelopeById(mailboxId, folderId, msgId);
+    if (newMsgEnve) {
+        int rowIndex(getInsertionIndex(*newMsgEnve));
+        if (!mDividersActive) {
+            if (rowIndex > -1){
+                insertMessageIntoModel(newMsgEnve, rowIndex, true);
+            } else {
+                NmMessageListModelItem *mailItem = createMessageItem(newMsgEnve);
+                appendRow(mailItem);
+                mailItem->callEmitDataChanged();
+            }
+        } else {
+            // model contain items, message goes to the middle of the list
+            if (rowIndex > 0) {
+                int dividerIndex(0);
+                QList<QStandardItem*> items = findItems("*", Qt::MatchWildcard | Qt::MatchRecursive);
+                QModelIndex index = items[rowIndex]->index();
+                NmMessageListModelItem *item = static_cast<NmMessageListModelItem*>(itemFromIndex(index));
+                NmMessageEnvelope *replacedEnve = item->envelopePtr();
+                if (messagesBelongUnderSameDivider(newMsgEnve,replacedEnve)) {
+                    // find the index of the divider and insert message as child object
+                    // dividerIndex is not actually used here but function sets
+                    // the correct parent pointer
+                    dividerIndex = dividerInsertionIndex(rowIndex);
+                    insertMessageIntoModel(newMsgEnve, 0, true);
+                } else {
+                    // create new divider and find the correct index for it
+                    dividerIndex = dividerInsertionIndex(rowIndex);
+                    insertDividerIntoModel(newMsgEnve, dividerIndex);
+                    // Insert message as a first child under new divider
+                    insertMessageIntoModel(newMsgEnve, 0, true);
+                }
+                items.clear();
+            }
+            // first mail item in the model,
+            // insert new divider to index 0 and new message as a child.
+            else if (0 == rowIndex) {
+                insertDividerIntoModel(newMsgEnve, rowIndex);
+                insertMessageIntoModel(newMsgEnve, 0, true);
+            }
+            // this will go to the last item of the list and create new title divider
+            else {
+                mParentPtr = createTitleDividerItem(newMsgEnve);
+                appendRow(mParentPtr);
+                mParentPtr->callEmitDataChanged();
+                NmMessageListModelItem *mailItem = createMessageItem(newMsgEnve);
+                // Add under parent divider
+                mParentPtr->appendRow(mailItem);
+                mailItem->callEmitDataChanged();
+            }
+        }
+    }
+    delete newMsgEnve;
+}
+
+/*!
+    Function check model index in which the new message should be inserted
+    with the currently active sort mode.
+ */
+int NmMessageListModel::getInsertionIndex(
+    const NmMessageEnvelope &envelope) const
+{
+    // NMLOG(QString("nmailuiengine: getInsertionIndex"));
+    int ret(NmNotFoundError);
+    
+    // Date+descending sort mode based comparison.
+    // Go through model and compare sent times with QDateTime >= comparison operation.
+    QList<QStandardItem*> items = findItems("*", Qt::MatchWildcard | Qt::MatchRecursive);
+    int count(items.count());
+    bool found(false);
+    int i(0);
+    while (i < count && !found) {
+        QModelIndex index = items[i]->index();
+        NmMessageListModelItem *item = static_cast<NmMessageListModelItem*>(itemFromIndex(index));
+        if (NmMessageListModelItem::NmMessageItemMessage == item->itemType()) {
+        	found = envelope.sentTime() >= item->envelope().sentTime();
+        	if (found) {
+                ret = i;
+        	}
+        }
+        i++;
+    }
+    if (0 == count) {
+        ret = NmNoError;
+    }
+    items.clear();
+    return ret;
+}
+
+/*!
+    Function finds preceding title divider index and sets the
+    mParentPtr variable.
+ */
+int NmMessageListModel::dividerInsertionIndex(int messageIndex)
+{
+    bool found(false);
+    int ret(NmNoError);
+    QModelIndex index;
+    QList<QStandardItem*> items = findItems("*", Qt::MatchWildcard | Qt::MatchRecursive);
+    for (int i = messageIndex-1; i >= 0 && !found; i--) {
+        index = items[i]->index();
+        NmMessageListModelItem *item = static_cast<NmMessageListModelItem*>(itemFromIndex(index));
+        if(item->itemType() == NmMessageListModelItem::NmMessageItemTitleDivider) {
+            found = true;
+            mParentPtr = static_cast<NmMessageListModelItem*>(itemFromIndex(index));
+            ret = index.row();
+        }
+    }
+    items.clear();
+    return ret;
+}
+
+/*!
+    Create title divider item
+ */
+NmMessageListModelItem *NmMessageListModel::createTitleDividerItem(
+		NmMessageEnvelope *messageForDivider)
+{
+    NmMessageListModelItem *item = new NmMessageListModelItem();
+    item->setItemType(NmMessageListModelItem::NmMessageItemTitleDivider);
+
+    QDate sentLocalDate = messageForDivider->sentTime().toLocalTime().date();
+    QDate currentdate = QDate::currentDate();
+
+    if (sentLocalDate == currentdate) {
+        // NOTE: when dividers are taken in to use hbTrId macro should used here,
+        // to be clarified how to access ts-file (located under ui component)
+        item->setTitleDivider(tr("Today", "txt_nmailuiengine_divider_today"));
+        item->setText(tr("Today", "txt_nmailuiengine_divider_today"));
+    }
+    else {
+        QLocale locale;
+        QString divider = locale.toString(sentLocalDate, QLocale::LongFormat);
+        item->setTitleDivider(divider);
+        item->setText(divider);
+        }
+    item->setEnvelope(*messageForDivider);
+    item->setData(Hb::ParentItem, Hb::ItemTypeRole);
+    return item;
+}
+
+/*!
+    Create message item
+ */
+NmMessageListModelItem *NmMessageListModel::createMessageItem(
+		NmMessageEnvelope *envelope)
+{
+
+    NmMessageListModelItem *mailItem = new NmMessageListModelItem();
+    mailItem->setEnvelope(*envelope);
+    mailItem->setItemType(NmMessageListModelItem::NmMessageItemMessage);
+    mailItem->setData(Hb::StandardItem, Hb::ItemTypeRole);
+    return mailItem;
+}
+
+/*!
+    Returns divider state
+ */
+bool NmMessageListModel::dividersActive()
+{
+    return mDividersActive;
+}
+
+/*!
+    Set divider state
+ */
+void NmMessageListModel::setDividers(bool active)
+{
+    mDividersActive = active;
+}
+
+/*!
+   Change item property if differs
+ */
+void NmMessageListModel::setEnvelopeProperties(
+    NmEnvelopeProperties property,
+    const QList<NmId> &messageIds)
+{
+    for (int i(0); i < messageIds.count(); i++) {
+        updateEnvelope(property, messageIds[i]);
+    }
+}
+
+/*!
+   Returns the id of the current mailbox
+ */
+NmId NmMessageListModel::currentMailboxId()
+{
+    return mCurrentMailboxId;
+}
+
+/*!
+   Remove message from model if message exists in model
+ */
+void NmMessageListModel::removeMessageFromModel(const NmId &msgId)
+{
+    QList<QStandardItem*> items = findItems("*", Qt::MatchWildcard | Qt::MatchRecursive);
+    int count(items.count());
+    bool found(false);
+    int i(0);
+    while (!found && i < count) {
+        QModelIndex index = items[i]->index();
+        NmMessageListModelItem *item = static_cast<NmMessageListModelItem*>(itemFromIndex(index));
+        if (NmMessageListModelItem::NmMessageItemMessage == item->itemType()
+                && msgId == item->envelope().id()) {
+            found = true;
+            // dividers are not active, just remove message
+            if (!mDividersActive) {
+                removeItem(index.row(), *item);
+            } else {
+                // check the type from previous item
+                QModelIndex prevIndex = items[i-1]->index();
+                NmMessageListModelItem* previous = static_cast<NmMessageListModelItem*>(itemFromIndex(prevIndex));
+                if (NmMessageListModelItem::NmMessageItemTitleDivider == previous->itemType()) {
+                    // previous item is title divider
+                    if (i < (count-1)) {
+                        // if this is not last item in the list, check next item
+                        QModelIndex nextIndex = items[i+1]->index();
+                        NmMessageListModelItem* next = static_cast<NmMessageListModelItem*>(itemFromIndex(nextIndex));
+                        if (NmMessageListModelItem::NmMessageItemMessage == next->itemType()) {
+                            // next item is message, divider should not be removed
+                            removeItem(index.row(), *item);
+                        } else {
+                            // next item is another divider, remove also divider
+                            removeItem(index.row(), *item);
+                            removeItem(prevIndex.row(), *previous);
+                        }
+                    } else {
+                        // this is the last message in list, remove also divider
+                        removeItem(index.row(), *item);
+                        removeItem(prevIndex.row(), *previous);
+                    }
+                }  else {
+                    // previous item is message, title divider should not be deleted
+                    removeItem(index.row(), *item);
+                }
+            }
+        }
+        i++;
+    }
+    items.clear();
+}
+
+/*!
+   Helper function to remove row
+ */
+void NmMessageListModel::removeItem(int row, NmMessageListModelItem &item)
+{
+    QStandardItem *parent = item.parent();
+    removeRow(row, indexFromItem(parent));
+}
+
+/*!
+   Search item from model
+ */
+NmMessageListModelItem *NmMessageListModel::itemFromModel(const NmId &messageId)
+{
+    QList<QStandardItem*> items = findItems("*", Qt::MatchWildcard | Qt::MatchRecursive);
+    int count(items.count());
+    bool found(false);
+    int i(0);
+    NmMessageListModelItem *ret(NULL);
+    while (i < count && !found) {
+        NmMessageListModelItem *item =
+            static_cast<NmMessageListModelItem*>(itemFromIndex(items[i]->index()));
+        if (NmMessageListModelItem::NmMessageItemMessage == item->itemType()
+            && messageId == item->envelope().id()) {
+            found = true;
+            ret = item;
+        }
+        i++;
+    }
+    return ret;
+}
+
+/*!
+   Helper function for envelope comparison
+ */
+bool NmMessageListModel::changed(const NmMessageEnvelope &first, const NmMessageEnvelope &second)
+{
+    return first != second;
+}
+
+/*!
+   Updates envelope if something is changed
+ */
+void NmMessageListModel::updateMessageEnvelope(const NmId &mailboxId,
+        const NmId &folderId,
+        const NmId &msgId)
+{
+    NmMessageListModelItem *item = itemFromModel(msgId);
+    // envelope ownership is here
+    NmMessageEnvelope *newEnvelope = mDataManager.envelopeById(mailboxId, folderId, msgId);
+    if (item && newEnvelope) {
+        if (changed(*item->envelopePtr(), *newEnvelope)) {
+            // function takes envelope ownership 
+            item->setEnvelope(newEnvelope);
+        } else {
+            delete newEnvelope;
+            newEnvelope = NULL;
+        }
+    }
+}
+
+/*!
+   Update envelope property
+ */
+void NmMessageListModel::updateEnvelope(NmEnvelopeProperties property, const NmId &msgId)
+{
+    NmMessageListModelItem *item = itemFromModel(msgId);
+    if (item) {
+        bool changed(false);
+        NmMessageEnvelope *envelope = item->envelopePtr();
+        switch (property)
+        {
+        case MarkAsRead:
+        {
+            if (!envelope->isRead()) {
+                envelope->setRead(true);
+                changed = true;
+            }
+            break;
+        }
+        case MarkAsUnread:
+        {
+            if (envelope->isRead()) {
+                envelope->setRead(false);
+                changed = true;
+            }
+            break;
+        }
+        default:
+            break;
+        }
+        if (changed) {
+            item->callEmitDataChanged();
+        }
+    }
+}