emailuis/nmailui/src/nmeditorcontent.cpp
author hgs
Fri, 02 Jul 2010 15:55:16 +0300
changeset 49 00c7ae862740
parent 48 10eaf342f539
child 51 d845db10c0d4
permissions -rw-r--r--
201025_2

/*
* 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: Message editor contents widget
*
*/

#include "nmuiheaders.h"

// Layout
static const char *NMUI_EDITOR_BODY = "BodyTextEdit";
static const char *NMUI_EDITOR_SCROLL_AREA = "scrollArea";
static const char *NMUI_EDITOR_SCROLL_AREA_CONTENTS = "scrollAreaContents";

// Regular expression for selecting img tags with "cid" in the mail.
static const char *NMUI_EDITOR_REMOVE_EMBD_IMAGES_REG = 
    "(<img[^<]+(src\\s*=\\s*)(.{0,1}cid)([^<]+)(>\\s*|/>\\s*|></img>\\s*))";
	
/*!
    Constructor
*/
NmEditorContent::NmEditorContent(QObject *parent,
                                 HbDocumentLoader *documentLoader,
                                 QNetworkAccessManager &manager,
                                 NmApplication &application) :
    QObject(parent),
    mHeader(NULL),
    mMessageBodyType(NmPlainText),
    mEditorWidget(NULL),
    mScrollArea(NULL),
    mScrollAreaContents(NULL),
    mApplication(application)
{
    NM_FUNCTION;

    // Construct container for the header widgets
    mHeader = new NmEditorHeader(this, documentLoader);

    // Get pointer to body text area handling widget
    mEditorWidget = qobject_cast<NmEditorTextEdit *>(documentLoader->findWidget(NMUI_EDITOR_BODY));
    
    // Set body editor to use NmEditorTextDocument
    NmEditorTextDocument *textDocument = new NmEditorTextDocument(manager);
    mEditorWidget->setDocument(textDocument); 
    textDocument->setParent(mEditorWidget); // ownership changes

    mScrollArea = qobject_cast<NmBaseViewScrollArea *>
        (documentLoader->findWidget(NMUI_EDITOR_SCROLL_AREA));
    mScrollArea->setScrollDirections(Qt::Vertical | Qt::Horizontal);
    
    // Enable style picker menu item.
    mEditorWidget->setFormatDialog(new HbFormatDialog());

    mScrollAreaContents =
        qobject_cast<HbWidget *>(documentLoader->findWidget(NMUI_EDITOR_SCROLL_AREA_CONTENTS));
    
    // Create signal slot connections
    createConnections();
}

/*!
    Destructor
*/
NmEditorContent::~NmEditorContent()
{
    NM_FUNCTION;
}

/*!
    Fill message data into header and body fileds. If reply envelopw is
    present, reply header is generated and set to editor. Reply
    envelope ownership is not transferred here.
 */
void NmEditorContent::setMessageData(const NmMessage &originalMessage, 
                                     NmUiEditorStartMode &editorStartMode)
{
    NM_FUNCTION;
    
    QString bodyContent;
    
    // Create the "reply" header (also for forward message)
    if (editorStartMode==NmUiEditorReply || editorStartMode==NmUiEditorReplyAll || 
        editorStartMode==NmUiEditorForward) {
        bodyContent.append(NmUtilities::createReplyHeader(originalMessage.envelope()));
    }
    
    // Check which part is present. Html or plain text part. We use the original message parts.
    const NmMessagePart *htmlPart = originalMessage.htmlBodyPart();
    const NmMessagePart *plainPart = originalMessage.plainTextBodyPart();
 
    if (htmlPart) {
        bodyContent.append(htmlPart->textContent());
        if(editorStartMode==NmUiEditorReply || editorStartMode==NmUiEditorReplyAll ) {
            removeEmbeddedImages(bodyContent);
        }
        emit setHtml(bodyContent);
        mMessageBodyType = NmHTMLText;
    }
    else if (plainPart) {
        // Plain text part was present, set it to HbTextEdit
        bodyContent.append(plainPart->textContent());
        emit setPlainText(bodyContent);
        mMessageBodyType = NmPlainText;
    }
}  

/*!
   This method creates all needed signal-slot connections
 */
void NmEditorContent::createConnections()
{
    NM_FUNCTION;
    
    // Signal for setting HbTextEdit widgets html content
    connect(this, SIGNAL(setHtml(QString)),
            mEditorWidget, SLOT(setHtml(QString)), Qt::QueuedConnection);

    // Signal for setting HbTextEdit widgets plain text content
    connect(this, SIGNAL(setPlainText(QString)),
            mEditorWidget, SLOT(setPlainText(QString)), Qt::QueuedConnection);

    // Inform text edit widget that header height has been changed
    connect(mHeader, SIGNAL(headerHeightChanged(int)), this, SLOT(setEditorContentHeight()),
        Qt::QueuedConnection);

    // we are interested in the document's height changes
    connect(mEditorWidget->document()->documentLayout(), SIGNAL(documentSizeChanged(QSizeF)), this,
        SLOT(setEditorContentHeight()), Qt::QueuedConnection);

    // We need to update the scroll position according the editor's cursor position
    connect(mHeader->toEdit(), SIGNAL(cursorPositionChanged(int, int)), this, 
        SLOT(ensureCursorVisibility()), Qt::QueuedConnection);
    connect(mHeader->ccEdit(), SIGNAL(cursorPositionChanged(int, int)), this, 
        SLOT(ensureCursorVisibility()), Qt::QueuedConnection);
    connect(mHeader->bccEdit(), SIGNAL(cursorPositionChanged(int, int)), this, 
        SLOT(ensureCursorVisibility()), Qt::QueuedConnection);
    connect(mHeader->subjectEdit(), SIGNAL(cursorPositionChanged(int, int)), this, 
        SLOT(ensureCursorVisibility()), Qt::QueuedConnection);
    connect(mEditorWidget, SIGNAL(cursorPositionChanged(int, int)), this, 
        SLOT(ensureCursorVisibility()), Qt::QueuedConnection);

    // listen to the parent's (NmEditorView) size changes which happen eg. when VKB is opened/closed
    connect(parent(), SIGNAL(sizeChanged()), this, SLOT(ensureCursorVisibility()),
        Qt::QueuedConnection);
}

/*!
    Return pointer to the email body text edit widget
 */
NmEditorTextEdit *NmEditorContent::editor() const
{
    NM_FUNCTION;
    
    return mEditorWidget;
}

/*!
    Return pointer to the header widget
 */
NmEditorHeader *NmEditorContent::header() const
{
    NM_FUNCTION;
    
    return mHeader;
}

/*!
    This slot is called when header widget height has been changed. Function performs
    the repositioning of the body field and resizing of the editor and content area.
 */
void NmEditorContent::setEditorContentHeight()
{
    NM_FUNCTION;
    
    // the height of the margin between the title bar and the header
    qreal topMargin = 0;
    HbStyle().parameter("hb-param-margin-gene-top", topMargin);
    
    // header height
    qreal headerHeight = mHeader->headerHeight();

    // body area editor's document height with margins added
    qreal documentHeightAndMargins = mEditorWidget->document()->size().height() +
        (mEditorWidget->document()->documentMargin() * 2);

    // chrome height
    qreal chromeHeight = 0;
    HbStyle().parameter("hb-param-widget-chrome-height", chromeHeight);
    
    // screen height
    qreal screenHeight = mApplication.screenSize().height();

    // set min size for the body area so that at least the screen area is always filled
    qreal bodyAreaMinSize = screenHeight - chromeHeight - topMargin - headerHeight;
    
    qreal bodyAreaSize = fmax(bodyAreaMinSize, documentHeightAndMargins);

    mScrollAreaContents->setPreferredHeight(topMargin + headerHeight + bodyAreaSize);
}

/*!
    This slot is called when the cursor visibility has to be ensured ie. the scroll position is 
    adjusted so that the cursor can be seen.
*/
void NmEditorContent::ensureCursorVisibility()
{
    NM_FUNCTION;

    // check which of the editors has the focus and get the x/y coordinates for the cursor position
    QGraphicsWidget *focused = mScrollAreaContents->focusWidget();
    
    if (focused) {
        QRectF localRect(0, 0, 0, 0);
        bool notFound = false;
        
        if (focused == mHeader->toEdit()) {
            localRect = mHeader->toEdit()->rectForCursorPosition();
        }
        else if (focused == mHeader->ccEdit()) {
            localRect = mHeader->ccEdit()->rectForCursorPosition();
        }
        else if (focused == mHeader->bccEdit()) {
            localRect = mHeader->bccEdit()->rectForCursorPosition();
        }
        else if (focused == mHeader->subjectEdit()) {
            localRect = mHeader->subjectEdit()->rectForCursorPosition();
        }
        else if (focused == mEditorWidget) {
            localRect = mEditorWidget->rectForCursorPosition();
        }
        else {
            notFound = true;
        }

        if (!notFound) {
            QPointF topLeftPos = focused->mapToItem(mScrollAreaContents, localRect.topLeft());
            QPointF bottomRightPos =
                focused->mapToItem(mScrollAreaContents, localRect.bottomRight());
            qreal marginRight = 0;
            HbStyle().parameter("hb-param-margin-gene-right", marginRight);
            bottomRightPos.rx() += marginRight;
            mScrollArea->ensureVisible(topLeftPos);
            mScrollArea->ensureVisible(bottomRightPos);
        }
    }
}
/*!
    Removes embedded images from the message body
 */
void NmEditorContent::removeEmbeddedImages(QString &bodyContent)
{
    NM_FUNCTION;
 
    QRegExp regExp(NMUI_EDITOR_REMOVE_EMBD_IMAGES_REG, Qt::CaseInsensitive);
    bodyContent.remove(regExp);
}