75 // The following line is necessary in terms of being able to add emoticons |
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 |
76 // (smileys) to an empty document (mail content). Otherwise the private |
77 // pointer of the QTextDocument which the smiley engine has is NULL and |
77 // pointer of the QTextDocument which the smiley engine has is NULL and |
78 // inserting a smiley will lead to an error. |
78 // inserting a smiley will lead to an error. |
79 mEditorWidget->setPlainText(""); |
79 mEditorWidget->setPlainText(""); |
|
80 |
|
81 QCoreApplication::instance()->installEventFilter(this); // see eventFilter() |
80 } |
82 } |
81 |
83 |
82 /*! |
84 /*! |
83 Destructor |
85 Destructor |
84 */ |
86 */ |
85 NmEditorContent::~NmEditorContent() |
87 NmEditorContent::~NmEditorContent() |
86 { |
88 { |
87 NM_FUNCTION; |
89 NM_FUNCTION; |
|
90 |
|
91 QCoreApplication::instance()->removeEventFilter(this); // see eventFilter() |
88 } |
92 } |
89 |
93 |
90 /*! |
94 /*! |
91 Sets the body content. If reply envelopw is present, reply header is generated and set to |
95 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. |
96 editor. Reply envelope ownership is not transferred here. |
108 |
112 |
109 QTextCursor cursor(mEditorWidget->document()); |
113 QTextCursor cursor(mEditorWidget->document()); |
110 |
114 |
111 // Create the "reply" header (also for forward message) |
115 // Create the "reply" header (also for forward message) |
112 // sets the font color of the reply header and the original body text to black |
116 // sets the font color of the reply header and the original body text to black |
113 if ((editorStartMode==NmUiEditorReply || editorStartMode==NmUiEditorReplyAll || |
117 if ((editorStartMode == NmUiEditorReply || editorStartMode == NmUiEditorReplyAll || |
114 editorStartMode==NmUiEditorForward) && originalMessage) { |
118 editorStartMode == NmUiEditorForward) && originalMessage) { |
115 bodyContent.append(QString("<style type=\"text/css\">* { color: black; }</style>")); |
119 bodyContent.append(QString("<style type=\"text/css\">* { color: black; }</style>")); |
116 bodyContent.append(NmUtilities::createReplyHeader(originalMessage->envelope())); |
120 bodyContent.append(NmUtilities::createReplyHeader(originalMessage->envelope())); |
117 } |
121 } |
118 |
122 |
119 // Check which part is present. Html or plain text part. We use the original message parts. |
123 // Check which part is present. Html or plain text part. We use the original message parts. |
124 plainPart = originalMessage->plainTextBodyPart(); |
128 plainPart = originalMessage->plainTextBodyPart(); |
125 } |
129 } |
126 |
130 |
127 if (htmlPart) { |
131 if (htmlPart) { |
128 QString bodyText(htmlPart->textContent()); |
132 QString bodyText(htmlPart->textContent()); |
129 if (editorStartMode==NmUiEditorReply || editorStartMode==NmUiEditorReplyAll || |
133 if (editorStartMode == NmUiEditorReply || editorStartMode == NmUiEditorReplyAll || |
130 editorStartMode==NmUiEditorForward) { |
134 editorStartMode == NmUiEditorForward) { |
131 convertBodyStylesToDivision(bodyText); |
135 convertBodyStylesToDivision(bodyText); |
132 } |
136 } |
133 |
137 |
134 if(editorStartMode==NmUiEditorReply || editorStartMode==NmUiEditorReplyAll ) { |
138 if(editorStartMode == NmUiEditorReply || editorStartMode == NmUiEditorReplyAll ) { |
135 removeEmbeddedImages(bodyText); |
139 removeEmbeddedImages(bodyText); |
136 } |
140 } |
137 |
|
138 bodyContent.append(bodyText); |
141 bodyContent.append(bodyText); |
139 cursor.insertHtml(bodyContent); |
|
140 } |
142 } |
141 else if (plainPart) { |
143 else if (plainPart) { |
142 // Plain text part was present, set it to HbTextEdit as HTML |
144 // Plain text part was present, set it to HbTextEdit as HTML |
143 bodyContent.append(QString("<html><body><p>")); |
145 bodyContent.append(QString("<html><body><p>")); |
144 bodyContent.append(plainPart->textContent()); |
146 bodyContent.append(plainPart->textContent()); |
149 // Update of the body width is done when next contentChanged signal comes from the body. |
151 // Update of the body width is done when next contentChanged signal comes from the body. |
150 mNeedForWidthAdjustment = true; |
152 mNeedForWidthAdjustment = true; |
151 cursor.clearSelection(); |
153 cursor.clearSelection(); |
152 cursor.setPosition(0); |
154 cursor.setPosition(0); |
153 cursor.insertHtml(QString("<html><body></body></html>")); |
155 cursor.insertHtml(QString("<html><body></body></html>")); |
|
156 mEditorWidget->moveCursor(QTextCursor::Start, QTextCursor::MoveAnchor); |
|
157 QMetaObject::invokeMethod(this, "ensureCursorVisibility", Qt::QueuedConnection); |
154 } |
158 } |
155 |
159 |
156 /*! |
160 /*! |
157 This method creates all needed signal-slot connections |
161 This method creates all needed signal-slot connections |
158 */ |
162 */ |
173 Qt::QueuedConnection); |
177 Qt::QueuedConnection); |
174 |
178 |
175 // we are interested in the document's height changes |
179 // we are interested in the document's height changes |
176 connect(mEditorWidget->document()->documentLayout(), SIGNAL(documentSizeChanged(QSizeF)), this, |
180 connect(mEditorWidget->document()->documentLayout(), SIGNAL(documentSizeChanged(QSizeF)), this, |
177 SLOT(setEditorContentHeight()), Qt::QueuedConnection); |
181 SLOT(setEditorContentHeight()), Qt::QueuedConnection); |
178 |
|
179 // We need to update the scroll position according the editor's cursor position |
|
180 connect(mHeader->toEdit(), SIGNAL(cursorPositionChanged(int, int)), this, |
|
181 SLOT(ensureCursorVisibility()), Qt::QueuedConnection); |
|
182 connect(mHeader->ccEdit(), SIGNAL(cursorPositionChanged(int, int)), this, |
|
183 SLOT(ensureCursorVisibility()), Qt::QueuedConnection); |
|
184 connect(mHeader->bccEdit(), SIGNAL(cursorPositionChanged(int, int)), this, |
|
185 SLOT(ensureCursorVisibility()), Qt::QueuedConnection); |
|
186 connect(mHeader->subjectEdit(), SIGNAL(cursorPositionChanged(int, int)), this, |
|
187 SLOT(ensureCursorVisibility()), Qt::QueuedConnection); |
|
188 connect(mEditorWidget, SIGNAL(cursorPositionChanged(int, int)), this, |
|
189 SLOT(ensureCursorVisibility()), Qt::QueuedConnection); |
|
190 |
182 |
191 // listen to the parent's (NmEditorView) size changes which happen eg. when VKB is opened/closed |
183 // listen to the parent's (NmEditorView) size changes which happen eg. when VKB is opened/closed |
192 connect(parent(), SIGNAL(sizeChanged()), this, SLOT(ensureCursorVisibility()), |
184 connect(parent(), SIGNAL(sizeChanged()), this, SLOT(ensureCursorVisibility()), |
193 Qt::QueuedConnection); |
185 Qt::QueuedConnection); |
194 |
186 |
307 localRect = mEditorWidget->rectForCursorPosition(); |
300 localRect = mEditorWidget->rectForCursorPosition(); |
308 } |
301 } |
309 |
302 |
310 // ensure that the cursor position is visible |
303 // ensure that the cursor position is visible |
311 if (focused && !localRect.isEmpty()) { |
304 if (focused && !localRect.isEmpty()) { |
312 QPointF topLeftPos = focused->mapToItem(mScrollAreaContents, localRect.topLeft()); |
305 QRectF rect = focused->mapRectToItem(mScrollAreaContents, localRect); |
313 QPointF bottomRightPos = |
306 mScrollArea->ensureVisible(rect.center(), rect.width(), rect.height() / 2 ); |
314 focused->mapToItem(mScrollAreaContents, localRect.bottomRight()); |
|
315 qreal marginRight = 0; |
|
316 if (mScrollArea->style()) { |
|
317 mScrollArea->style()->parameter("hb-param-margin-gene-right", marginRight); |
|
318 } |
|
319 bottomRightPos.rx() += marginRight; |
|
320 |
|
321 mScrollArea->ensureVisible(topLeftPos); |
|
322 mScrollArea->ensureVisible(bottomRightPos); |
|
323 } |
307 } |
324 } |
308 } |
325 /*! |
309 /*! |
326 Removes embedded images from the message body |
310 Removes embedded images from the message body |
327 */ |
311 */ |
350 qreal headerWidth = mApplication.screenSize().width() - margin - margin; |
334 qreal headerWidth = mApplication.screenSize().width() - margin - margin; |
351 |
335 |
352 // Create translation object for header position adjustment. |
336 // Create translation object for header position adjustment. |
353 QRectF editorBodyRect = mEditorWidget->geometry(); |
337 QRectF editorBodyRect = mEditorWidget->geometry(); |
354 QTransform tr; |
338 QTransform tr; |
355 qreal leftMovementThreshold(editorBodyRect.width() - headerWidth); |
339 |
|
340 qreal bodyWidth = editorBodyRect.width(); |
|
341 if ( bodyWidth < mApplication.screenSize().width() ) { |
|
342 bodyWidth = mApplication.screenSize().width(); |
|
343 } |
|
344 |
|
345 qreal leftMovementThreshold( bodyWidth - headerWidth); |
|
346 |
356 if (scrollPosition.x() < 0) { |
347 if (scrollPosition.x() < 0) { |
357 // Left side positioning. Allow left side baunch effect. |
348 // Left side positioning. Allow left side baunch effect. |
358 tr.translate(editorBodyRect.topLeft().x() - margin ,0); |
349 tr.translate(editorBodyRect.topLeft().x() - margin ,0); |
359 } |
350 } |
360 else if (scrollPosition.x() >= 0 && scrollPosition.x() < leftMovementThreshold) { |
351 else if (scrollPosition.x() >= 0 && scrollPosition.x() < leftMovementThreshold) { |
432 QRegExp bodyStyleDefinedRegExp(bodyStyleDefined, Qt::CaseInsensitive); |
423 QRegExp bodyStyleDefinedRegExp(bodyStyleDefined, Qt::CaseInsensitive); |
433 bodyStyleDefinedRegExp.setMinimal(true); |
424 bodyStyleDefinedRegExp.setMinimal(true); |
434 |
425 |
435 QString bodyStartReplacement("<body>\n<div"); |
426 QString bodyStartReplacement("<body>\n<div"); |
436 |
427 |
437 if(bodyContent.contains(bodyStyleDefinedRegExp)) { |
428 if (bodyContent.contains(bodyStyleDefinedRegExp)) { |
438 QString headPartString = bodyStyleDefinedRegExp.cap(0); |
429 QString headPartString = bodyStyleDefinedRegExp.cap(0); |
439 QString headBodyStyleString("body(\\s)*"); |
430 QString headBodyStyleString("body(\\s)*"); |
440 QRegExp bodyStyleReplacementRegExp(headBodyStyleString, Qt::CaseInsensitive); |
431 QRegExp bodyStyleReplacementRegExp(headBodyStyleString, Qt::CaseInsensitive); |
441 |
432 |
442 if(headPartString.contains(bodyStyleReplacementRegExp)) { |
433 if (headPartString.contains(bodyStyleReplacementRegExp)) { |
443 headPartString.replace(bodyStyleReplacementRegExp, "div.reply "); |
434 headPartString.replace(bodyStyleReplacementRegExp, "div.reply "); |
444 bodyContent.replace(bodyStyleDefinedRegExp, headPartString); |
435 bodyContent.replace(bodyStyleDefinedRegExp, headPartString); |
445 |
436 |
446 bodyStartReplacement = "<body>\n<div class=\"reply\""; |
437 bodyStartReplacement = "<body>\n<div class=\"reply\""; |
447 } |
438 } |
495 */ |
483 */ |
496 void NmEditorContent::convertBGColorToStyle(QString &bodyContent) |
484 void NmEditorContent::convertBGColorToStyle(QString &bodyContent) |
497 { |
485 { |
498 NM_FUNCTION; |
486 NM_FUNCTION; |
499 |
487 |
500 QString bgColorInBodyFetchString("<body[^<]+(bgcolor(\\s|)=(\\s|)(\"|)(#|))*>"); |
488 QString bgColorInBodyFetchString("<body" |
|
489 "[^<]+" // 1...* any character except '<' |
|
490 "(bgcolor" |
|
491 "(\\s|)" // White space or nothing |
|
492 "=" |
|
493 "(\\s|)" // White space or nothing |
|
494 "(\"|)" // '"' or nothing |
|
495 "(#|))" // '#' or nothing |
|
496 "*>"); // 0...* Any character + > |
501 QRegExp bgColorInBodyRegExp(bgColorInBodyFetchString, Qt::CaseInsensitive); |
497 QRegExp bgColorInBodyRegExp(bgColorInBodyFetchString, Qt::CaseInsensitive); |
502 |
498 |
503 if(bodyContent.contains(bgColorInBodyRegExp)) { |
499 if (bodyContent.contains(bgColorInBodyRegExp)) { |
504 // There can be only one meaningful bgcolor, the first one. |
500 // There can be only one meaningful bgcolor, the first one. |
505 QString bgColorString = bgColorInBodyRegExp.cap(0); |
501 QString bgColorString = bgColorInBodyRegExp.cap(0); |
506 |
502 |
507 // Extract the color code from the string |
503 // Extract the color code from the string |
508 QString colorCode(bgColorString); |
504 QString colorCode(bgColorString); |
514 "=" |
510 "=" |
515 "(\\s|)" // White space or nothing |
511 "(\\s|)" // White space or nothing |
516 "(\"|)" // '"' or nothing |
512 "(\"|)" // '"' or nothing |
517 "(#|)", // '#' or nothing |
513 "(#|)", // '#' or nothing |
518 Qt::CaseInsensitive); |
514 Qt::CaseInsensitive); |
|
515 |
519 colorCode.remove(removeBeginningRegExp); |
516 colorCode.remove(removeBeginningRegExp); |
520 |
517 |
521 QRegExp removeEndRegExp("" |
518 QRegExp removeEndRegExp("" |
522 "((\"|\\s)" // '"' or white space |
519 "((\"|\\s)" // '"' or white space |
523 "([^<]*)" // 0...* any characters except '<' |
520 "([^<]*)" // 0...* any characters except '<' |
524 ">)" // '>' |
521 ">)" // '>' |
525 "|>"); // ... or nothing before this, just '>' |
522 "|>"); // ... or nothing before this, just '>' |
|
523 |
526 colorCode.remove(removeEndRegExp); |
524 colorCode.remove(removeEndRegExp); |
527 |
525 |
528 QString plainBgColorFetchString("bgcolor(\\s|)*=(\\s|)*[^\\s]+(\\s|(?=>))"); |
526 QString plainBgColorFetchString("bgcolor" |
|
527 "(\\s|)*" // 0...* White space or nothing |
|
528 "=" |
|
529 "(\\s|)*" // 0...* White space or nothing |
|
530 "[^\\s]+" // 1...* any character except white space |
|
531 "(\\s|(?=>))"); // White space or follower by '>' |
|
532 |
529 QString bgColorReplacement("style=\"background: #"+colorCode+"\" "); |
533 QString bgColorReplacement("style=\"background: #"+colorCode+"\" "); |
530 QRegExp plainBgColorFetchRegExp(plainBgColorFetchString, Qt::CaseInsensitive); |
534 QRegExp plainBgColorFetchRegExp(plainBgColorFetchString, Qt::CaseInsensitive); |
531 |
535 |
532 // Create temporary string for bgcolor |
536 // Create temporary string for bgcolor |
533 bgColorString.replace(plainBgColorFetchRegExp, bgColorReplacement); |
537 bgColorString.replace(plainBgColorFetchRegExp, bgColorReplacement); |
534 |
538 |
535 // Copy the contents of the temporary string to the message |
539 // Copy the contents of the temporary string to the message |
536 bodyContent.replace(bgColorInBodyRegExp, bgColorString); |
540 bodyContent.replace(bgColorInBodyRegExp, bgColorString); |
537 } |
541 } |
538 } |
542 } |
|
543 |
|
544 /*! |
|
545 * Listen to the input method events (eg. cursor position changes, preedit text changes etc.) and |
|
546 * invoke ensureCursorVisibility. Qt FW does not automatically ensure the visiblity of a text in a |
|
547 * edit widget in our custom scroll area. Filtering events is the only way to get events of the |
|
548 * preedit text changes. |
|
549 */ |
|
550 bool NmEditorContent::eventFilter(QObject *obj, QEvent *event) |
|
551 { |
|
552 // let the FW handle the event first |
|
553 bool ret = QObject::eventFilter(obj, event); |
|
554 |
|
555 if (event && event->type() == QEvent::InputMethod) { |
|
556 QMetaObject::invokeMethod(this, "ensureCursorVisibility", Qt::QueuedConnection); |
|
557 } |
|
558 |
|
559 return ret; |
|
560 } |
|
561 |