emailuis/nmailui/src/nmviewerview.cpp
branchRCL_3
changeset 63 d189ee25cf9d
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/emailuis/nmailui/src/nmviewerview.cpp	Tue Aug 31 15:04:17 2010 +0300
@@ -0,0 +1,1054 @@
+/*
+ * Copyright (c) 2009-2010 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: Mail viewer implementation.
+ *
+ */
+
+#include "nmuiheaders.h"
+
+static const char *NMUI_MESSAGE_VIEWER_XML = ":/docml/nmmailviewer.docml";
+static const char *NMUI_MESSAGE_VIEWER_VIEW = "nmailViewerView";
+static const char *NMUI_MESSAGE_VIEWER_CONTENT = "content";
+static const char *NMUI_MESSAGE_VIEWER_SCROLL_AREA = "viewerScrollArea";
+static const char *NMUI_MESSAGE_VIEWER_SCROLL_AREA_CONTENTS = "viewerScrollAreaContents";
+static const char *NMUI_MESSAGE_VIEWER_HEADER = "viewerHeader";
+static const char *NMUI_MESSAGE_VIEWER_ATTALIST = "viewerAttaList";
+static const char *NMUI_MESSAGE_VIEWER_SCROLL_WEB_VIEW = "viewerWebView";
+static const int NmOrientationTimer = 100;
+static const int NmHeaderMargin = 3;
+static const int NmWhitePixmapSize = 10;
+static const int NmProgressValueComplete = 100;
+static const QString NmParamTextHeightSecondary = "hb-param-text-height-secondary";
+static const QString NmHttpLinkScheme = "http";
+static const QString NmHttpsLinkScheme = "https";
+static const QString NmMailtoLinkScheme = "mailto";
+
+// Local constants
+const qreal NmZoomFactor = 1.5;
+
+/*!
+	\class NmViewerView
+	\brief Mail viewer class
+*/
+
+/*!
+    Constructor
+*/
+NmViewerView::NmViewerView(
+    NmApplication &application,
+    NmUiStartParam* startParam,
+    NmUiEngine &uiEngine,
+    HbMainWindow *mainWindow,
+    NmAttachmentManager &attaManager,
+    bool toolbarEnabled,
+    QGraphicsItem *parent)
+:NmBaseView(startParam, application, parent),
+mApplication(application),
+mUiEngine(uiEngine),
+mMainWindow(mainWindow),
+mAttaManager(attaManager),
+mToolbarEnabled(toolbarEnabled),
+mMessage(NULL),
+mScrollArea(NULL),
+mViewerContent(NULL),
+mWebView(NULL),
+mHeaderWidget(NULL),
+mMessageFetchingOperation(NULL),
+mDisplayingPlainText(false),
+mDocumentLoader(NULL),
+mScrollAreaContents(NULL),
+mScreenSize(QSize(0,0)),
+mWaitDialog(NULL),
+webFrameloadingCompleted(false),
+mLatestLoadingSize(QSize(0,0)),
+mAttaIndexUnderFetch(NmNotFoundError),
+mAttaWidget(NULL),
+mViewReady(false),
+mWaitNoteCancelled(false),
+mErrorNote(NULL)
+{
+    // Create documentloader
+    mDocumentLoader = new NmUiDocumentLoader(mMainWindow);
+    // Get screensize
+    mScreenSize = mApplication.screenSize();
+    // Connect external delete handling to uiengine signal
+    connect(&mUiEngine,
+            SIGNAL(messageDeleted(const NmId &, const NmId &, const NmId &)),
+            this, SLOT(messageDeleted(const NmId &, const NmId &, const NmId &)),
+            Qt::UniqueConnection);
+    // Fetch message
+    loadMessage();
+    // Load view layout
+    loadViewLayout();
+}
+
+/*!
+    Destructor
+*/
+NmViewerView::~NmViewerView()
+{
+    delete mErrorNote;
+    mErrorNote=NULL;
+    delete mWebView;
+    mWebView = NULL;
+    delete mMessage;
+    mMessage = NULL;
+    delete mDocumentLoader;
+    mDocumentLoader = NULL;
+    mWidgetList.clear();
+    delete mWaitDialog;
+    mWaitDialog = NULL;
+    // remove view from osbserving atta manager events
+    mAttaManager.clearObserver();
+    mAttaManager.cancelFetch();
+}
+
+/*!
+    View is about to exit
+*/
+void NmViewerView::aboutToExitView()
+{
+    // View is about to exit, for safety, stop 
+    // loading of content before closing the view
+    if (mWebView){
+        mAttaManager.cancelFetch();
+        mWebView->stop();
+        if (mWebView->page()){
+            mWebView->page()->deleteLater();
+        }
+    }
+}
+
+/*!
+    View layout loading from XML
+*/
+void NmViewerView::loadViewLayout()
+{
+    NM_FUNCTION;
+    
+    // Use document loader to load the view
+    bool ok(false);
+    setObjectName(QString(NMUI_MESSAGE_VIEWER_VIEW));
+   QObjectList objectList;
+   objectList.append(this);
+   // Pass the view to documentloader. Document loader uses this view
+   // when docml is parsed, instead of creating new view.
+   // documentloader is created in constructor
+   mDocumentLoader->setObjectTree(objectList);
+   mWidgetList = mDocumentLoader->load(NMUI_MESSAGE_VIEWER_XML, &ok);
+
+   if (ok)
+   {
+        // Create content and content layout
+        // qobject_cast not work in this case, using reinterpret_cast
+        mViewerContent = reinterpret_cast<HbWidget *>(
+                mDocumentLoader->findObject(NMUI_MESSAGE_VIEWER_CONTENT));
+        // Find scroll area
+        mScrollArea = reinterpret_cast<HbScrollArea *>(
+                mDocumentLoader->findObject(NMUI_MESSAGE_VIEWER_SCROLL_AREA));
+        if (mScrollArea) {
+            mScrollArea->setParentItem(this);
+            mScrollArea->setScrollDirections(Qt::Vertical | Qt::Horizontal);
+            connect(mScrollArea, SIGNAL(scrollPositionChanged(QPointF)),
+                this, SLOT(contentScrollPositionChanged(QPointF)));
+
+            // Get scroll area contents and set layout margins
+            mScrollAreaContents = qobject_cast<HbWidget *>(
+                    mDocumentLoader->findObject(NMUI_MESSAGE_VIEWER_SCROLL_AREA_CONTENTS));
+            if (mScrollAreaContents) {
+                QGraphicsLayout *layout = mScrollAreaContents->layout();
+                if (layout){
+                    layout->setContentsMargins(0,0,0,0);
+                }
+                // Set white pixmap to backgrounditem 
+                QPixmap whitePixmap(NmWhitePixmapSize,NmWhitePixmapSize);
+                whitePixmap.fill(Qt::white);
+                QGraphicsPixmapItem *pixmapItem = new QGraphicsPixmapItem(whitePixmap);
+                mScrollAreaContents->setBackgroundItem(pixmapItem);
+            }
+
+            // Load headerwidget
+            mHeaderWidget = qobject_cast<NmViewerHeader *>(
+                    mDocumentLoader->findObject(NMUI_MESSAGE_VIEWER_HEADER));
+            if (mHeaderWidget) {
+                mHeaderWidget->setView(this);
+                mHeaderWidget->rescaleHeader(mScreenSize);
+                mHeaderWidget->setMessage(mMessage);
+                QPointF headerStartPos = mHeaderWidget->scenePos();
+                mHeaderStartScenePos = QPointF(0,headerStartPos.y());
+            }
+
+            // Load webview
+            mWebView = reinterpret_cast<NmMailViewerWK *>(
+                    mDocumentLoader->findObject(QString(NMUI_MESSAGE_VIEWER_SCROLL_WEB_VIEW)));
+            if (mWebView) {
+                // Set auto load images and private browsing(no history) attributes
+                QWebSettings *settings = mWebView->settings();
+                if (settings) {
+                    settings->setAttribute(QWebSettings::AutoLoadImages, true);
+                    settings->setAttribute(QWebSettings::PrivateBrowsingEnabled, true);
+                }
+                QWebPage *page = mWebView->page();
+                if (page) {
+                    QWebFrame *frame = page->mainFrame();
+                    if (frame) {
+                        frame->setScrollBarPolicy(Qt::Vertical,Qt::ScrollBarAlwaysOff);
+                        frame->setScrollBarPolicy(Qt::Horizontal, Qt::ScrollBarAlwaysOff);
+                        frame->setTextSizeMultiplier(NmZoomFactor);
+                        connect(mWebView->page()->mainFrame(),
+                                SIGNAL(contentsSizeChanged(const QSize&)),
+                            this, SLOT(scaleWebViewWhenLoading(const QSize&)));  
+                    }
+                }
+            }
+        }
+    }
+}
+ 
+/*!
+    Lazy loading when view layout has been loaded
+*/
+void NmViewerView::viewReady()
+{
+    if (!mViewReady){
+        // Set mailbox name to title
+        setMailboxName();
+        // Create toolbar if needed
+        if (mToolbarEnabled) {
+            createToolBar();
+        } else {
+            // Connect options menu about to show to create options menu function
+            QObject::connect(menu(), SIGNAL(aboutToShow()),
+                    this, SLOT(createOptionsMenu())); 
+            // Menu needs one dummy item so that aboutToShow signal is emitted.
+            NmAction *dummy = new NmAction(0);
+            menu()->addAction(dummy);
+        }
+                
+        if (mHeaderWidget) {
+            QPointF contentWidgetPos = mScrollArea->pos();
+            qreal headerHeight = mHeaderWidget->geometry().height();
+            if (mMainWindow->orientation() == Qt::Horizontal) {
+                const QPointF pointToWebView(contentWidgetPos.x(), headerHeight+NmHeaderMargin);
+                mScrollArea->scrollContentsTo(pointToWebView,0);
+            }
+        }
+        
+        // Run fetchmessage in queue
+        QMetaObject::invokeMethod(this, "fetchMessage", Qt::QueuedConnection);
+        // Set view ready
+        mViewReady = true;
+    }
+}
+
+/*!
+    Function fecthes message data based on parameters
+*/
+void NmViewerView::loadMessage()
+{
+    NM_FUNCTION;
+    
+    if (mMessage) {
+        delete mMessage;
+        mMessage = NULL;
+    }
+    // Read start params and message object
+    if (mStartParam){
+        NmId mailboxId = mStartParam->mailboxId();
+        NmId folderId = mStartParam->folderId();
+        NmId msgId = mStartParam->messageId();
+        mMessage = mUiEngine.message(mailboxId, folderId, msgId);
+    }
+}
+
+/*!
+    Function fecthes message data based on parameters. Returns false if message is available,
+    true if message have to be fetched
+*/
+void NmViewerView::fetchMessage()
+{
+    NM_FUNCTION;
+    
+    if (mMessage) {
+        NmId mailboxId = mStartParam->mailboxId();
+        NmId folderId = mStartParam->folderId();
+        NmId msgId = mStartParam->messageId();
+        const NmMessagePart *body = mMessage->htmlBodyPart();
+        if (!body) {
+            // try plain to plain text part
+            body = mMessage->plainTextBodyPart();
+        }
+        // try to fetch if body missing or fetched size is smaller than content size
+        // if body missing it might mean that only header is fetched or message has no body
+        if (!body || (body && (body->fetchedSize() < body->size()))) {
+            // start fetching operation
+            if (mMessageFetchingOperation && mMessageFetchingOperation->isRunning()) { 
+                mMessageFetchingOperation->cancelOperation();
+            }
+            mMessageFetchingOperation = mUiEngine.fetchMessage(mailboxId, folderId, msgId);
+
+            if (mMessageFetchingOperation) {
+                connect(mMessageFetchingOperation,
+                        SIGNAL(operationCompleted(int)),
+                        this,
+                        SLOT(messageFetched(int)));
+                createAndShowWaitDialog();
+            }
+        } else {
+            // message is fetched
+            setMessageData();
+        }
+    }
+}
+
+/*!
+    This is signalled by mMessageFetchingOperation when the original message is fetched.
+ */
+void NmViewerView::messageFetched(int result)
+{
+    mWaitDialog->close();
+    disconnect(mWaitDialog->mainWindow(), SIGNAL(orientationChanged(Qt::Orientation)),
+                this, SLOT(orientationChanged(Qt::Orientation)));
+    
+    if (result == NmNoError && mMessageFetchingOperation) {
+        if (mMessage) {
+            delete mMessage;
+            mMessage = NULL;
+        }
+        // Read start params and message object
+        if (mStartParam) {
+            NmId mailboxId = mStartParam->mailboxId();
+            NmId folderId = mStartParam->folderId();
+            NmId msgId = mStartParam->messageId();
+            mMessage = mUiEngine.message(mailboxId, folderId, msgId);
+        }
+        setMessageData();
+        // Update header message data
+		if (mHeaderWidget){
+        	mHeaderWidget->updateMessageData(mMessage);
+		}
+    }
+}
+
+
+/*!
+    This is signalled by mWaitDialog when the note is cancelled
+ */
+void NmViewerView::waitNoteCancelled()
+{
+    if (!mWaitNoteCancelled) {
+        if (mMessageFetchingOperation && mMessageFetchingOperation->isRunning()) { 
+	        mMessageFetchingOperation->cancelOperation();
+        }
+        mWaitNoteCancelled = true;
+        QMetaObject::invokeMethod(&mApplication, "prepareForPopView", Qt::QueuedConnection);
+    }
+}
+
+
+/*!
+    Function sets message data to web view and header
+*/
+void NmViewerView::setMessageData()
+{
+    NM_FUNCTION;
+    
+    // Connect to observe orientation change events
+    connect(mApplication.mainWindow(), SIGNAL(orientationChanged(Qt::Orientation)),
+                this, SLOT(orientationChanged(Qt::Orientation)));
+
+    // Set page parameters
+    QWebPage *page(NULL);
+    if (mWebView){
+       page = mWebView->page();
+    }
+    if (page){
+        // Set custom network access manager for embedded image handling
+        NmViewerViewNetManager &netMngr = mApplication.networkAccessManager();
+        netMngr.setView(this);
+        page->setNetworkAccessManager(&netMngr);
+        QWebSettings *webSettings = page->settings();
+        if (webSettings) {
+            webSettings->setObjectCacheCapacities(0,0,0);
+        }
+
+        connect(page, SIGNAL(loadFinished(bool)),
+                    this, SLOT(webFrameLoaded(bool)));
+        page->setLinkDelegationPolicy(QWebPage::DelegateAllLinks);
+        page->setContentEditable(false);
+    }
+
+  	// if everything is ok, set message to html viewer
+    if (mMessage && mWebView && page) {
+        // Set initial size of component and content before loading data
+        mWebView->setPreferredWidth(mScreenSize.width());
+        QRectF myGeometry = geometry();
+        page->setViewportSize(myGeometry.size().toSize());
+        //Set message data to html viewer.
+        mWebView->setHtml(formatMessage());
+        // Connect to link clicked
+        QObject::connect(page, SIGNAL(linkClicked(const QUrl&)),
+                this, SLOT(linkClicked(const QUrl&)));
+        changeMessageReadStatus(true);
+        setAttachmentList();
+    }
+}
+
+/*!
+
+*/
+void NmViewerView::setAttachmentList()
+{
+    NM_FUNCTION;
+    
+    // Load headerwidget
+    mAttaWidget = qobject_cast<NmAttachmentListWidget *>(
+            mDocumentLoader->findObject(NMUI_MESSAGE_VIEWER_ATTALIST));
+    if (mMessage && mAttaWidget) {
+        // connect atta widget to listen progress value events
+        QObject::connect(this, SIGNAL(progressValueChanged(int, int)),
+                mAttaWidget, SLOT(setProgressBarValue(int, int)));
+        // Set atta widget text color black since header has static white bg.
+        mAttaWidget->setTextColor(Qt::black);
+        // Set attawidget minimum & maximum size
+        mAttaWidget->setMinimumWidth(mScreenSize.width());
+        mAttaWidget->setMaximumWidth(mScreenSize.width());
+        bool inserted(false);
+        QList<NmMessagePart*> messageParts;
+        mMessage->attachmentList(messageParts);
+        for (int i = 0; i < messageParts.count();i++) {
+            NmMessagePart *part = messageParts[i];
+            if (part &&
+                ((part->contentDisposition().trimmed().startsWith("inline", Qt::CaseInsensitive)==false) ||
+                 (part->contentType().trimmed().startsWith("image", Qt::CaseInsensitive)==false))) {
+                QString fileName = part->attachmentName();
+                // index starts from zero, next index is same as count
+                int attaIndex = mAttaWidget->count();
+                mAttaWidget->insertAttachment(attaIndex, fileName,
+                                              NmUtilities::attachmentSizeString(part->size()));
+                mAttaIdList.insert(attaIndex, part->partId());
+                // check is part under fetch, add progress value to attachment widget
+                if (mAttaManager.isFetching() && 
+                        mAttaManager.partIdUnderFetch() == part->partId()) {
+                    mAttaIndexUnderFetch = attaIndex;
+                }
+                inserted = true;
+            }
+        }
+        if (inserted) {
+            QObject::connect(mAttaWidget, SIGNAL(itemActivated(int)),
+                    this, SLOT(openAttachment(int)));
+        }
+        else {
+            // No attachments, set height to 0
+            mAttaWidget->setMinimumHeight(0);
+            mAttaWidget->setMaximumHeight(0);
+        }
+        // set this as a observer for attachment manager
+        mAttaManager.setObserver(this);
+    }
+}
+
+/*!
+
+*/
+void NmViewerView::openAttachment(int index)
+{
+    NM_FUNCTION;
+    if (index >= 0) {
+        NmId attaId = mAttaIdList.at(index);
+        // reload message to get updates part sizes
+        loadMessage();
+        QList<NmMessagePart*> messageParts;
+        if (mMessage) {
+            mMessage->attachmentList(messageParts);
+             NmId mailboxId = mMessage->envelope().mailboxId();
+             NmId folderId = mMessage->envelope().folderId();
+             NmId messageId = mMessage->envelope().messageId();
+             for (int i = 0; i < messageParts.count(); i++) {
+                 // message part found have to found
+                 // and its fetched size is smaller than size, then start part fetch
+                 if (messageParts[i]->partId() == attaId &&
+                     messageParts[i]->size() > messageParts[i]->fetchedSize()) {
+                     // do not start if there's already ongoing fetch
+                     if (mAttaIndexUnderFetch == NmNotFoundError) {
+                         mAttaIndexUnderFetch = index;
+                         mAttaManager.fetchAttachment(mailboxId, folderId,
+                                                      messageId, attaId);
+                     }
+                 }
+                 // attachment is fetched, open file
+                 else if (messageParts[i]->partId() == attaId) {
+                     mAttaManager.cancelFetch();
+                     XQSharableFile file = mUiEngine.messagePartFile(mailboxId, folderId,
+                                                                     messageId, attaId);
+                     int error = NmUtilities::openFile(file);
+                     file.close();
+                     if (error==NmNotFoundError){
+                         delete mErrorNote;
+                         mErrorNote=NULL;
+                         mErrorNote = NmUtilities::displayWarningNote(
+                                 hbTrId("txt_mail_dialog_unable_to_open_attachment_file_ty"));
+                     }
+                 }
+             }            
+         }
+    }
+}
+
+/*!
+    Function formats message based on actual data
+*/
+QString NmViewerView::formatMessage()
+{
+    NM_FUNCTION;
+    
+    QString msg = "";
+    if (mMessage) {
+        NmMessagePart *html = mMessage->htmlBodyPart();
+        if (html) {
+            msg += formatHtmlMessage(html);
+        }
+        else {
+            NmMessagePart *plain = mMessage->plainTextBodyPart();
+            if (plain) {
+                msg += formatPlainTextMessage(plain);
+            }
+        }    
+    }
+    return msg;
+}
+
+/*!
+    Function formats html message
+*/
+QString NmViewerView::formatHtmlMessage(NmMessagePart *html)
+{    
+    NM_FUNCTION;
+
+    QString msg = "";
+    if (html && mMessage) {
+        NmId mailboxId = mMessage->envelope().mailboxId();
+        NmId folderId = mMessage->envelope().folderId();
+        NmId messageId = mMessage->envelope().messageId();
+        QList<NmMessagePart*> parts;
+        mMessage->attachmentList(parts);
+        for (int i=0; i < parts.count(); i++) {
+            NmMessagePart *child = parts[i];
+            // Browse through embedded image parts and add those
+            // the web view.
+            bool isFetched = child->fetchedSize() >= child->size();
+            if (child->contentType().startsWith("image", Qt::CaseInsensitive)) {
+                QString contentId = child->contentId();
+                if (isFetched) {
+                    int ret = mUiEngine.contentToMessagePart(
+                            mailboxId, folderId, messageId, *child);
+                    if (ret == NmNoError) {
+                      mWebView->addContent(contentId, QVariant::fromValue(child->binaryContent()), 
+                              child->partId(), isFetched);
+                    }
+                }
+                else {
+                    mWebView->addContent(contentId, QVariant::fromValue(QByteArray()), 
+                            child->partId(), isFetched);
+                }
+            }
+        }
+        int ret = mUiEngine.contentToMessagePart(mailboxId, folderId, messageId, *html);
+        if (ret == NmNoError) {
+         msg = html->textContent();
+        }    
+    }
+    return msg;
+}
+
+/*!
+    Function formats plain text message message
+*/
+QString NmViewerView::formatPlainTextMessage(NmMessagePart *plain)
+{
+    NM_FUNCTION;
+      
+    QString msg = "";
+    if (plain && mMessage) {
+        NmId mailboxId = mMessage->envelope().mailboxId();
+        NmId folderId = mMessage->envelope().folderId();
+        NmId messageId = mMessage->envelope().messageId();
+        int ret = mUiEngine.contentToMessagePart(mailboxId, folderId,
+                                                 messageId, *plain);
+        if (ret == NmNoError) {
+            QTextDocument document;
+            // set font
+            QFont currentFont = document.defaultFont();
+            currentFont.setWeight(QFont::Normal);
+            qreal secondarySize;
+            HbStyle myStyle;
+            bool found = myStyle.parameter(NmParamTextHeightSecondary, secondarySize);
+            if (found) {
+                HbFontSpec fontSpec(HbFontSpec::Secondary);
+                fontSpec.setTextHeight(secondarySize);
+                currentFont.setPixelSize(fontSpec.font().pixelSize());
+            }
+            document.setDefaultFont(currentFont);
+            // convert to html
+            document.setPlainText(plain->textContent());
+            msg = document.toHtml();
+    
+            if (qApp->layoutDirection()==Qt::RightToLeft){
+                // add right alignment to document css section
+                QRegExp rx("(<style type=\"text/css\">)(.+)(</style>)", Qt::CaseInsensitive);
+                rx.setMinimal(true);
+                int pos = rx.indexIn(msg);
+                if (pos > -1) {
+                    QString newStr = rx.cap(1);
+                    newStr.append(rx.cap(2));
+                    newStr.append("p { text-align: right } ");
+                    newStr.append(rx.cap(3));
+                    msg.replace(rx, newStr);
+                }
+            }
+        }    
+    }
+    mDisplayingPlainText=true;  
+    return msg;
+}
+
+/*!
+    Reload view contents with new start parameters
+    Typically when view is already open and external view activation occurs
+    for this same view
+*/
+void NmViewerView::reloadViewContents(NmUiStartParam* startParam)
+{
+    // Check start parameter validity, message view cannot
+    // be updated if given parameter is zero.
+    if (startParam && startParam->viewId() == NmUiViewMessageViewer &&
+        startParam->messageId()!= 0) {
+        // Delete existing start parameter data
+        delete mStartParam;
+        mStartParam = NULL;
+        // Store new start parameter data
+        mStartParam = startParam;
+        // Reload viewer with new message information
+        setMessageData();
+    }
+    else {
+        NMLOG("nmailui: Invalid viewer start parameter");
+        // Unused start parameter needs to be deleted
+        delete startParam;
+    }
+}
+
+/*!
+    nmailViewId
+*/
+NmUiViewId NmViewerView::nmailViewId() const
+{
+    return NmUiViewMessageViewer;
+}
+
+/*!
+    Scale web view width
+*/
+void NmViewerView::webFrameLoaded(bool loaded)
+{
+    if (loaded){
+        webFrameloadingCompleted = true;
+        // Scale web view after loading the
+        // complete contents, including images
+        QMetaObject::invokeMethod(this, "scaleWebViewWhenLoaded", Qt::QueuedConnection);       
+    }
+}
+
+/*!
+    Scale web view width when loading is ongoing
+*/
+void NmViewerView::scaleWebViewWhenLoading(const QSize &size)
+{
+    // Try to scale web view while mainframe is being loaded.
+    // So that screen is scrollable even before images are fully loaded
+    // First check that new size is different than previous, no need to react if
+    // same size value is received more than once.
+    if (size != mLatestLoadingSize) {
+        if (!webFrameloadingCompleted && mWebView && mWebView->page() &&
+            (size.width() > mScreenSize.width() || size.height() > geometry().height())) {
+            int width = (int)size.width();
+            int height = (int)size.height();
+            // Set content (webview) width
+            if (mDisplayingPlainText){
+                mWebView->setPreferredWidth(geometry().width());           
+            }
+            else {
+                mWebView->setPreferredWidth(width);
+            }
+            mWebView->setMinimumHeight(height);
+        }
+    }
+    mLatestLoadingSize = size;
+}
+
+/*!
+    Scale web view width when loading is completed
+*/
+void NmViewerView::scaleWebViewWhenLoaded()
+{
+    QRectF myGeometry = geometry();
+    QWebPage *page = mWebView->page();
+    if (mWebView && page) {
+        page->setPreferredContentsSize(myGeometry.size().toSize());
+        QSizeF contentSize = page->mainFrame()->contentsSize();
+        mWebView->setPreferredSize(contentSize);
+    }
+    // Workaround for scrolling problem
+    scene()->setProperty("overridingGesture",QVariant());
+}
+
+/*!
+    Set new dimensions after orientation change.
+*/
+void NmViewerView::adjustViewDimensions()
+{
+    // Update current screensize
+    mScreenSize = mApplication.screenSize();
+    // Scale header to screen width
+    if (mHeaderWidget){
+        mHeaderWidget->rescaleHeader(mScreenSize);
+    }
+    if (mAttaWidget){
+        // Set attawidget minimum & maximum size
+        mAttaWidget->setMinimumWidth(mScreenSize.width());
+        mAttaWidget->setMaximumWidth(mScreenSize.width());
+    }    
+    scaleWebViewWhenLoaded();
+
+    if (mToolbarEnabled) {
+		// Re-create toolbar in orientation switch
+		createToolBar();
+    }
+}
+
+/*!
+   Screen orientation changed. Web view needs to be scaled when
+   landscape <-> portrait switch occurs because text needs to
+   be wrapped again.
+*/
+void NmViewerView::orientationChanged(Qt::Orientation orientation)
+{
+    Q_UNUSED(orientation);
+    QTimer::singleShot(NmOrientationTimer, this, SLOT(adjustViewDimensions()));
+}
+
+/*!
+   Link clicked callback
+*/
+void NmViewerView::linkClicked(const QUrl& link)
+{
+    NM_FUNCTION;
+    
+    if (link.scheme() == NmHttpLinkScheme ||
+        link.scheme() == NmHttpsLinkScheme) {
+        mAttaManager.cancelFetch();
+        QDesktopServices::openUrl(link);
+    } else if (link.scheme() == NmMailtoLinkScheme){
+        mAttaManager.cancelFetch();
+        QList<NmAddress*> *addrList = new QList<NmAddress*>();
+        NmAddress *mailtoAddr = new NmAddress();
+        QString address = link.toString(QUrl::RemoveScheme);
+        mailtoAddr->setAddress(address);
+        mailtoAddr->setDisplayName(address);
+        addrList->append(mailtoAddr);
+        // Create start parameters. Address list ownership
+        // is transferred to startparam object
+        NmUiStartParam* param = new NmUiStartParam(NmUiViewMessageEditor,
+                                                   mStartParam->mailboxId(),
+                                                   mStartParam->folderId(),
+                                                   0,
+                                                   NmUiEditorMailto,
+                                                   addrList);
+        mApplication.enterNmUiView(param);
+    }    
+}
+
+/*!
+   Function can be used to check whether mouse event has
+   occured on top of header area.
+*/
+bool NmViewerView::eventOnTopOfHeaderArea(QGraphicsSceneMouseEvent *event)
+{
+    bool ret(false);
+    if (event && mHeaderWidget) {
+        QPointF lastReleasePoint = event->lastPos();
+        QPointF contentWidgetPos = mScrollAreaContents->pos();
+        int headerHeight = (int)mHeaderWidget->geometry().height();
+        if (lastReleasePoint.y()<headerHeight+contentWidgetPos.y()) {
+            ret=true;
+        }
+    }
+    return ret;
+}
+
+/*!
+    Get function for content widget web view.
+*/
+NmMailViewerWK* NmViewerView::webView()
+{
+    return mWebView;
+}
+
+/*!
+    Get function for message being viewed
+*/
+NmMessage* NmViewerView::message()
+{
+    return mMessage;
+}
+
+/*!
+   Function to set message read status
+*/
+void NmViewerView::changeMessageReadStatus(bool read)
+{
+    NM_FUNCTION;
+    
+    QList<const NmMessageEnvelope*> envelopeList;
+    NmMessageEnvelope *envelope = &mMessage->envelope();
+    QPointer<NmStoreEnvelopesOperation> op(NULL);
+    if (envelope) {
+        if ( read != envelope->isRead() ){
+            if (read){
+                envelope->setRead(true);
+                envelopeList.append(envelope);
+                op = mUiEngine.setEnvelopes(
+                    mStartParam->mailboxId(),
+                    mStartParam->folderId(),
+                    MarkAsRead,
+                    envelopeList);
+            }
+            else {
+                envelope->setRead(false);
+                envelopeList.append(envelope);
+                op = mUiEngine.setEnvelopes(
+                    mStartParam->mailboxId(),
+                    mStartParam->folderId(),
+                    MarkAsUnread,
+                    envelopeList);
+            }
+        }
+    }
+}
+
+/*!
+    Set mailbox name to title
+*/
+void NmViewerView::setMailboxName()
+{
+    if (mStartParam){
+        NmMailboxMetaData *meta = mUiEngine.mailboxById(mStartParam->mailboxId());
+        if (meta) {
+            setTitle(meta->name());
+        }
+    }
+}
+
+/*!
+    contentScrollPositionChanged.
+    Function reacts to scroll position change events and sets
+    header to correct position
+*/
+void NmViewerView::contentScrollPositionChanged(const QPointF &newPosition)
+{
+    if (mWebView&&mHeaderWidget){
+        QRectF webViewRect = mWebView->geometry();
+        QTransform tr;
+        qreal leftMovementThreshold(webViewRect.width()-mHeaderWidget->geometry().width());
+        if (newPosition.x()<0) {
+            tr.translate(webViewRect.topLeft().x() ,0);
+        }
+        else if (newPosition.x()>=0 && newPosition.x()<leftMovementThreshold) {
+            tr.translate(mHeaderStartScenePos.x()+newPosition.x() ,0);
+        }
+        else {
+            tr.translate(webViewRect.topLeft().x()+leftMovementThreshold ,0);
+        }
+        mHeaderWidget->setTransform(tr);
+        if (mAttaWidget) {
+            mAttaWidget->setTransform(tr);
+        }
+    }
+    mLatestScrollPos = newPosition;
+}
+
+/*!
+    createToolBar. Function asks menu commands from extension
+    to be added to toolbar owned by the HbView.
+*/
+void NmViewerView::createToolBar()
+{
+    HbToolBar *tb = toolBar();
+    NmUiExtensionManager &extMngr = mApplication.extManager();
+    if (tb && &extMngr && mStartParam) {
+        tb->clearActions();
+        NmActionRequest request(this, NmActionToolbar, NmActionContextViewViewer,
+                NmActionContextDataNone, mStartParam->mailboxId(), mStartParam->folderId() );
+        QList<NmAction *> list;
+        extMngr.getActions(request, list);
+        for (int i = 0; i < list.count(); i++) {
+            tb->addAction(list[i]);
+        }
+    }
+}
+
+/*!
+    createOptionsMenu. Functions asks menu commands from extension
+    to be added to options menu.
+*/
+void NmViewerView::createOptionsMenu()
+{
+	HbMenu *optionsMenu = menu();
+	NmUiExtensionManager &extMngr = mApplication.extManager();
+	if (optionsMenu && &extMngr && mStartParam) {
+		optionsMenu->clearActions();
+		NmActionRequest request(this, NmActionOptionsMenu, NmActionContextViewViewer,
+				NmActionContextDataNone, mStartParam->mailboxId(), mStartParam->folderId() );
+
+		QList<NmAction*> list;
+		extMngr.getActions(request, list);
+		for (int i=0;i<list.count();i++) {
+			optionsMenu->addAction(list[i]);
+		}
+	}
+}
+
+/*!
+    handleActionCommand. From NmActionObserver, extension manager calls this
+    call to handle menu command in the UI.
+*/
+void NmViewerView::handleActionCommand(NmActionResponse &actionResponse)
+{
+    // Handle options menu or toolbar
+    if (actionResponse.menuType() == NmActionOptionsMenu ||
+    	actionResponse.menuType() == NmActionToolbar) {
+        switch (actionResponse.responseCommand()) {
+            case NmActionResponseCommandReply: {
+                mAttaManager.cancelFetch();
+                NmUiStartParam *startParam = new NmUiStartParam(NmUiViewMessageEditor,
+                    mStartParam->mailboxId(), mStartParam->folderId(),
+                    mStartParam->messageId(), NmUiEditorReply);
+                mApplication.enterNmUiView(startParam);
+            }
+            break;
+            case NmActionResponseCommandReplyAll: {
+                mAttaManager.cancelFetch();
+                NmUiStartParam *startParam = new NmUiStartParam(NmUiViewMessageEditor,
+                    mStartParam->mailboxId(), mStartParam->folderId(),
+                    mStartParam->messageId(), NmUiEditorReplyAll);
+                mApplication.enterNmUiView(startParam);
+            }
+            break;
+            case NmActionResponseCommandForward: {
+                mAttaManager.cancelFetch();
+                NmUiStartParam *startParam = new NmUiStartParam(NmUiViewMessageEditor,
+                    mStartParam->mailboxId(), mStartParam->folderId(),
+                    mStartParam->messageId(), NmUiEditorForward);
+                mApplication.enterNmUiView(startParam);
+            }
+            break;
+            case NmActionResponseCommandDeleteMail: {
+                mAttaManager.cancelFetch();
+                deleteMessage();
+                }
+            break;
+            default:
+                break;
+        }
+    }
+}
+
+/*!
+    Deletes the currently open message
+*/
+void NmViewerView::deleteMessage()
+{
+    NM_FUNCTION;
+    
+    QList<NmId> messageList;
+    messageList.append(mStartParam->messageId());  
+    int err = mUiEngine.deleteMessages(mStartParam->mailboxId(),
+                                       mStartParam->folderId(),
+                                       messageList);
+    messageList.clear();
+}
+
+
+/*!
+    This is called when attachment fetch progress changes
+*/
+void NmViewerView::progressChanged(int value)
+{
+    if (mAttaIndexUnderFetch != NmNotFoundError) {
+        // emit signal
+        if (mAttaWidget && mAttaWidget->progressValue(mAttaIndexUnderFetch) < value) {
+            progressValueChanged(mAttaIndexUnderFetch, value);
+        }
+    }
+}
+
+/*!
+    This is called when attachment fetch is completed
+*/
+void NmViewerView::fetchCompleted(int result)
+{
+    if (mAttaWidget && mAttaIndexUnderFetch != NmNotFoundError) {
+        if (result == NmNoError) {
+            progressValueChanged(mAttaIndexUnderFetch, NmProgressValueComplete);
+            openAttachment(mAttaIndexUnderFetch);
+        } else {
+            mAttaWidget->hideProgressBar(mAttaIndexUnderFetch);
+        }
+    }
+    mAttaIndexUnderFetch = NmNotFoundError;
+}
+
+/*!
+    externalDelete. From NmUiEngine, handles viewer shutdown when current message is deleted.
+*/
+void NmViewerView::messageDeleted(const NmId &mailboxId, const NmId &folderId, const NmId &messageId)
+{
+    if ((mStartParam->viewId() == NmUiViewMessageViewer)
+        && (mStartParam->mailboxId()== mailboxId)
+        && (mStartParam->folderId()== folderId)
+        && (mStartParam->messageId()== messageId)) {
+        mApplication.prepareForPopView();
+    }
+}
+
+/*!
+    Helper function for wait dialog creation.
+*/
+void NmViewerView::createAndShowWaitDialog()
+{
+    delete mWaitDialog;
+    mWaitDialog = NULL;
+    // Create new wait dialog and set it to me modal with dimmed background
+    mWaitDialog = new HbProgressDialog(HbProgressDialog::WaitDialog);
+    // Connect to observe orientation change events
+    connect(mWaitDialog->mainWindow(), SIGNAL(orientationChanged(Qt::Orientation)),
+                this, SLOT(orientationChanged(Qt::Orientation)));
+    mWaitDialog->setModal(true);
+    mWaitDialog->setBackgroundFaded(true);
+    connect(mWaitDialog, SIGNAL(cancelled()), this, SLOT(waitNoteCancelled()));
+    mWaitDialog->setText(hbTrId("txt_mail_dialog_loading_mail_content"));
+    // Display wait dialog
+    mWaitDialog->show(); 
+}