|
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: Mail viewer implementation. |
|
15 * |
|
16 */ |
|
17 |
|
18 #include "nmuiheaders.h" |
|
19 |
|
20 static const char *NMUI_MESSAGE_VIEWER_XML = ":/docml/nmmailviewer.docml"; |
|
21 static const char *NMUI_MESSAGE_VIEWER_VIEW = "nmailViewerView"; |
|
22 static const char *NMUI_MESSAGE_VIEWER_CONTENT = "content"; |
|
23 static const char *NMUI_MESSAGE_VIEWER_SCROLL_AREA = "viewerScrollArea"; |
|
24 static const char *NMUI_MESSAGE_VIEWER_SCROLL_AREA_CONTENTS = "viewerScrollAreaContents"; |
|
25 static const char *NMUI_MESSAGE_VIEWER_HEADER = "viewerHeader"; |
|
26 static const char *NMUI_MESSAGE_VIEWER_ATTALIST = "viewerAttaList"; |
|
27 static const char *NMUI_MESSAGE_VIEWER_SCROLL_WEB_VIEW = "viewerWebView"; |
|
28 static const int NmOrientationTimer = 100; |
|
29 static const int NmHeaderMargin = 3; |
|
30 static const int NmWhitePixmapSize = 10; |
|
31 static const int NmProgressValueComplete = 100; |
|
32 static const QString NmParamTextHeightSecondary = "hb-param-text-height-secondary"; |
|
33 static const QString NmHttpLinkScheme = "http"; |
|
34 static const QString NmHttpsLinkScheme = "https"; |
|
35 static const QString NmMailtoLinkScheme = "mailto"; |
|
36 |
|
37 // Local constants |
|
38 const qreal NmZoomFactor = 1.5; |
|
39 |
|
40 /*! |
|
41 \class NmViewerView |
|
42 \brief Mail viewer class |
|
43 */ |
|
44 |
|
45 /*! |
|
46 Constructor |
|
47 */ |
|
48 NmViewerView::NmViewerView( |
|
49 NmApplication &application, |
|
50 NmUiStartParam* startParam, |
|
51 NmUiEngine &uiEngine, |
|
52 HbMainWindow *mainWindow, |
|
53 NmAttachmentManager &attaManager, |
|
54 bool toolbarEnabled, |
|
55 QGraphicsItem *parent) |
|
56 :NmBaseView(startParam, application, parent), |
|
57 mApplication(application), |
|
58 mUiEngine(uiEngine), |
|
59 mMainWindow(mainWindow), |
|
60 mAttaManager(attaManager), |
|
61 mToolbarEnabled(toolbarEnabled), |
|
62 mMessage(NULL), |
|
63 mScrollArea(NULL), |
|
64 mViewerContent(NULL), |
|
65 mWebView(NULL), |
|
66 mHeaderWidget(NULL), |
|
67 mMessageFetchingOperation(NULL), |
|
68 mDisplayingPlainText(false), |
|
69 mDocumentLoader(NULL), |
|
70 mScrollAreaContents(NULL), |
|
71 mScreenSize(QSize(0,0)), |
|
72 mWaitDialog(NULL), |
|
73 webFrameloadingCompleted(false), |
|
74 mLatestLoadingSize(QSize(0,0)), |
|
75 mAttaIndexUnderFetch(NmNotFoundError), |
|
76 mAttaWidget(NULL), |
|
77 mViewReady(false), |
|
78 mWaitNoteCancelled(false), |
|
79 mErrorNote(NULL) |
|
80 { |
|
81 // Create documentloader |
|
82 mDocumentLoader = new NmUiDocumentLoader(mMainWindow); |
|
83 // Get screensize |
|
84 mScreenSize = mApplication.screenSize(); |
|
85 // Connect external delete handling to uiengine signal |
|
86 connect(&mUiEngine, |
|
87 SIGNAL(messageDeleted(const NmId &, const NmId &, const NmId &)), |
|
88 this, SLOT(messageDeleted(const NmId &, const NmId &, const NmId &)), |
|
89 Qt::UniqueConnection); |
|
90 // Fetch message |
|
91 loadMessage(); |
|
92 // Load view layout |
|
93 loadViewLayout(); |
|
94 } |
|
95 |
|
96 /*! |
|
97 Destructor |
|
98 */ |
|
99 NmViewerView::~NmViewerView() |
|
100 { |
|
101 delete mErrorNote; |
|
102 mErrorNote=NULL; |
|
103 delete mWebView; |
|
104 mWebView = NULL; |
|
105 delete mMessage; |
|
106 mMessage = NULL; |
|
107 delete mDocumentLoader; |
|
108 mDocumentLoader = NULL; |
|
109 mWidgetList.clear(); |
|
110 delete mWaitDialog; |
|
111 mWaitDialog = NULL; |
|
112 // remove view from osbserving atta manager events |
|
113 mAttaManager.clearObserver(); |
|
114 mAttaManager.cancelFetch(); |
|
115 } |
|
116 |
|
117 /*! |
|
118 View is about to exit |
|
119 */ |
|
120 void NmViewerView::aboutToExitView() |
|
121 { |
|
122 // View is about to exit, for safety, stop |
|
123 // loading of content before closing the view |
|
124 if (mWebView){ |
|
125 mAttaManager.cancelFetch(); |
|
126 mWebView->stop(); |
|
127 if (mWebView->page()){ |
|
128 mWebView->page()->deleteLater(); |
|
129 } |
|
130 } |
|
131 } |
|
132 |
|
133 /*! |
|
134 View layout loading from XML |
|
135 */ |
|
136 void NmViewerView::loadViewLayout() |
|
137 { |
|
138 NM_FUNCTION; |
|
139 |
|
140 // Use document loader to load the view |
|
141 bool ok(false); |
|
142 setObjectName(QString(NMUI_MESSAGE_VIEWER_VIEW)); |
|
143 QObjectList objectList; |
|
144 objectList.append(this); |
|
145 // Pass the view to documentloader. Document loader uses this view |
|
146 // when docml is parsed, instead of creating new view. |
|
147 // documentloader is created in constructor |
|
148 mDocumentLoader->setObjectTree(objectList); |
|
149 mWidgetList = mDocumentLoader->load(NMUI_MESSAGE_VIEWER_XML, &ok); |
|
150 |
|
151 if (ok) |
|
152 { |
|
153 // Create content and content layout |
|
154 // qobject_cast not work in this case, using reinterpret_cast |
|
155 mViewerContent = reinterpret_cast<HbWidget *>( |
|
156 mDocumentLoader->findObject(NMUI_MESSAGE_VIEWER_CONTENT)); |
|
157 // Find scroll area |
|
158 mScrollArea = reinterpret_cast<HbScrollArea *>( |
|
159 mDocumentLoader->findObject(NMUI_MESSAGE_VIEWER_SCROLL_AREA)); |
|
160 if (mScrollArea) { |
|
161 mScrollArea->setParentItem(this); |
|
162 mScrollArea->setScrollDirections(Qt::Vertical | Qt::Horizontal); |
|
163 connect(mScrollArea, SIGNAL(scrollPositionChanged(QPointF)), |
|
164 this, SLOT(contentScrollPositionChanged(QPointF))); |
|
165 |
|
166 // Get scroll area contents and set layout margins |
|
167 mScrollAreaContents = qobject_cast<HbWidget *>( |
|
168 mDocumentLoader->findObject(NMUI_MESSAGE_VIEWER_SCROLL_AREA_CONTENTS)); |
|
169 if (mScrollAreaContents) { |
|
170 QGraphicsLayout *layout = mScrollAreaContents->layout(); |
|
171 if (layout){ |
|
172 layout->setContentsMargins(0,0,0,0); |
|
173 } |
|
174 // Set white pixmap to backgrounditem |
|
175 QPixmap whitePixmap(NmWhitePixmapSize,NmWhitePixmapSize); |
|
176 whitePixmap.fill(Qt::white); |
|
177 QGraphicsPixmapItem *pixmapItem = new QGraphicsPixmapItem(whitePixmap); |
|
178 mScrollAreaContents->setBackgroundItem(pixmapItem); |
|
179 } |
|
180 |
|
181 // Load headerwidget |
|
182 mHeaderWidget = qobject_cast<NmViewerHeader *>( |
|
183 mDocumentLoader->findObject(NMUI_MESSAGE_VIEWER_HEADER)); |
|
184 if (mHeaderWidget) { |
|
185 mHeaderWidget->setView(this); |
|
186 mHeaderWidget->rescaleHeader(mScreenSize); |
|
187 mHeaderWidget->setMessage(mMessage); |
|
188 QPointF headerStartPos = mHeaderWidget->scenePos(); |
|
189 mHeaderStartScenePos = QPointF(0,headerStartPos.y()); |
|
190 } |
|
191 |
|
192 // Load webview |
|
193 mWebView = reinterpret_cast<NmMailViewerWK *>( |
|
194 mDocumentLoader->findObject(QString(NMUI_MESSAGE_VIEWER_SCROLL_WEB_VIEW))); |
|
195 if (mWebView) { |
|
196 // Set auto load images and private browsing(no history) attributes |
|
197 QWebSettings *settings = mWebView->settings(); |
|
198 if (settings) { |
|
199 settings->setAttribute(QWebSettings::AutoLoadImages, true); |
|
200 settings->setAttribute(QWebSettings::PrivateBrowsingEnabled, true); |
|
201 } |
|
202 QWebPage *page = mWebView->page(); |
|
203 if (page) { |
|
204 QWebFrame *frame = page->mainFrame(); |
|
205 if (frame) { |
|
206 frame->setScrollBarPolicy(Qt::Vertical,Qt::ScrollBarAlwaysOff); |
|
207 frame->setScrollBarPolicy(Qt::Horizontal, Qt::ScrollBarAlwaysOff); |
|
208 frame->setTextSizeMultiplier(NmZoomFactor); |
|
209 connect(mWebView->page()->mainFrame(), |
|
210 SIGNAL(contentsSizeChanged(const QSize&)), |
|
211 this, SLOT(scaleWebViewWhenLoading(const QSize&))); |
|
212 } |
|
213 } |
|
214 } |
|
215 } |
|
216 } |
|
217 } |
|
218 |
|
219 /*! |
|
220 Lazy loading when view layout has been loaded |
|
221 */ |
|
222 void NmViewerView::viewReady() |
|
223 { |
|
224 if (!mViewReady){ |
|
225 // Set mailbox name to title |
|
226 setMailboxName(); |
|
227 // Create toolbar if needed |
|
228 if (mToolbarEnabled) { |
|
229 createToolBar(); |
|
230 } else { |
|
231 // Connect options menu about to show to create options menu function |
|
232 QObject::connect(menu(), SIGNAL(aboutToShow()), |
|
233 this, SLOT(createOptionsMenu())); |
|
234 // Menu needs one dummy item so that aboutToShow signal is emitted. |
|
235 NmAction *dummy = new NmAction(0); |
|
236 menu()->addAction(dummy); |
|
237 } |
|
238 |
|
239 if (mHeaderWidget) { |
|
240 QPointF contentWidgetPos = mScrollArea->pos(); |
|
241 qreal headerHeight = mHeaderWidget->geometry().height(); |
|
242 if (mMainWindow->orientation() == Qt::Horizontal) { |
|
243 const QPointF pointToWebView(contentWidgetPos.x(), headerHeight+NmHeaderMargin); |
|
244 mScrollArea->scrollContentsTo(pointToWebView,0); |
|
245 } |
|
246 } |
|
247 |
|
248 // Run fetchmessage in queue |
|
249 QMetaObject::invokeMethod(this, "fetchMessage", Qt::QueuedConnection); |
|
250 // Set view ready |
|
251 mViewReady = true; |
|
252 } |
|
253 } |
|
254 |
|
255 /*! |
|
256 Function fecthes message data based on parameters |
|
257 */ |
|
258 void NmViewerView::loadMessage() |
|
259 { |
|
260 NM_FUNCTION; |
|
261 |
|
262 if (mMessage) { |
|
263 delete mMessage; |
|
264 mMessage = NULL; |
|
265 } |
|
266 // Read start params and message object |
|
267 if (mStartParam){ |
|
268 NmId mailboxId = mStartParam->mailboxId(); |
|
269 NmId folderId = mStartParam->folderId(); |
|
270 NmId msgId = mStartParam->messageId(); |
|
271 mMessage = mUiEngine.message(mailboxId, folderId, msgId); |
|
272 } |
|
273 } |
|
274 |
|
275 /*! |
|
276 Function fecthes message data based on parameters. Returns false if message is available, |
|
277 true if message have to be fetched |
|
278 */ |
|
279 void NmViewerView::fetchMessage() |
|
280 { |
|
281 NM_FUNCTION; |
|
282 |
|
283 if (mMessage) { |
|
284 NmId mailboxId = mStartParam->mailboxId(); |
|
285 NmId folderId = mStartParam->folderId(); |
|
286 NmId msgId = mStartParam->messageId(); |
|
287 const NmMessagePart *body = mMessage->htmlBodyPart(); |
|
288 if (!body) { |
|
289 // try plain to plain text part |
|
290 body = mMessage->plainTextBodyPart(); |
|
291 } |
|
292 // try to fetch if body missing or fetched size is smaller than content size |
|
293 // if body missing it might mean that only header is fetched or message has no body |
|
294 if (!body || (body && (body->fetchedSize() < body->size()))) { |
|
295 // start fetching operation |
|
296 if (mMessageFetchingOperation && mMessageFetchingOperation->isRunning()) { |
|
297 mMessageFetchingOperation->cancelOperation(); |
|
298 } |
|
299 mMessageFetchingOperation = mUiEngine.fetchMessage(mailboxId, folderId, msgId); |
|
300 |
|
301 if (mMessageFetchingOperation) { |
|
302 connect(mMessageFetchingOperation, |
|
303 SIGNAL(operationCompleted(int)), |
|
304 this, |
|
305 SLOT(messageFetched(int))); |
|
306 createAndShowWaitDialog(); |
|
307 } |
|
308 } else { |
|
309 // message is fetched |
|
310 setMessageData(); |
|
311 } |
|
312 } |
|
313 } |
|
314 |
|
315 /*! |
|
316 This is signalled by mMessageFetchingOperation when the original message is fetched. |
|
317 */ |
|
318 void NmViewerView::messageFetched(int result) |
|
319 { |
|
320 mWaitDialog->close(); |
|
321 disconnect(mWaitDialog->mainWindow(), SIGNAL(orientationChanged(Qt::Orientation)), |
|
322 this, SLOT(orientationChanged(Qt::Orientation))); |
|
323 |
|
324 if (result == NmNoError && mMessageFetchingOperation) { |
|
325 if (mMessage) { |
|
326 delete mMessage; |
|
327 mMessage = NULL; |
|
328 } |
|
329 // Read start params and message object |
|
330 if (mStartParam) { |
|
331 NmId mailboxId = mStartParam->mailboxId(); |
|
332 NmId folderId = mStartParam->folderId(); |
|
333 NmId msgId = mStartParam->messageId(); |
|
334 mMessage = mUiEngine.message(mailboxId, folderId, msgId); |
|
335 } |
|
336 setMessageData(); |
|
337 // Update header message data |
|
338 if (mHeaderWidget){ |
|
339 mHeaderWidget->updateMessageData(mMessage); |
|
340 } |
|
341 } |
|
342 } |
|
343 |
|
344 |
|
345 /*! |
|
346 This is signalled by mWaitDialog when the note is cancelled |
|
347 */ |
|
348 void NmViewerView::waitNoteCancelled() |
|
349 { |
|
350 if (!mWaitNoteCancelled) { |
|
351 if (mMessageFetchingOperation && mMessageFetchingOperation->isRunning()) { |
|
352 mMessageFetchingOperation->cancelOperation(); |
|
353 } |
|
354 mWaitNoteCancelled = true; |
|
355 QMetaObject::invokeMethod(&mApplication, "prepareForPopView", Qt::QueuedConnection); |
|
356 } |
|
357 } |
|
358 |
|
359 |
|
360 /*! |
|
361 Function sets message data to web view and header |
|
362 */ |
|
363 void NmViewerView::setMessageData() |
|
364 { |
|
365 NM_FUNCTION; |
|
366 |
|
367 // Connect to observe orientation change events |
|
368 connect(mApplication.mainWindow(), SIGNAL(orientationChanged(Qt::Orientation)), |
|
369 this, SLOT(orientationChanged(Qt::Orientation))); |
|
370 |
|
371 // Set page parameters |
|
372 QWebPage *page(NULL); |
|
373 if (mWebView){ |
|
374 page = mWebView->page(); |
|
375 } |
|
376 if (page){ |
|
377 // Set custom network access manager for embedded image handling |
|
378 NmViewerViewNetManager &netMngr = mApplication.networkAccessManager(); |
|
379 netMngr.setView(this); |
|
380 page->setNetworkAccessManager(&netMngr); |
|
381 QWebSettings *webSettings = page->settings(); |
|
382 if (webSettings) { |
|
383 webSettings->setObjectCacheCapacities(0,0,0); |
|
384 } |
|
385 |
|
386 connect(page, SIGNAL(loadFinished(bool)), |
|
387 this, SLOT(webFrameLoaded(bool))); |
|
388 page->setLinkDelegationPolicy(QWebPage::DelegateAllLinks); |
|
389 page->setContentEditable(false); |
|
390 } |
|
391 |
|
392 // if everything is ok, set message to html viewer |
|
393 if (mMessage && mWebView && page) { |
|
394 // Set initial size of component and content before loading data |
|
395 mWebView->setPreferredWidth(mScreenSize.width()); |
|
396 QRectF myGeometry = geometry(); |
|
397 page->setViewportSize(myGeometry.size().toSize()); |
|
398 //Set message data to html viewer. |
|
399 mWebView->setHtml(formatMessage()); |
|
400 // Connect to link clicked |
|
401 QObject::connect(page, SIGNAL(linkClicked(const QUrl&)), |
|
402 this, SLOT(linkClicked(const QUrl&))); |
|
403 changeMessageReadStatus(true); |
|
404 setAttachmentList(); |
|
405 } |
|
406 } |
|
407 |
|
408 /*! |
|
409 |
|
410 */ |
|
411 void NmViewerView::setAttachmentList() |
|
412 { |
|
413 NM_FUNCTION; |
|
414 |
|
415 // Load headerwidget |
|
416 mAttaWidget = qobject_cast<NmAttachmentListWidget *>( |
|
417 mDocumentLoader->findObject(NMUI_MESSAGE_VIEWER_ATTALIST)); |
|
418 if (mMessage && mAttaWidget) { |
|
419 // connect atta widget to listen progress value events |
|
420 QObject::connect(this, SIGNAL(progressValueChanged(int, int)), |
|
421 mAttaWidget, SLOT(setProgressBarValue(int, int))); |
|
422 // Set atta widget text color black since header has static white bg. |
|
423 mAttaWidget->setTextColor(Qt::black); |
|
424 // Set attawidget minimum & maximum size |
|
425 mAttaWidget->setMinimumWidth(mScreenSize.width()); |
|
426 mAttaWidget->setMaximumWidth(mScreenSize.width()); |
|
427 bool inserted(false); |
|
428 QList<NmMessagePart*> messageParts; |
|
429 mMessage->attachmentList(messageParts); |
|
430 for (int i = 0; i < messageParts.count();i++) { |
|
431 NmMessagePart *part = messageParts[i]; |
|
432 if (part && |
|
433 ((part->contentDisposition().trimmed().startsWith("inline", Qt::CaseInsensitive)==false) || |
|
434 (part->contentType().trimmed().startsWith("image", Qt::CaseInsensitive)==false))) { |
|
435 QString fileName = part->attachmentName(); |
|
436 // index starts from zero, next index is same as count |
|
437 int attaIndex = mAttaWidget->count(); |
|
438 mAttaWidget->insertAttachment(attaIndex, fileName, |
|
439 NmUtilities::attachmentSizeString(part->size())); |
|
440 mAttaIdList.insert(attaIndex, part->partId()); |
|
441 // check is part under fetch, add progress value to attachment widget |
|
442 if (mAttaManager.isFetching() && |
|
443 mAttaManager.partIdUnderFetch() == part->partId()) { |
|
444 mAttaIndexUnderFetch = attaIndex; |
|
445 } |
|
446 inserted = true; |
|
447 } |
|
448 } |
|
449 if (inserted) { |
|
450 QObject::connect(mAttaWidget, SIGNAL(itemActivated(int)), |
|
451 this, SLOT(openAttachment(int))); |
|
452 } |
|
453 else { |
|
454 // No attachments, set height to 0 |
|
455 mAttaWidget->setMinimumHeight(0); |
|
456 mAttaWidget->setMaximumHeight(0); |
|
457 } |
|
458 // set this as a observer for attachment manager |
|
459 mAttaManager.setObserver(this); |
|
460 } |
|
461 } |
|
462 |
|
463 /*! |
|
464 |
|
465 */ |
|
466 void NmViewerView::openAttachment(int index) |
|
467 { |
|
468 NM_FUNCTION; |
|
469 if (index >= 0) { |
|
470 NmId attaId = mAttaIdList.at(index); |
|
471 // reload message to get updates part sizes |
|
472 loadMessage(); |
|
473 QList<NmMessagePart*> messageParts; |
|
474 if (mMessage) { |
|
475 mMessage->attachmentList(messageParts); |
|
476 NmId mailboxId = mMessage->envelope().mailboxId(); |
|
477 NmId folderId = mMessage->envelope().folderId(); |
|
478 NmId messageId = mMessage->envelope().messageId(); |
|
479 for (int i = 0; i < messageParts.count(); i++) { |
|
480 // message part found have to found |
|
481 // and its fetched size is smaller than size, then start part fetch |
|
482 if (messageParts[i]->partId() == attaId && |
|
483 messageParts[i]->size() > messageParts[i]->fetchedSize()) { |
|
484 // do not start if there's already ongoing fetch |
|
485 if (mAttaIndexUnderFetch == NmNotFoundError) { |
|
486 mAttaIndexUnderFetch = index; |
|
487 mAttaManager.fetchAttachment(mailboxId, folderId, |
|
488 messageId, attaId); |
|
489 } |
|
490 } |
|
491 // attachment is fetched, open file |
|
492 else if (messageParts[i]->partId() == attaId) { |
|
493 mAttaManager.cancelFetch(); |
|
494 XQSharableFile file = mUiEngine.messagePartFile(mailboxId, folderId, |
|
495 messageId, attaId); |
|
496 int error = NmUtilities::openFile(file); |
|
497 file.close(); |
|
498 if (error==NmNotFoundError){ |
|
499 delete mErrorNote; |
|
500 mErrorNote=NULL; |
|
501 mErrorNote = NmUtilities::displayWarningNote( |
|
502 hbTrId("txt_mail_dialog_unable_to_open_attachment_file_ty")); |
|
503 } |
|
504 } |
|
505 } |
|
506 } |
|
507 } |
|
508 } |
|
509 |
|
510 /*! |
|
511 Function formats message based on actual data |
|
512 */ |
|
513 QString NmViewerView::formatMessage() |
|
514 { |
|
515 NM_FUNCTION; |
|
516 |
|
517 QString msg = ""; |
|
518 if (mMessage) { |
|
519 NmMessagePart *html = mMessage->htmlBodyPart(); |
|
520 if (html) { |
|
521 msg += formatHtmlMessage(html); |
|
522 } |
|
523 else { |
|
524 NmMessagePart *plain = mMessage->plainTextBodyPart(); |
|
525 if (plain) { |
|
526 msg += formatPlainTextMessage(plain); |
|
527 } |
|
528 } |
|
529 } |
|
530 return msg; |
|
531 } |
|
532 |
|
533 /*! |
|
534 Function formats html message |
|
535 */ |
|
536 QString NmViewerView::formatHtmlMessage(NmMessagePart *html) |
|
537 { |
|
538 NM_FUNCTION; |
|
539 |
|
540 QString msg = ""; |
|
541 if (html && mMessage) { |
|
542 NmId mailboxId = mMessage->envelope().mailboxId(); |
|
543 NmId folderId = mMessage->envelope().folderId(); |
|
544 NmId messageId = mMessage->envelope().messageId(); |
|
545 QList<NmMessagePart*> parts; |
|
546 mMessage->attachmentList(parts); |
|
547 for (int i=0; i < parts.count(); i++) { |
|
548 NmMessagePart *child = parts[i]; |
|
549 // Browse through embedded image parts and add those |
|
550 // the web view. |
|
551 bool isFetched = child->fetchedSize() >= child->size(); |
|
552 if (child->contentType().startsWith("image", Qt::CaseInsensitive)) { |
|
553 QString contentId = child->contentId(); |
|
554 if (isFetched) { |
|
555 int ret = mUiEngine.contentToMessagePart( |
|
556 mailboxId, folderId, messageId, *child); |
|
557 if (ret == NmNoError) { |
|
558 mWebView->addContent(contentId, QVariant::fromValue(child->binaryContent()), |
|
559 child->partId(), isFetched); |
|
560 } |
|
561 } |
|
562 else { |
|
563 mWebView->addContent(contentId, QVariant::fromValue(QByteArray()), |
|
564 child->partId(), isFetched); |
|
565 } |
|
566 } |
|
567 } |
|
568 int ret = mUiEngine.contentToMessagePart(mailboxId, folderId, messageId, *html); |
|
569 if (ret == NmNoError) { |
|
570 msg = html->textContent(); |
|
571 } |
|
572 } |
|
573 return msg; |
|
574 } |
|
575 |
|
576 /*! |
|
577 Function formats plain text message message |
|
578 */ |
|
579 QString NmViewerView::formatPlainTextMessage(NmMessagePart *plain) |
|
580 { |
|
581 NM_FUNCTION; |
|
582 |
|
583 QString msg = ""; |
|
584 if (plain && mMessage) { |
|
585 NmId mailboxId = mMessage->envelope().mailboxId(); |
|
586 NmId folderId = mMessage->envelope().folderId(); |
|
587 NmId messageId = mMessage->envelope().messageId(); |
|
588 int ret = mUiEngine.contentToMessagePart(mailboxId, folderId, |
|
589 messageId, *plain); |
|
590 if (ret == NmNoError) { |
|
591 QTextDocument document; |
|
592 // set font |
|
593 QFont currentFont = document.defaultFont(); |
|
594 currentFont.setWeight(QFont::Normal); |
|
595 qreal secondarySize; |
|
596 HbStyle myStyle; |
|
597 bool found = myStyle.parameter(NmParamTextHeightSecondary, secondarySize); |
|
598 if (found) { |
|
599 HbFontSpec fontSpec(HbFontSpec::Secondary); |
|
600 fontSpec.setTextHeight(secondarySize); |
|
601 currentFont.setPixelSize(fontSpec.font().pixelSize()); |
|
602 } |
|
603 document.setDefaultFont(currentFont); |
|
604 // convert to html |
|
605 document.setPlainText(plain->textContent()); |
|
606 msg = document.toHtml(); |
|
607 |
|
608 if (qApp->layoutDirection()==Qt::RightToLeft){ |
|
609 // add right alignment to document css section |
|
610 QRegExp rx("(<style type=\"text/css\">)(.+)(</style>)", Qt::CaseInsensitive); |
|
611 rx.setMinimal(true); |
|
612 int pos = rx.indexIn(msg); |
|
613 if (pos > -1) { |
|
614 QString newStr = rx.cap(1); |
|
615 newStr.append(rx.cap(2)); |
|
616 newStr.append("p { text-align: right } "); |
|
617 newStr.append(rx.cap(3)); |
|
618 msg.replace(rx, newStr); |
|
619 } |
|
620 } |
|
621 } |
|
622 } |
|
623 mDisplayingPlainText=true; |
|
624 return msg; |
|
625 } |
|
626 |
|
627 /*! |
|
628 Reload view contents with new start parameters |
|
629 Typically when view is already open and external view activation occurs |
|
630 for this same view |
|
631 */ |
|
632 void NmViewerView::reloadViewContents(NmUiStartParam* startParam) |
|
633 { |
|
634 // Check start parameter validity, message view cannot |
|
635 // be updated if given parameter is zero. |
|
636 if (startParam && startParam->viewId() == NmUiViewMessageViewer && |
|
637 startParam->messageId()!= 0) { |
|
638 // Delete existing start parameter data |
|
639 delete mStartParam; |
|
640 mStartParam = NULL; |
|
641 // Store new start parameter data |
|
642 mStartParam = startParam; |
|
643 // Reload viewer with new message information |
|
644 setMessageData(); |
|
645 } |
|
646 else { |
|
647 NMLOG("nmailui: Invalid viewer start parameter"); |
|
648 // Unused start parameter needs to be deleted |
|
649 delete startParam; |
|
650 } |
|
651 } |
|
652 |
|
653 /*! |
|
654 nmailViewId |
|
655 */ |
|
656 NmUiViewId NmViewerView::nmailViewId() const |
|
657 { |
|
658 return NmUiViewMessageViewer; |
|
659 } |
|
660 |
|
661 /*! |
|
662 Scale web view width |
|
663 */ |
|
664 void NmViewerView::webFrameLoaded(bool loaded) |
|
665 { |
|
666 if (loaded){ |
|
667 webFrameloadingCompleted = true; |
|
668 // Scale web view after loading the |
|
669 // complete contents, including images |
|
670 QMetaObject::invokeMethod(this, "scaleWebViewWhenLoaded", Qt::QueuedConnection); |
|
671 } |
|
672 } |
|
673 |
|
674 /*! |
|
675 Scale web view width when loading is ongoing |
|
676 */ |
|
677 void NmViewerView::scaleWebViewWhenLoading(const QSize &size) |
|
678 { |
|
679 // Try to scale web view while mainframe is being loaded. |
|
680 // So that screen is scrollable even before images are fully loaded |
|
681 // First check that new size is different than previous, no need to react if |
|
682 // same size value is received more than once. |
|
683 if (size != mLatestLoadingSize) { |
|
684 if (!webFrameloadingCompleted && mWebView && mWebView->page() && |
|
685 (size.width() > mScreenSize.width() || size.height() > geometry().height())) { |
|
686 int width = (int)size.width(); |
|
687 int height = (int)size.height(); |
|
688 // Set content (webview) width |
|
689 if (mDisplayingPlainText){ |
|
690 mWebView->setPreferredWidth(geometry().width()); |
|
691 } |
|
692 else { |
|
693 mWebView->setPreferredWidth(width); |
|
694 } |
|
695 mWebView->setMinimumHeight(height); |
|
696 } |
|
697 } |
|
698 mLatestLoadingSize = size; |
|
699 } |
|
700 |
|
701 /*! |
|
702 Scale web view width when loading is completed |
|
703 */ |
|
704 void NmViewerView::scaleWebViewWhenLoaded() |
|
705 { |
|
706 QRectF myGeometry = geometry(); |
|
707 QWebPage *page = mWebView->page(); |
|
708 if (mWebView && page) { |
|
709 page->setPreferredContentsSize(myGeometry.size().toSize()); |
|
710 QSizeF contentSize = page->mainFrame()->contentsSize(); |
|
711 mWebView->setPreferredSize(contentSize); |
|
712 } |
|
713 // Workaround for scrolling problem |
|
714 scene()->setProperty("overridingGesture",QVariant()); |
|
715 } |
|
716 |
|
717 /*! |
|
718 Set new dimensions after orientation change. |
|
719 */ |
|
720 void NmViewerView::adjustViewDimensions() |
|
721 { |
|
722 // Update current screensize |
|
723 mScreenSize = mApplication.screenSize(); |
|
724 // Scale header to screen width |
|
725 if (mHeaderWidget){ |
|
726 mHeaderWidget->rescaleHeader(mScreenSize); |
|
727 } |
|
728 if (mAttaWidget){ |
|
729 // Set attawidget minimum & maximum size |
|
730 mAttaWidget->setMinimumWidth(mScreenSize.width()); |
|
731 mAttaWidget->setMaximumWidth(mScreenSize.width()); |
|
732 } |
|
733 scaleWebViewWhenLoaded(); |
|
734 |
|
735 if (mToolbarEnabled) { |
|
736 // Re-create toolbar in orientation switch |
|
737 createToolBar(); |
|
738 } |
|
739 } |
|
740 |
|
741 /*! |
|
742 Screen orientation changed. Web view needs to be scaled when |
|
743 landscape <-> portrait switch occurs because text needs to |
|
744 be wrapped again. |
|
745 */ |
|
746 void NmViewerView::orientationChanged(Qt::Orientation orientation) |
|
747 { |
|
748 Q_UNUSED(orientation); |
|
749 QTimer::singleShot(NmOrientationTimer, this, SLOT(adjustViewDimensions())); |
|
750 } |
|
751 |
|
752 /*! |
|
753 Link clicked callback |
|
754 */ |
|
755 void NmViewerView::linkClicked(const QUrl& link) |
|
756 { |
|
757 NM_FUNCTION; |
|
758 |
|
759 if (link.scheme() == NmHttpLinkScheme || |
|
760 link.scheme() == NmHttpsLinkScheme) { |
|
761 mAttaManager.cancelFetch(); |
|
762 QDesktopServices::openUrl(link); |
|
763 } else if (link.scheme() == NmMailtoLinkScheme){ |
|
764 mAttaManager.cancelFetch(); |
|
765 QList<NmAddress*> *addrList = new QList<NmAddress*>(); |
|
766 NmAddress *mailtoAddr = new NmAddress(); |
|
767 QString address = link.toString(QUrl::RemoveScheme); |
|
768 mailtoAddr->setAddress(address); |
|
769 mailtoAddr->setDisplayName(address); |
|
770 addrList->append(mailtoAddr); |
|
771 // Create start parameters. Address list ownership |
|
772 // is transferred to startparam object |
|
773 NmUiStartParam* param = new NmUiStartParam(NmUiViewMessageEditor, |
|
774 mStartParam->mailboxId(), |
|
775 mStartParam->folderId(), |
|
776 0, |
|
777 NmUiEditorMailto, |
|
778 addrList); |
|
779 mApplication.enterNmUiView(param); |
|
780 } |
|
781 } |
|
782 |
|
783 /*! |
|
784 Function can be used to check whether mouse event has |
|
785 occured on top of header area. |
|
786 */ |
|
787 bool NmViewerView::eventOnTopOfHeaderArea(QGraphicsSceneMouseEvent *event) |
|
788 { |
|
789 bool ret(false); |
|
790 if (event && mHeaderWidget) { |
|
791 QPointF lastReleasePoint = event->lastPos(); |
|
792 QPointF contentWidgetPos = mScrollAreaContents->pos(); |
|
793 int headerHeight = (int)mHeaderWidget->geometry().height(); |
|
794 if (lastReleasePoint.y()<headerHeight+contentWidgetPos.y()) { |
|
795 ret=true; |
|
796 } |
|
797 } |
|
798 return ret; |
|
799 } |
|
800 |
|
801 /*! |
|
802 Get function for content widget web view. |
|
803 */ |
|
804 NmMailViewerWK* NmViewerView::webView() |
|
805 { |
|
806 return mWebView; |
|
807 } |
|
808 |
|
809 /*! |
|
810 Get function for message being viewed |
|
811 */ |
|
812 NmMessage* NmViewerView::message() |
|
813 { |
|
814 return mMessage; |
|
815 } |
|
816 |
|
817 /*! |
|
818 Function to set message read status |
|
819 */ |
|
820 void NmViewerView::changeMessageReadStatus(bool read) |
|
821 { |
|
822 NM_FUNCTION; |
|
823 |
|
824 QList<const NmMessageEnvelope*> envelopeList; |
|
825 NmMessageEnvelope *envelope = &mMessage->envelope(); |
|
826 QPointer<NmStoreEnvelopesOperation> op(NULL); |
|
827 if (envelope) { |
|
828 if ( read != envelope->isRead() ){ |
|
829 if (read){ |
|
830 envelope->setRead(true); |
|
831 envelopeList.append(envelope); |
|
832 op = mUiEngine.setEnvelopes( |
|
833 mStartParam->mailboxId(), |
|
834 mStartParam->folderId(), |
|
835 MarkAsRead, |
|
836 envelopeList); |
|
837 } |
|
838 else { |
|
839 envelope->setRead(false); |
|
840 envelopeList.append(envelope); |
|
841 op = mUiEngine.setEnvelopes( |
|
842 mStartParam->mailboxId(), |
|
843 mStartParam->folderId(), |
|
844 MarkAsUnread, |
|
845 envelopeList); |
|
846 } |
|
847 } |
|
848 } |
|
849 } |
|
850 |
|
851 /*! |
|
852 Set mailbox name to title |
|
853 */ |
|
854 void NmViewerView::setMailboxName() |
|
855 { |
|
856 if (mStartParam){ |
|
857 NmMailboxMetaData *meta = mUiEngine.mailboxById(mStartParam->mailboxId()); |
|
858 if (meta) { |
|
859 setTitle(meta->name()); |
|
860 } |
|
861 } |
|
862 } |
|
863 |
|
864 /*! |
|
865 contentScrollPositionChanged. |
|
866 Function reacts to scroll position change events and sets |
|
867 header to correct position |
|
868 */ |
|
869 void NmViewerView::contentScrollPositionChanged(const QPointF &newPosition) |
|
870 { |
|
871 if (mWebView&&mHeaderWidget){ |
|
872 QRectF webViewRect = mWebView->geometry(); |
|
873 QTransform tr; |
|
874 qreal leftMovementThreshold(webViewRect.width()-mHeaderWidget->geometry().width()); |
|
875 if (newPosition.x()<0) { |
|
876 tr.translate(webViewRect.topLeft().x() ,0); |
|
877 } |
|
878 else if (newPosition.x()>=0 && newPosition.x()<leftMovementThreshold) { |
|
879 tr.translate(mHeaderStartScenePos.x()+newPosition.x() ,0); |
|
880 } |
|
881 else { |
|
882 tr.translate(webViewRect.topLeft().x()+leftMovementThreshold ,0); |
|
883 } |
|
884 mHeaderWidget->setTransform(tr); |
|
885 if (mAttaWidget) { |
|
886 mAttaWidget->setTransform(tr); |
|
887 } |
|
888 } |
|
889 mLatestScrollPos = newPosition; |
|
890 } |
|
891 |
|
892 /*! |
|
893 createToolBar. Function asks menu commands from extension |
|
894 to be added to toolbar owned by the HbView. |
|
895 */ |
|
896 void NmViewerView::createToolBar() |
|
897 { |
|
898 HbToolBar *tb = toolBar(); |
|
899 NmUiExtensionManager &extMngr = mApplication.extManager(); |
|
900 if (tb && &extMngr && mStartParam) { |
|
901 tb->clearActions(); |
|
902 NmActionRequest request(this, NmActionToolbar, NmActionContextViewViewer, |
|
903 NmActionContextDataNone, mStartParam->mailboxId(), mStartParam->folderId() ); |
|
904 QList<NmAction *> list; |
|
905 extMngr.getActions(request, list); |
|
906 for (int i = 0; i < list.count(); i++) { |
|
907 tb->addAction(list[i]); |
|
908 } |
|
909 } |
|
910 } |
|
911 |
|
912 /*! |
|
913 createOptionsMenu. Functions asks menu commands from extension |
|
914 to be added to options menu. |
|
915 */ |
|
916 void NmViewerView::createOptionsMenu() |
|
917 { |
|
918 HbMenu *optionsMenu = menu(); |
|
919 NmUiExtensionManager &extMngr = mApplication.extManager(); |
|
920 if (optionsMenu && &extMngr && mStartParam) { |
|
921 optionsMenu->clearActions(); |
|
922 NmActionRequest request(this, NmActionOptionsMenu, NmActionContextViewViewer, |
|
923 NmActionContextDataNone, mStartParam->mailboxId(), mStartParam->folderId() ); |
|
924 |
|
925 QList<NmAction*> list; |
|
926 extMngr.getActions(request, list); |
|
927 for (int i=0;i<list.count();i++) { |
|
928 optionsMenu->addAction(list[i]); |
|
929 } |
|
930 } |
|
931 } |
|
932 |
|
933 /*! |
|
934 handleActionCommand. From NmActionObserver, extension manager calls this |
|
935 call to handle menu command in the UI. |
|
936 */ |
|
937 void NmViewerView::handleActionCommand(NmActionResponse &actionResponse) |
|
938 { |
|
939 // Handle options menu or toolbar |
|
940 if (actionResponse.menuType() == NmActionOptionsMenu || |
|
941 actionResponse.menuType() == NmActionToolbar) { |
|
942 switch (actionResponse.responseCommand()) { |
|
943 case NmActionResponseCommandReply: { |
|
944 mAttaManager.cancelFetch(); |
|
945 NmUiStartParam *startParam = new NmUiStartParam(NmUiViewMessageEditor, |
|
946 mStartParam->mailboxId(), mStartParam->folderId(), |
|
947 mStartParam->messageId(), NmUiEditorReply); |
|
948 mApplication.enterNmUiView(startParam); |
|
949 } |
|
950 break; |
|
951 case NmActionResponseCommandReplyAll: { |
|
952 mAttaManager.cancelFetch(); |
|
953 NmUiStartParam *startParam = new NmUiStartParam(NmUiViewMessageEditor, |
|
954 mStartParam->mailboxId(), mStartParam->folderId(), |
|
955 mStartParam->messageId(), NmUiEditorReplyAll); |
|
956 mApplication.enterNmUiView(startParam); |
|
957 } |
|
958 break; |
|
959 case NmActionResponseCommandForward: { |
|
960 mAttaManager.cancelFetch(); |
|
961 NmUiStartParam *startParam = new NmUiStartParam(NmUiViewMessageEditor, |
|
962 mStartParam->mailboxId(), mStartParam->folderId(), |
|
963 mStartParam->messageId(), NmUiEditorForward); |
|
964 mApplication.enterNmUiView(startParam); |
|
965 } |
|
966 break; |
|
967 case NmActionResponseCommandDeleteMail: { |
|
968 mAttaManager.cancelFetch(); |
|
969 deleteMessage(); |
|
970 } |
|
971 break; |
|
972 default: |
|
973 break; |
|
974 } |
|
975 } |
|
976 } |
|
977 |
|
978 /*! |
|
979 Deletes the currently open message |
|
980 */ |
|
981 void NmViewerView::deleteMessage() |
|
982 { |
|
983 NM_FUNCTION; |
|
984 |
|
985 QList<NmId> messageList; |
|
986 messageList.append(mStartParam->messageId()); |
|
987 int err = mUiEngine.deleteMessages(mStartParam->mailboxId(), |
|
988 mStartParam->folderId(), |
|
989 messageList); |
|
990 messageList.clear(); |
|
991 } |
|
992 |
|
993 |
|
994 /*! |
|
995 This is called when attachment fetch progress changes |
|
996 */ |
|
997 void NmViewerView::progressChanged(int value) |
|
998 { |
|
999 if (mAttaIndexUnderFetch != NmNotFoundError) { |
|
1000 // emit signal |
|
1001 if (mAttaWidget && mAttaWidget->progressValue(mAttaIndexUnderFetch) < value) { |
|
1002 progressValueChanged(mAttaIndexUnderFetch, value); |
|
1003 } |
|
1004 } |
|
1005 } |
|
1006 |
|
1007 /*! |
|
1008 This is called when attachment fetch is completed |
|
1009 */ |
|
1010 void NmViewerView::fetchCompleted(int result) |
|
1011 { |
|
1012 if (mAttaWidget && mAttaIndexUnderFetch != NmNotFoundError) { |
|
1013 if (result == NmNoError) { |
|
1014 progressValueChanged(mAttaIndexUnderFetch, NmProgressValueComplete); |
|
1015 openAttachment(mAttaIndexUnderFetch); |
|
1016 } else { |
|
1017 mAttaWidget->hideProgressBar(mAttaIndexUnderFetch); |
|
1018 } |
|
1019 } |
|
1020 mAttaIndexUnderFetch = NmNotFoundError; |
|
1021 } |
|
1022 |
|
1023 /*! |
|
1024 externalDelete. From NmUiEngine, handles viewer shutdown when current message is deleted. |
|
1025 */ |
|
1026 void NmViewerView::messageDeleted(const NmId &mailboxId, const NmId &folderId, const NmId &messageId) |
|
1027 { |
|
1028 if ((mStartParam->viewId() == NmUiViewMessageViewer) |
|
1029 && (mStartParam->mailboxId()== mailboxId) |
|
1030 && (mStartParam->folderId()== folderId) |
|
1031 && (mStartParam->messageId()== messageId)) { |
|
1032 mApplication.prepareForPopView(); |
|
1033 } |
|
1034 } |
|
1035 |
|
1036 /*! |
|
1037 Helper function for wait dialog creation. |
|
1038 */ |
|
1039 void NmViewerView::createAndShowWaitDialog() |
|
1040 { |
|
1041 delete mWaitDialog; |
|
1042 mWaitDialog = NULL; |
|
1043 // Create new wait dialog and set it to me modal with dimmed background |
|
1044 mWaitDialog = new HbProgressDialog(HbProgressDialog::WaitDialog); |
|
1045 // Connect to observe orientation change events |
|
1046 connect(mWaitDialog->mainWindow(), SIGNAL(orientationChanged(Qt::Orientation)), |
|
1047 this, SLOT(orientationChanged(Qt::Orientation))); |
|
1048 mWaitDialog->setModal(true); |
|
1049 mWaitDialog->setBackgroundFaded(true); |
|
1050 connect(mWaitDialog, SIGNAL(cancelled()), this, SLOT(waitNoteCancelled())); |
|
1051 mWaitDialog->setText(hbTrId("txt_mail_dialog_loading_mail_content")); |
|
1052 // Display wait dialog |
|
1053 mWaitDialog->show(); |
|
1054 } |