emailuis/nmailui/src/nmeditorcontent.cpp
branchRCL_3
changeset 63 d189ee25cf9d
equal deleted inserted replaced
61:dcf0eedfc1a3 63:d189ee25cf9d
       
     1 /*
       
     2 * Copyright (c) 2009 - 2010 Nokia Corporation and/or its subsidiary(-ies).
       
     3 * All rights reserved.
       
     4 * This component and the accompanying materials are made available
       
     5 * under the terms of "Eclipse Public License v1.0"
       
     6 * which accompanies this distribution, and is available
       
     7 * at the URL "http://www.eclipse.org/legal/epl-v10.html".
       
     8 *
       
     9 * Initial Contributors:
       
    10 * Nokia Corporation - initial contribution.
       
    11 *
       
    12 * Contributors:
       
    13 *
       
    14 * Description: Message editor contents widget
       
    15 *
       
    16 */
       
    17 
       
    18 #include "nmuiheaders.h"
       
    19 
       
    20 // Layout
       
    21 static const char *NMUI_EDITOR_BODY = "BodyTextEdit";
       
    22 static const char *NMUI_EDITOR_SCROLL_AREA = "scrollArea";
       
    23 static const char *NMUI_EDITOR_SCROLL_AREA_CONTENTS = "scrollAreaContents";
       
    24 
       
    25 // Regular expression for selecting img tags with "cid" in the mail.
       
    26 static const char *NMUI_EDITOR_REMOVE_EMBD_IMAGES_REG = 
       
    27     "(<img[^<]+(src\\s*=\\s*)(.{0,1}cid)([^<]+)(>\\s*|/>\\s*|></img>\\s*))";
       
    28 	
       
    29 /*!
       
    30     Constructor
       
    31 */
       
    32 NmEditorContent::NmEditorContent(QObject *parent,
       
    33                                  HbDocumentLoader *documentLoader,
       
    34                                  QNetworkAccessManager &manager,
       
    35                                  NmApplication &application) :
       
    36     QObject(parent),
       
    37     mHeader(NULL),
       
    38     mEditorWidget(NULL),
       
    39     mScrollArea(NULL),
       
    40     mScrollAreaContents(NULL),
       
    41     mApplication(application),
       
    42     mNeedForWidthAdjustment(false)
       
    43 {
       
    44     NM_FUNCTION;
       
    45 
       
    46     // Construct container for the header widgets
       
    47     mHeader = new NmEditorHeader(this, application, documentLoader);
       
    48 
       
    49     // Get pointer to body text area handling widget
       
    50     mEditorWidget = qobject_cast<NmEditorTextEdit *>(documentLoader->findWidget(NMUI_EDITOR_BODY));
       
    51 
       
    52     // Enable the emoticons.
       
    53     mEditorWidget->setSmileysEnabled(true);
       
    54 
       
    55     // Set body editor to use NmEditorTextDocument
       
    56     NmEditorTextDocument *textDocument = new NmEditorTextDocument(manager);
       
    57     mEditorWidget->setDocument(textDocument); 
       
    58     textDocument->setParent(mEditorWidget); // ownership changes
       
    59 
       
    60     mScrollArea = qobject_cast<NmBaseViewScrollArea *>
       
    61         (documentLoader->findWidget(NMUI_EDITOR_SCROLL_AREA));
       
    62     
       
    63     mScrollArea->setScrollDirections(Qt::Vertical | Qt::Horizontal);
       
    64     mScrollArea->setClampingStyle(HbScrollArea::BounceBackClamping);
       
    65         
       
    66     // Enable style picker menu item.
       
    67     mEditorWidget->setFormatDialog(new HbFormatDialog());
       
    68 
       
    69     mScrollAreaContents =
       
    70         qobject_cast<HbWidget *>(documentLoader->findWidget(NMUI_EDITOR_SCROLL_AREA_CONTENTS));
       
    71     
       
    72     // Create signal slot connections
       
    73     createConnections();
       
    74 
       
    75     // The following line is necessary in terms of being able to add emoticons
       
    76     // (smileys) to an empty document (mail content). Otherwise the private
       
    77     // pointer of the QTextDocument which the smiley engine has is NULL and
       
    78     // inserting a smiley will lead to an error.
       
    79     mEditorWidget->setPlainText("");
       
    80 }
       
    81 
       
    82 /*!
       
    83     Destructor
       
    84 */
       
    85 NmEditorContent::~NmEditorContent()
       
    86 {
       
    87     NM_FUNCTION;
       
    88 }
       
    89 
       
    90 /*!
       
    91     Sets the body content. If reply envelopw is present, reply header is generated and set to 
       
    92     editor. Reply envelope ownership is not transferred here.
       
    93  */
       
    94 void NmEditorContent::setBodyContent(NmUiEditorStartMode editorStartMode,
       
    95                                      const NmMessage *originalMessage, 
       
    96                                      const QString *signature)
       
    97 {
       
    98     NM_FUNCTION;
       
    99     
       
   100     QString bodyContent;
       
   101     
       
   102     // first insert the signature
       
   103     if (signature) {
       
   104         bodyContent.append("<html><body><br><br>");
       
   105         bodyContent.append(*signature);
       
   106         bodyContent.append("<br></body></html>");
       
   107     }
       
   108     
       
   109 	QTextCursor cursor(mEditorWidget->document());
       
   110     
       
   111     // Create the "reply" header (also for forward message)
       
   112 	// sets the font color of the reply header and the original body text to black
       
   113     if ((editorStartMode==NmUiEditorReply || editorStartMode==NmUiEditorReplyAll || 
       
   114         editorStartMode==NmUiEditorForward) && originalMessage) {
       
   115 		bodyContent.append(QString("<style type=\"text/css\">* { color: black; }</style>"));
       
   116         bodyContent.append(NmUtilities::createReplyHeader(originalMessage->envelope()));
       
   117     }
       
   118     
       
   119     // Check which part is present. Html or plain text part. We use the original message parts.
       
   120     const NmMessagePart *htmlPart = NULL;
       
   121     const NmMessagePart *plainPart = NULL;
       
   122     if (originalMessage) {
       
   123         htmlPart = originalMessage->htmlBodyPart();
       
   124         plainPart = originalMessage->plainTextBodyPart();
       
   125     }
       
   126  
       
   127     if (htmlPart) {
       
   128         bodyContent.append(htmlPart->textContent());
       
   129         if(editorStartMode==NmUiEditorReply || editorStartMode==NmUiEditorReplyAll ) {
       
   130             removeEmbeddedImages(bodyContent);
       
   131         }
       
   132     }
       
   133     else if (plainPart) {
       
   134         // Plain text part was present, set it to HbTextEdit as HTML
       
   135         bodyContent.append(QString("<html><body><p>"));
       
   136         bodyContent.append(plainPart->textContent());
       
   137         bodyContent.append(QString("</p></body></html>"));
       
   138     }
       
   139     cursor.insertHtml(bodyContent);
       
   140 
       
   141     // Update of the body width is done when next contentChanged signal comes from the body.
       
   142     mNeedForWidthAdjustment = true;
       
   143 	cursor.clearSelection();
       
   144 	cursor.setPosition(0);
       
   145 	cursor.insertHtml(QString("<html><body></body></html>"));
       
   146 }  
       
   147 
       
   148 /*!
       
   149    This method creates all needed signal-slot connections
       
   150  */
       
   151 void NmEditorContent::createConnections()
       
   152 {
       
   153     NM_FUNCTION;
       
   154     
       
   155     // Signal for setting HbTextEdit widgets html content
       
   156     connect(this, SIGNAL(setHtml(QString)),
       
   157             mEditorWidget, SLOT(setHtml(QString)), Qt::QueuedConnection);
       
   158 
       
   159     // Signal for setting HbTextEdit widgets plain text content
       
   160     connect(this, SIGNAL(setPlainText(QString)),
       
   161             mEditorWidget, SLOT(setPlainText(QString)), Qt::QueuedConnection);
       
   162 
       
   163     // Inform text edit widget that header height has been changed
       
   164     connect(mHeader, SIGNAL(headerHeightChanged(int)), this, SLOT(setEditorContentHeight()),
       
   165         Qt::QueuedConnection);
       
   166 
       
   167     // we are interested in the document's height changes
       
   168     connect(mEditorWidget->document()->documentLayout(), SIGNAL(documentSizeChanged(QSizeF)), this,
       
   169         SLOT(setEditorContentHeight()), Qt::QueuedConnection);
       
   170 
       
   171     // We need to update the scroll position according the editor's cursor position
       
   172     connect(mHeader->toEdit(), SIGNAL(cursorPositionChanged(int, int)), this, 
       
   173         SLOT(ensureCursorVisibility()), Qt::QueuedConnection);
       
   174     connect(mHeader->ccEdit(), SIGNAL(cursorPositionChanged(int, int)), this, 
       
   175         SLOT(ensureCursorVisibility()), Qt::QueuedConnection);
       
   176     connect(mHeader->bccEdit(), SIGNAL(cursorPositionChanged(int, int)), this, 
       
   177         SLOT(ensureCursorVisibility()), Qt::QueuedConnection);
       
   178     connect(mHeader->subjectEdit(), SIGNAL(cursorPositionChanged(int, int)), this, 
       
   179         SLOT(ensureCursorVisibility()), Qt::QueuedConnection);
       
   180     connect(mEditorWidget, SIGNAL(cursorPositionChanged(int, int)), this, 
       
   181         SLOT(ensureCursorVisibility()), Qt::QueuedConnection);
       
   182 
       
   183     // listen to the parent's (NmEditorView) size changes which happen eg. when VKB is opened/closed
       
   184     connect(parent(), SIGNAL(sizeChanged()), this, SLOT(ensureCursorVisibility()),
       
   185         Qt::QueuedConnection);
       
   186 
       
   187     // Listen scroll posion change signals for header reposition.
       
   188     connect(mScrollArea, SIGNAL(scrollPositionChanged(QPointF)),
       
   189        		this, SLOT(repositHeader(QPointF)));
       
   190     
       
   191     // Listen content change signal for body widget width adjustment.
       
   192     connect(mEditorWidget->document(), SIGNAL(contentsChanged()), this, 
       
   193         SLOT(setEditorContentWidth()), Qt::QueuedConnection);
       
   194 }
       
   195 
       
   196 /*!
       
   197     Return pointer to the email body text edit widget
       
   198  */
       
   199 NmEditorTextEdit *NmEditorContent::editor() const
       
   200 {
       
   201     NM_FUNCTION;
       
   202     
       
   203     return mEditorWidget;
       
   204 }
       
   205 
       
   206 /*!
       
   207     Return pointer to the header widget
       
   208  */
       
   209 NmEditorHeader *NmEditorContent::header() const
       
   210 {
       
   211     NM_FUNCTION;
       
   212     
       
   213     return mHeader;
       
   214 }
       
   215 
       
   216 /*!
       
   217     This slot is called when header widget height has been changed. Function performs
       
   218     the repositioning of the body field and resizing of the editor and content area.
       
   219  */
       
   220 void NmEditorContent::setEditorContentHeight()
       
   221 {
       
   222     NM_FUNCTION;
       
   223     
       
   224     // the height of the margin between the title bar and the header
       
   225     qreal topMargin = 0;
       
   226     HbStyle().parameter("hb-param-margin-gene-top", topMargin);
       
   227     
       
   228     // header height
       
   229     qreal headerHeight = mHeader->headerHeight();
       
   230 
       
   231     // body area editor's document height with margins added
       
   232     qreal documentHeightAndMargins = mEditorWidget->document()->size().height() +
       
   233         (mEditorWidget->document()->documentMargin() * 2);
       
   234 
       
   235     // chrome height
       
   236     qreal chromeHeight = 0;
       
   237     HbStyle().parameter("hb-param-widget-chrome-height", chromeHeight);
       
   238     
       
   239     // screen height
       
   240     qreal screenHeight = mApplication.screenSize().height();
       
   241 
       
   242     // set min size for the body area so that at least the screen area is always filled
       
   243     qreal bodyAreaMinSize = screenHeight - chromeHeight - topMargin - headerHeight;
       
   244     
       
   245     qreal bodyAreaSize = fmax(bodyAreaMinSize, documentHeightAndMargins);
       
   246 
       
   247     mScrollAreaContents->setPreferredHeight(topMargin + headerHeight + bodyAreaSize);
       
   248 }
       
   249 
       
   250 /*!
       
   251     This slot is used to set width for the body field.
       
   252     For some reason HbTextEdit does not set it's width, so we need to se it here.
       
   253     This slot can be removed if HbTextEdit is fixed.
       
   254  */
       
   255 void NmEditorContent::setEditorContentWidth()
       
   256 {
       
   257     NM_FUNCTION;
       
   258     
       
   259     if (mNeedForWidthAdjustment) {
       
   260         mNeedForWidthAdjustment = false;
       
   261         mScrollAreaContents->setPreferredWidth(mEditorWidget->document()->idealWidth());
       
   262     }
       
   263 }
       
   264 
       
   265 /*!
       
   266     This slot is called when the cursor visibility has to be ensured ie. the scroll position is 
       
   267     adjusted so that the cursor can be seen.
       
   268 */
       
   269 void NmEditorContent::ensureCursorVisibility()
       
   270 {
       
   271     NM_FUNCTION;
       
   272 
       
   273     // check which of the editors has the focus and get the x/y coordinates for the cursor position
       
   274     QGraphicsWidget *focused = mScrollAreaContents->focusWidget();
       
   275     
       
   276     if (focused) {
       
   277         QRectF localRect(0, 0, 0, 0);
       
   278         bool notFound = false;
       
   279         
       
   280         if (focused == mHeader->toEdit()) {
       
   281             localRect = mHeader->toEdit()->rectForCursorPosition();
       
   282         }
       
   283         else if (focused == mHeader->ccEdit()) {
       
   284             localRect = mHeader->ccEdit()->rectForCursorPosition();
       
   285         }
       
   286         else if (focused == mHeader->bccEdit()) {
       
   287             localRect = mHeader->bccEdit()->rectForCursorPosition();
       
   288         }
       
   289         else if (focused == mHeader->subjectEdit()) {
       
   290             localRect = mHeader->subjectEdit()->rectForCursorPosition();
       
   291         }
       
   292         else if (focused == mEditorWidget) {
       
   293             localRect = mEditorWidget->rectForCursorPosition();
       
   294         }
       
   295         else {
       
   296             notFound = true;
       
   297         }
       
   298 
       
   299         if (!notFound) {
       
   300             QPointF topLeftPos = focused->mapToItem(mScrollAreaContents, localRect.topLeft());
       
   301             QPointF bottomRightPos =
       
   302                 focused->mapToItem(mScrollAreaContents, localRect.bottomRight());
       
   303             qreal marginRight = 0;
       
   304             HbStyle().parameter("hb-param-margin-gene-right", marginRight);
       
   305             bottomRightPos.rx() += marginRight;
       
   306             mScrollArea->ensureVisible(topLeftPos);
       
   307             mScrollArea->ensureVisible(bottomRightPos);
       
   308         }
       
   309     }
       
   310 }
       
   311 /*!
       
   312     Removes embedded images from the message body
       
   313  */
       
   314 void NmEditorContent::removeEmbeddedImages(QString &bodyContent)
       
   315 {
       
   316     NM_FUNCTION;
       
   317  
       
   318     QRegExp regExp(NMUI_EDITOR_REMOVE_EMBD_IMAGES_REG, Qt::CaseInsensitive);
       
   319     bodyContent.remove(regExp);
       
   320 }
       
   321 
       
   322 /*!
       
   323     This slot is called when scroll position has been changed.
       
   324     Function create translation object which is used to set new position for
       
   325     header so that header stays visible when body is scrolled horizontally.
       
   326  */
       
   327 void NmEditorContent::repositHeader(const QPointF &scrollPosition)
       
   328 {
       
   329     NM_FUNCTION;
       
   330     
       
   331     // Get the layout's left margin
       
   332     qreal margin = 0;
       
   333     HbStyle().parameter("hb-param-margin-gene-left", margin);
       
   334     
       
   335     // Calculate header width. (Screen width minus left and right margins.
       
   336     qreal headerWidth = mApplication.screenSize().width() - margin - margin;
       
   337 
       
   338     // Create translation object for header position adjustment.
       
   339     QRectF editorBodyRect = mEditorWidget->geometry();
       
   340     QTransform tr;
       
   341     qreal leftMovementThreshold(editorBodyRect.width() - headerWidth);
       
   342     if (scrollPosition.x() < 0) {
       
   343         // Left side positioning. Allow left side baunch effect.
       
   344         tr.translate(editorBodyRect.topLeft().x() - margin ,0);
       
   345     }
       
   346     else if (scrollPosition.x() >= 0 && scrollPosition.x() < leftMovementThreshold) {
       
   347         // Middle area positioning
       
   348         tr.translate(scrollPosition.x() ,0);
       
   349     }
       
   350     else {
       
   351         // Right side positioning. Allow right side baunch effect.
       
   352         tr.translate(editorBodyRect.topLeft().x() + leftMovementThreshold - margin ,0);
       
   353     }
       
   354     // Call header to perform the translation which moves hader to new position.
       
   355     mHeader->repositHeader(tr);
       
   356 }
       
   357