emailuis/nmailui/src/nmeditorcontent.cpp
changeset 54 997a02608b3a
parent 53 bf7eb7911fc5
child 59 16ed8d08d0b1
--- a/emailuis/nmailui/src/nmeditorcontent.cpp	Wed Jun 23 18:00:21 2010 +0300
+++ b/emailuis/nmailui/src/nmeditorcontent.cpp	Tue Jul 06 14:04:34 2010 +0300
@@ -19,26 +19,32 @@
 
 // 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(QGraphicsItem *parent,
-                                 NmEditorView *parentView,
+NmEditorContent::NmEditorContent(QObject *parent,
                                  HbDocumentLoader *documentLoader,
-                                 QNetworkAccessManager &manager) :
-    HbWidget(parent),
-    mHeaderWidget(NULL),
-    mParentView(parentView),
-    mEditorLayout(NULL),
-    mMessageBodyType(PlainText),
+                                 QNetworkAccessManager &manager,
+                                 NmApplication &application) :
+    QObject(parent),
+    mHeader(NULL),
+    mMessageBodyType(NmPlainText),
     mEditorWidget(NULL),
-    mBackgroundScrollArea((NmBaseViewScrollArea*)parent)
+    mScrollArea(NULL),
+    mScrollAreaContents(NULL),
+    mApplication(application)
 {
     NM_FUNCTION;
 
-    // Add header area handling widget into layout
-    mHeaderWidget = new NmEditorHeader(documentLoader);
+    // 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));
@@ -48,14 +54,16 @@
     mEditorWidget->setDocument(textDocument); 
     textDocument->setParent(mEditorWidget); // ownership changes
 
-    mEditorWidget->init(mBackgroundScrollArea);
-    // we are interested in the editor widget's height changes
-    connect(mEditorWidget, SIGNAL(editorContentHeightChanged()), this,
-        SLOT(setEditorContentHeight()));
-
+    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();
 }
@@ -66,8 +74,6 @@
 NmEditorContent::~NmEditorContent()
 {
     NM_FUNCTION;
-    
-   delete mHeaderWidget;
 }
 
 /*!
@@ -75,88 +81,83 @@
     present, reply header is generated and set to editor. Reply
     envelope ownership is not transferred here.
  */
-void NmEditorContent::setMessageData(const NmMessage &originalMessage,
-                                     bool createReplyHeader)
+void NmEditorContent::setMessageData(const NmMessage &originalMessage, 
+                                     NmUiEditorStartMode &editorStartMode)
 {
     NM_FUNCTION;
     
     QString bodyContent;
     
-    // We create the "reply" header (also for forward message), but not to draft message.
-    if (mEditorWidget && createReplyHeader) {          
-        QTextCursor cursor = mEditorWidget->textCursor();
-        cursor.setPosition(0);
-        cursor.insertHtml(NmUtilities::createReplyHeader(originalMessage.envelope()));
+    // Create the "reply" header (also for forward message)
+    if (editorStartMode==NmUiEditorReply || editorStartMode==NmUiEditorReplyAll || 
+        editorStartMode==NmUiEditorForward) {
+        bodyContent.append(NmUtilities::createReplyHeader(originalMessage.envelope()));
     }
-    // Take reply header as html format.
-    bodyContent.append(mEditorWidget->toHtml());
     
     // 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 && mEditorWidget) {
+    if (htmlPart) {
         bodyContent.append(htmlPart->textContent());
+        if(editorStartMode==NmUiEditorReply || editorStartMode==NmUiEditorReplyAll ) {
+            removeEmbeddedImages(bodyContent);
+        }
         emit setHtml(bodyContent);
-        mMessageBodyType = HTMLText;
+        mMessageBodyType = NmHTMLText;
     }
     else if (plainPart) {
         // Plain text part was present, set it to HbTextEdit
         bodyContent.append(plainPart->textContent());
         emit setPlainText(bodyContent);
-        mMessageBodyType = PlainText;
+        mMessageBodyType = NmPlainText;
     }
 }  
 
 /*!
-   This method set new height for the editor content when header or body field
-   height has been changed.
- */
-void NmEditorContent::setEditorContentHeight()
-{
-    NM_FUNCTION;
-    
-	const QSizeF reso = HbDeviceProfile::current().logicalSize();
-    qreal containerHeight = mEditorWidget->contentHeight() + mHeaderWidget->headerHeight();
-    if (containerHeight < reso.height()) {
-        //Currently content height is too long because Chrome hiding is not supported.
-        //Fix this when Chrome works.
-        containerHeight = reso.height();
-        qreal bodyContentHeight =
-            reso.height() - mHeaderWidget->headerHeight();
-        mEditorWidget->setPreferredHeight(bodyContentHeight);
-        mEditorWidget->setMaximumHeight(bodyContentHeight);
-    }
-    mParentView->scrollAreaContents()->setMinimumHeight(containerHeight);
-    mParentView->scrollAreaContents()->setMaximumHeight(containerHeight);
-    mBackgroundScrollArea->setMaximumHeight(containerHeight);
-}
-
-/*!
    This method creates all needed signal-slot connections
  */
 void NmEditorContent::createConnections()
 {
     NM_FUNCTION;
     
-    // Body edit widget is also interested about bg scroll position change
-    connect(mBackgroundScrollArea, SIGNAL(scrollPositionChanged(QPointF)),
-            mEditorWidget, SLOT(updateScrollPosition(QPointF)));
     // 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(mHeaderWidget, SIGNAL(headerHeightChanged(int)),
-            mEditorWidget, SLOT(setHeaderHeight(int)));
+    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
+NmEditorTextEdit *NmEditorContent::editor() const
 {
     NM_FUNCTION;
     
@@ -166,10 +167,100 @@
 /*!
     Return pointer to the header widget
  */
-NmEditorHeader* NmEditorContent::header() const
+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;
     
-    return mHeaderWidget;
+    // 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);
+}