|
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 view |
|
15 * |
|
16 */ |
|
17 #include "nmuiheaders.h" |
|
18 |
|
19 // Layout file and view |
|
20 static const char *NMUI_EDITOR_VIEW_XML = ":/docml/nmeditorview.docml"; |
|
21 static const char *NMUI_EDITOR_VIEW= "editorview"; |
|
22 static const char *NMUI_EDITOR_SCROLL_AREA = "scrollArea"; |
|
23 static const char *NMUI_EDITOR_SCROLL_AREA_CONTENTS = "scrollAreaContents"; |
|
24 |
|
25 static const int nmOrientationTimer=100; |
|
26 |
|
27 static const QString Delimiter("; "); |
|
28 #define IMAGE_FETCHER_INTERFACE "Image" |
|
29 #define FETCHER_OPERATION "fetch(QVariantMap,QVariant)" |
|
30 |
|
31 /*! |
|
32 \class NmEditorView |
|
33 \brief Mail editor view |
|
34 */ |
|
35 |
|
36 /*! |
|
37 Constructor |
|
38 */ |
|
39 NmEditorView::NmEditorView( |
|
40 NmApplication &application, |
|
41 NmUiStartParam* startParam, |
|
42 NmUiEngine &uiEngine, |
|
43 QGraphicsItem *parent) |
|
44 : NmBaseView(startParam, parent), |
|
45 mApplication(application), |
|
46 mUiEngine(uiEngine), |
|
47 mDocumentLoader(NULL), |
|
48 mScrollArea(NULL), |
|
49 mEditWidget(NULL), |
|
50 mHeaderWidget(NULL), |
|
51 mMessage(NULL), |
|
52 mContentWidget(NULL), |
|
53 mAttachContextMenu(NULL), |
|
54 mMessageCreationOperation(NULL), |
|
55 mAddAttachmentOperation(NULL), |
|
56 mRemoveAttachmentOperation(NULL), |
|
57 mCheckOutboxOperation(NULL) |
|
58 { |
|
59 mDocumentLoader = new HbDocumentLoader(); |
|
60 // Set object name |
|
61 setObjectName("NmEditorView"); |
|
62 // Load view layout |
|
63 loadViewLayout(); |
|
64 // Set mailbox name to title pane |
|
65 setMailboxName(); |
|
66 // Set message data |
|
67 setMessageData(); |
|
68 } |
|
69 |
|
70 /*! |
|
71 Destructor |
|
72 */ |
|
73 NmEditorView::~NmEditorView() |
|
74 { |
|
75 delete mMessageCreationOperation; |
|
76 delete mCheckOutboxOperation; |
|
77 |
|
78 delete mMessage; |
|
79 |
|
80 mWidgetList.clear(); |
|
81 delete mDocumentLoader; |
|
82 |
|
83 delete mContentWidget; |
|
84 |
|
85 delete mPrioritySubMenu; |
|
86 delete mAttachContextMenu; |
|
87 } |
|
88 |
|
89 /*! |
|
90 View layout loading from XML |
|
91 */ |
|
92 void NmEditorView::loadViewLayout() |
|
93 { |
|
94 mPrioritySubMenu = NULL; |
|
95 |
|
96 // Use document loader to load the view |
|
97 bool ok = false; |
|
98 mWidgetList = mDocumentLoader->load(NMUI_EDITOR_VIEW_XML, &ok); |
|
99 |
|
100 if (ok == true && mWidgetList.count()) { |
|
101 // Set view |
|
102 QGraphicsWidget *view = mDocumentLoader->findWidget(NMUI_EDITOR_VIEW); |
|
103 if (view){ |
|
104 setWidget(view); |
|
105 } |
|
106 |
|
107 mScrollArea = qobject_cast<NmBaseViewScrollArea *> |
|
108 (mDocumentLoader->findObject(NMUI_EDITOR_SCROLL_AREA)); |
|
109 mScrollAreaContents = qobject_cast<HbWidget *> |
|
110 (mDocumentLoader->findObject(NMUI_EDITOR_SCROLL_AREA_CONTENTS)); |
|
111 |
|
112 mContentWidget = new NmEditorContent(mScrollArea, this, mDocumentLoader); |
|
113 mEditWidget = mContentWidget->editor(); |
|
114 mHeaderWidget = mContentWidget->header(); |
|
115 |
|
116 // Set default color for user - entered text if editor is in re/reAll/fw mode |
|
117 NmUiEditorStartMode mode = mStartParam->editorStartMode(); |
|
118 if (mode == NmUiEditorReply || mode == NmUiEditorReplyAll || mode == NmUiEditorForward) { |
|
119 mEditWidget->setCustomTextColor(true, Qt::blue); |
|
120 } |
|
121 } |
|
122 |
|
123 // Connect signals from background scroll area |
|
124 connect(mScrollArea, SIGNAL(handleMousePressEvent(QGraphicsSceneMouseEvent*)), |
|
125 this, SLOT(sendMousePressEventToScroll(QGraphicsSceneMouseEvent*))); |
|
126 connect(mScrollArea, SIGNAL(handleMouseReleaseEvent(QGraphicsSceneMouseEvent*)), |
|
127 this, SLOT(sendMouseReleaseEventToScroll(QGraphicsSceneMouseEvent*))); |
|
128 connect(mScrollArea, SIGNAL(handleMouseMoveEvent(QGraphicsSceneMouseEvent*)), |
|
129 this, SLOT(sendMouseMoveEventToScroll(QGraphicsSceneMouseEvent*))); |
|
130 |
|
131 connect(mScrollArea, SIGNAL(handleLongPressGesture(const QPointF &)), |
|
132 this, SLOT(sendLongPressGesture(const QPointF &))); |
|
133 |
|
134 // Connect options menu about to show to create options menu function |
|
135 // Menu needs to be create "just-in-time" |
|
136 connect(menu(), SIGNAL(aboutToShow()), this, SLOT(createOptionsMenu())); |
|
137 NmAction *dummy = new NmAction(0); |
|
138 menu()->addAction(dummy); |
|
139 |
|
140 initializeVKB(); |
|
141 connect(mContentWidget->header(), SIGNAL(recipientFieldsHaveContent(bool)), |
|
142 this, SLOT(setButtonsDimming(bool)) ); |
|
143 |
|
144 // call the createToolBar on load view layout |
|
145 createToolBar(); |
|
146 |
|
147 // Set dimensions |
|
148 adjustViewDimensions(); |
|
149 |
|
150 // Connect to observe orientation change events |
|
151 connect(mApplication.mainWindow(), SIGNAL(orientationChanged(Qt::Orientation)), |
|
152 this, SLOT(orientationChanged(Qt::Orientation))); |
|
153 // Signal for handling the attachment list selection |
|
154 connect(mHeaderWidget, SIGNAL(attachmentRemoved(const NmId)), |
|
155 this, SLOT(removeAttachment(const NmId))); |
|
156 } |
|
157 |
|
158 /*! |
|
159 Reload view contents with new start parameters |
|
160 Typically when view is already open and external view activation occurs |
|
161 for this same view |
|
162 */ |
|
163 void NmEditorView::reloadViewContents(NmUiStartParam* startParam) |
|
164 { |
|
165 // Check start parameter validity. |
|
166 if (startParam&&startParam->viewId()==NmUiViewMessageEditor) { |
|
167 // Delete existing start parameter data |
|
168 delete mStartParam; |
|
169 mStartParam=NULL; |
|
170 // Store new start parameter data |
|
171 mStartParam=startParam; |
|
172 // Store existing edited message to drafts and reload |
|
173 // editor with new start parameters. |
|
174 // .. |
|
175 // Reload editor with new message data |
|
176 setMessageData(); |
|
177 } |
|
178 else { |
|
179 NMLOG("nmailui: Invalid editor start parameter"); |
|
180 // Unused start parameter needs to be deleted |
|
181 delete startParam; |
|
182 startParam = NULL; |
|
183 } |
|
184 } |
|
185 |
|
186 /*! |
|
187 Screen orientation changed. Editor view needs to be scaled when |
|
188 landscape <-> portrait switch occurs because text needs to |
|
189 be wrapped again. |
|
190 */ |
|
191 void NmEditorView::orientationChanged(Qt::Orientation orientation) |
|
192 { |
|
193 Q_UNUSED(orientation); |
|
194 // Adjust content height |
|
195 QTimer::singleShot(nmOrientationTimer, this, SLOT(adjustViewDimensions())); |
|
196 } |
|
197 |
|
198 /*! |
|
199 Set new dimensions after orientation change. |
|
200 */ |
|
201 void NmEditorView::adjustViewDimensions() |
|
202 { |
|
203 if (mScrollAreaContents) { |
|
204 const QSize reso = mApplication.screenSize(); |
|
205 mScrollAreaContents->setMinimumWidth(reso.width()); |
|
206 mScrollAreaContents->setMaximumWidth(reso.width()); |
|
207 } |
|
208 } |
|
209 |
|
210 /*! |
|
211 View id |
|
212 */ |
|
213 NmUiViewId NmEditorView::nmailViewId() const |
|
214 { |
|
215 return NmUiViewMessageEditor; |
|
216 } |
|
217 |
|
218 /*! |
|
219 ScrollArea contents |
|
220 */ |
|
221 HbWidget* NmEditorView::scrollAreaContents() |
|
222 { |
|
223 return mScrollAreaContents; |
|
224 } |
|
225 |
|
226 /* |
|
227 Query user if we want to exit the editor |
|
228 */ |
|
229 bool NmEditorView::okToExitView() |
|
230 { |
|
231 bool okToExit = true; |
|
232 |
|
233 // show the query if the message has not been sent |
|
234 if (mMessage && mContentWidget->header()) { |
|
235 // see if editor has any content |
|
236 NmEditorHeader *header = mContentWidget->header(); |
|
237 |
|
238 int toTextLength = 0; |
|
239 if (header->toField()) { |
|
240 toTextLength = header->toField()->text().length(); |
|
241 } |
|
242 |
|
243 int ccTextLength = 0; |
|
244 if (header->ccField()) { |
|
245 ccTextLength = header->ccField()->text().length(); |
|
246 } |
|
247 |
|
248 int bccTextLength = 0; |
|
249 if (header->bccField()) { |
|
250 bccTextLength = header->bccField()->text().length(); |
|
251 } |
|
252 |
|
253 int subjectLength = 0; |
|
254 if (header->subjectField()) { |
|
255 subjectLength = header->subjectField()->text().length(); |
|
256 } |
|
257 |
|
258 okToExit = (toTextLength == 0 && ccTextLength == 0 && bccTextLength == 0 && |
|
259 subjectLength == 0 && mContentWidget->editor()->document()->isEmpty()); |
|
260 |
|
261 // content exists, verify exit from user |
|
262 if (!okToExit) { |
|
263 HbMessageBox *messageBox = new HbMessageBox(HbMessageBox::MessageTypeQuestion); |
|
264 messageBox->setText(hbTrId("txt_mail_dialog_delete_message")); |
|
265 messageBox->setTimeout(HbMessageBox::NoTimeout); |
|
266 |
|
267 // Read user selection |
|
268 HbAction *action = messageBox->exec(); |
|
269 okToExit = (action == messageBox->primaryAction()); |
|
270 } |
|
271 } |
|
272 |
|
273 return okToExit; |
|
274 } |
|
275 |
|
276 /*! |
|
277 About to exit view. Application calls this function when user has |
|
278 pressed back key and editor needs to delete the draft message. This is |
|
279 called when "auto-exiting" after a successful mail sending. |
|
280 */ |
|
281 void NmEditorView::aboutToExitView() |
|
282 { |
|
283 // These operations need to be stopped before message can be deleted |
|
284 delete mAddAttachmentOperation; |
|
285 delete mRemoveAttachmentOperation; |
|
286 |
|
287 if (mMessage) { // this is NULL if sending is started |
|
288 // Delete message from drafts |
|
289 NmId mailboxId = mMessage->mailboxId(); |
|
290 NmId folderId = mMessage->parentId(); |
|
291 NmId msgId = mMessage->envelope().id(); |
|
292 mUiEngine.removeMessage(mailboxId, folderId, msgId); |
|
293 } |
|
294 } |
|
295 |
|
296 /*! |
|
297 Find message data based on start parameters. Method is called |
|
298 when editor is started. If message data is found it means that |
|
299 operation is forward or reply message. |
|
300 */ |
|
301 void NmEditorView::setMessageData() |
|
302 { |
|
303 // Check the outbox. |
|
304 delete mCheckOutboxOperation; |
|
305 mCheckOutboxOperation = NULL; |
|
306 |
|
307 mCheckOutboxOperation = mUiEngine.checkOutbox(mStartParam->mailboxId()); |
|
308 |
|
309 if (mCheckOutboxOperation) { |
|
310 connect(mCheckOutboxOperation, SIGNAL(operationCompleted(int)), |
|
311 this, SLOT(outboxChecked(int))); |
|
312 } |
|
313 else { |
|
314 startMessageCreation( mStartParam->editorStartMode() ); |
|
315 } |
|
316 } |
|
317 |
|
318 /*! |
|
319 */ |
|
320 void NmEditorView::startMessageCreation(NmUiEditorStartMode startMode) |
|
321 { |
|
322 NmId mailboxId = mStartParam->mailboxId(); |
|
323 NmId folderId = mStartParam->folderId(); |
|
324 NmId msgId = mStartParam->messageId(); |
|
325 |
|
326 delete mMessageCreationOperation; |
|
327 mMessageCreationOperation = NULL; |
|
328 |
|
329 // original message is now fetched so start message creation |
|
330 if (startMode == NmUiEditorForward) { |
|
331 mMessageCreationOperation = mUiEngine.createForwardMessage(mailboxId, msgId); |
|
332 } |
|
333 else if (startMode == NmUiEditorReply || startMode == NmUiEditorReplyAll) { |
|
334 mMessageCreationOperation = mUiEngine.createReplyMessage(mailboxId, |
|
335 msgId, |
|
336 startMode == NmUiEditorReplyAll); |
|
337 } |
|
338 else { |
|
339 mMessageCreationOperation = mUiEngine.createNewMessage(mailboxId); |
|
340 } |
|
341 |
|
342 // operation continues in messageCreated() once it finishes ok |
|
343 if (mMessageCreationOperation) { |
|
344 connect(mMessageCreationOperation, |
|
345 SIGNAL(operationCompleted(int)), |
|
346 this, |
|
347 SLOT(messageCreated(int))); |
|
348 } |
|
349 } |
|
350 |
|
351 /*! |
|
352 Starting the message sending is handled here. |
|
353 */ |
|
354 void NmEditorView::startSending() |
|
355 { |
|
356 // The message contents should be verified |
|
357 updateMessageWithEditorContents(); |
|
358 |
|
359 // verify addresses before sending |
|
360 QList<NmAddress> invalidAddresses; |
|
361 NmUtilities::getRecipientsFromMessage(*mMessage, invalidAddresses, NmUtilities::InvalidAddress); |
|
362 |
|
363 bool okToSend = true; |
|
364 if (invalidAddresses.count() > 0) { |
|
365 |
|
366 // invalid addresses found, verify send from user |
|
367 HbMessageBox *messageBox = new HbMessageBox(HbMessageBox::MessageTypeQuestion); |
|
368 QString noteText = hbTrId("txt_mail_dialog_invalid_mail_address_send"); |
|
369 // set the first failing address to the note |
|
370 noteText = noteText.arg(invalidAddresses.at(0).address()); |
|
371 messageBox->setText(noteText); |
|
372 messageBox->setTimeout(HbMessageBox::NoTimeout); |
|
373 |
|
374 // Read user selection |
|
375 HbAction *action = messageBox->exec(); |
|
376 okToSend = (action == messageBox->primaryAction()); |
|
377 } |
|
378 |
|
379 if (okToSend) { |
|
380 // ownership of mMessage changes |
|
381 mUiEngine.sendMessage(mMessage); |
|
382 mMessage = NULL; |
|
383 // sending animation should be shown here, then exit |
|
384 QTimer::singleShot(1000, &mApplication, SLOT(popView())); |
|
385 } |
|
386 } |
|
387 |
|
388 /*! |
|
389 This is signalled by mMessageCreationOperation when message is created. |
|
390 */ |
|
391 void NmEditorView::messageCreated(int result) |
|
392 { |
|
393 delete mMessage; |
|
394 mMessage = NULL; |
|
395 |
|
396 if (result == NmNoError && mMessageCreationOperation) { |
|
397 NmUiEditorStartMode startMode = mStartParam->editorStartMode(); |
|
398 |
|
399 // get message "again" from engine to update the message contents |
|
400 mMessage = mUiEngine.message( |
|
401 mStartParam->mailboxId(), |
|
402 mStartParam->folderId(), |
|
403 mMessageCreationOperation->getMessageId()); |
|
404 |
|
405 fillEditorWithMessageContents(); |
|
406 } |
|
407 } |
|
408 |
|
409 /*! |
|
410 Updates the message with the editor contents. |
|
411 */ |
|
412 void NmEditorView::updateMessageWithEditorContents() |
|
413 { |
|
414 if (mMessage) { |
|
415 if (mContentWidget && mContentWidget->editor()) { |
|
416 NmMessagePart* bodyPart = mMessage->htmlBodyPart(); |
|
417 if (bodyPart) { |
|
418 bodyPart->setTextContent(mContentWidget->editor()->toHtml(), NmContentTypeTextHtml); |
|
419 } |
|
420 bodyPart = mMessage->plainTextBodyPart(); |
|
421 if (bodyPart) { |
|
422 bodyPart->setTextContent(mContentWidget->editor()->toPlainText(), NmContentTypeTextPlain); |
|
423 } |
|
424 } |
|
425 if (mContentWidget && mContentWidget->header() ) { |
|
426 if (mContentWidget->header()->subjectField()) { |
|
427 mMessage->envelope().setSubject( |
|
428 mContentWidget->header()->subjectField()->text()); |
|
429 } |
|
430 if (mContentWidget->header()->toField()) { |
|
431 QString toFieldText = |
|
432 mContentWidget->header()->toField()->text(); |
|
433 |
|
434 // This verification of zero length string isn't needed |
|
435 // after list of addresses |
|
436 if (toFieldText.length() > 0) { |
|
437 mMessage->envelope().setToRecipients(mContentWidget->header()->toField()->emailAddressList()); |
|
438 } |
|
439 } |
|
440 if (mContentWidget->header()->ccField()) { |
|
441 QString ccFieldText = |
|
442 mContentWidget->header()->ccField()->text(); |
|
443 |
|
444 if (ccFieldText.length() > 0) { |
|
445 mMessage->envelope().setCcRecipients(mContentWidget->header()->ccField()->emailAddressList()); |
|
446 } |
|
447 } |
|
448 if (mContentWidget->header()->bccField()) { |
|
449 QString bccFieldText = |
|
450 mContentWidget->header()->bccField()->text(); |
|
451 |
|
452 if (bccFieldText.length() > 0) { |
|
453 mMessage->envelope().setBccRecipients(mContentWidget->header()->bccField()->emailAddressList()); |
|
454 } |
|
455 } |
|
456 } |
|
457 } |
|
458 } |
|
459 |
|
460 |
|
461 /*! |
|
462 Updates the message with the editor contents. Called only once when the |
|
463 editor is launched. |
|
464 */ |
|
465 void NmEditorView::fillEditorWithMessageContents() |
|
466 { |
|
467 if (!mMessage || !mContentWidget) { |
|
468 return; |
|
469 } |
|
470 |
|
471 NmMessageEnvelope messageEnvelope(mMessage->envelope()); |
|
472 NmUiEditorStartMode editorStartMode = NmUiEditorCreateNew; |
|
473 bool useStartParam(false); |
|
474 |
|
475 if (mStartParam) { |
|
476 editorStartMode = mStartParam->editorStartMode(); |
|
477 |
|
478 if (editorStartMode == NmUiEditorMailto) { |
|
479 // Retrieve the message header data e.g. recipients from mStartParam. |
|
480 useStartParam = true; |
|
481 } |
|
482 } |
|
483 |
|
484 // Set recipients (to, cc and bcc). |
|
485 QString toAddressesString; |
|
486 QString ccAddressesString; |
|
487 QString bccAddressesString; |
|
488 |
|
489 if (useStartParam) { |
|
490 toAddressesString = addressListToString(mStartParam->mailtoAddressList()); |
|
491 ccAddressesString = addressListToString(mStartParam->ccAddressList()); |
|
492 bccAddressesString = addressListToString(mStartParam->bccAddressList()); |
|
493 } |
|
494 else { |
|
495 toAddressesString = addressListToString(messageEnvelope.toRecipients()); |
|
496 ccAddressesString = addressListToString(messageEnvelope.ccRecipients()); |
|
497 bccAddressesString = addressListToString(messageEnvelope.bccRecipients()); |
|
498 } |
|
499 |
|
500 mContentWidget->header()->toField()->setPlainText(toAddressesString); |
|
501 mContentWidget->header()->ccField()->setPlainText(ccAddressesString); |
|
502 mContentWidget->header()->bccField()->setPlainText(bccAddressesString); |
|
503 |
|
504 if (ccAddressesString.length() || bccAddressesString.length()) { |
|
505 // Since cc or/and bcc recipients exist, expand the group box to display |
|
506 // the addresses by expanding the group box. |
|
507 mContentWidget->header()->setGroupBoxCollapsed(false); |
|
508 } |
|
509 |
|
510 // Set subject. |
|
511 if (useStartParam) { |
|
512 QString *subject = mStartParam->subject(); |
|
513 |
|
514 if (subject) { |
|
515 mContentWidget->header()->subjectField()->setPlainText(*subject); |
|
516 } |
|
517 } |
|
518 else { |
|
519 // If a message is taken from the outbox, no subject formatting is done. |
|
520 NmId notUsed(0); |
|
521 |
|
522 if (mCheckOutboxOperation && |
|
523 mCheckOutboxOperation->getMessageId(notUsed)) { |
|
524 editorStartMode = NmUiEditorCreateNew; |
|
525 } |
|
526 |
|
527 // Construct the subject field. |
|
528 mContentWidget->header()->subjectField()->setPlainText( |
|
529 addSubjectPrefix(editorStartMode, messageEnvelope.subject())); |
|
530 } |
|
531 |
|
532 // Set priority. |
|
533 mHeaderWidget->setPriority(messageEnvelope.priority()); |
|
534 |
|
535 // Set the message body. |
|
536 // Fetch plain text part form message store. |
|
537 NmMessagePart *plainPart = mMessage->plainTextBodyPart(); |
|
538 |
|
539 if (plainPart) { |
|
540 mUiEngine.contentToMessagePart(mMessage->mailboxId(), |
|
541 mMessage->parentId(), |
|
542 mMessage->envelope().id(), |
|
543 *plainPart); |
|
544 } |
|
545 |
|
546 // Fetch html part form message store. |
|
547 NmMessagePart *htmlPart = mMessage->htmlBodyPart(); |
|
548 |
|
549 if (htmlPart) { |
|
550 mUiEngine.contentToMessagePart(mMessage->mailboxId(), |
|
551 mMessage->parentId(), |
|
552 mMessage->envelope().id(), |
|
553 *htmlPart); |
|
554 } |
|
555 |
|
556 // Fetch attachment.html part form message store if such exists. |
|
557 QList<NmMessagePart*> parts; |
|
558 mMessage->attachmentList(parts); |
|
559 NmMessagePart *attachmentHtml = NULL; |
|
560 |
|
561 foreach(NmMessagePart *part, parts) { |
|
562 if (part->contentDescription().startsWith(NmContentDescrAttachmentHtml)) { |
|
563 attachmentHtml = part; |
|
564 } |
|
565 } |
|
566 |
|
567 if (attachmentHtml) { |
|
568 mUiEngine.contentToMessagePart(mMessage->mailboxId(), |
|
569 mMessage->parentId(), |
|
570 mMessage->envelope().id(), |
|
571 *attachmentHtml); |
|
572 } |
|
573 |
|
574 mContentWidget->setMessageData(mMessage); |
|
575 |
|
576 // Get list of attachments from the message and set those into UI attachment list |
|
577 QList<NmMessagePart*> attachments; |
|
578 mMessage->attachmentList(attachments); |
|
579 |
|
580 for (int i=0; i<attachments.count(); i++) { |
|
581 mHeaderWidget->addAttachment( |
|
582 attachments[i]->attachmentName(), |
|
583 QString::number(attachments[i]->size()), |
|
584 attachments[i]->id()); |
|
585 } |
|
586 |
|
587 if (mStartParam) { |
|
588 // Attach passed files to the message. |
|
589 QStringList *fileList = mStartParam->attachmentList(); |
|
590 |
|
591 if (fileList) { |
|
592 addAttachments(*fileList); |
|
593 } |
|
594 } |
|
595 |
|
596 // TODO Switch the following arbitrary (magic number) timeout to a |
|
597 // meaningful constant, please! |
|
598 QTimer::singleShot(200, mHeaderWidget, SLOT(sendHeaderHeightChanged())); |
|
599 } |
|
600 |
|
601 |
|
602 /*! |
|
603 createToolBar. Function asks menu commands from extension |
|
604 to be added to toolbar owned by the HbView. |
|
605 */ |
|
606 void NmEditorView::createToolBar() |
|
607 { |
|
608 HbToolBar *tb = toolBar(); |
|
609 NmUiExtensionManager &extMngr = mApplication.extManager(); |
|
610 if (tb && &extMngr && mStartParam) { |
|
611 tb->clearActions(); |
|
612 NmActionRequest request(this, NmActionToolbar, NmActionContextViewEditor, |
|
613 NmActionContextDataNone, mStartParam->mailboxId(), mStartParam->folderId() ); |
|
614 QList<NmAction *> list; |
|
615 extMngr.getActions(request, list); |
|
616 for (int i = 0; i < list.count(); i++) { |
|
617 tb->addAction(list[i]); |
|
618 // If action has NmSendable condition, it is shown only when send action |
|
619 // is available, i.e. when at least one recipient field has data. |
|
620 if( list[i]->availabilityCondition() == NmAction::NmSendable ) { |
|
621 list[i]->setEnabled(false); |
|
622 } |
|
623 } |
|
624 } |
|
625 } |
|
626 |
|
627 /*! |
|
628 createOptionsMenu. Functions asks menu commands from extension |
|
629 to be added to options menu. |
|
630 */ |
|
631 void NmEditorView::createOptionsMenu() |
|
632 { |
|
633 if (!mPrioritySubMenu) { |
|
634 mPrioritySubMenu = new HbMenu(); |
|
635 } |
|
636 mPrioritySubMenu->clearActions(); |
|
637 menu()->clearActions(); |
|
638 NmActionRequest request(this, NmActionOptionsMenu, NmActionContextViewEditor, |
|
639 NmActionContextDataMessage, mStartParam->mailboxId(), mStartParam->folderId(), |
|
640 mStartParam->messageId()); |
|
641 NmUiExtensionManager &extMngr = mApplication.extManager(); |
|
642 QList<NmAction*> list; |
|
643 extMngr.getActions(request, list); |
|
644 for (int i = 0; i < list.count(); i++) { |
|
645 mPrioritySubMenu->addAction(list[i]); |
|
646 } |
|
647 mPrioritySubMenu->setObjectName("editorPrioritySubMenu"); |
|
648 mPrioritySubMenu->setTitle(hbTrId("txt_mail_opt_add_priority")); |
|
649 menu()->addMenu(mPrioritySubMenu); |
|
650 } |
|
651 |
|
652 /*! |
|
653 handleActionCommand. From NmActionObserver, extension manager calls this |
|
654 call to handle menu command in the UI. |
|
655 */ |
|
656 void NmEditorView::handleActionCommand(NmActionResponse &actionResponse) |
|
657 { |
|
658 NmActionResponseCommand responseCommand = actionResponse.responseCommand(); |
|
659 |
|
660 // Handle options menu |
|
661 if (actionResponse.menuType() == NmActionOptionsMenu) { |
|
662 setPriority(responseCommand); |
|
663 } |
|
664 else if (actionResponse.menuType() == NmActionToolbar) { |
|
665 switch (responseCommand) { |
|
666 case NmActionResponseCommandSendMail: { |
|
667 startSending(); |
|
668 break; |
|
669 } |
|
670 case NmActionResponseCommandAttach : { |
|
671 // Do nothing if previous addAttachment operation is still ongoing. |
|
672 if(!mAddAttachmentOperation || !mAddAttachmentOperation->isRunning()) { |
|
673 //will be replaced by toolbar extension |
|
674 if (!mAttachContextMenu) { |
|
675 QList<NmAction *> actionList; |
|
676 NmAction* actionPhoto = new NmAction(0); |
|
677 actionPhoto->setText(QObject::tr("Photo", "txt_nmailui_photo_attach")); |
|
678 actionList.append(actionPhoto); |
|
679 connect(actionPhoto, SIGNAL(triggered()), this, SLOT(attachImage())); |
|
680 |
|
681 mAttachContextMenu = new HbMenu(); |
|
682 mAttachContextMenu->clearActions(); |
|
683 |
|
684 for (int i=0;i<actionList.count();i++) { |
|
685 mAttachContextMenu->addAction(actionList[i]); |
|
686 } |
|
687 } |
|
688 |
|
689 QPointF menuPos(qreal(20),qreal(520)); |
|
690 mAttachContextMenu->exec(menuPos); |
|
691 } |
|
692 break; |
|
693 } |
|
694 default: |
|
695 break; |
|
696 } |
|
697 } |
|
698 } |
|
699 |
|
700 /*! |
|
701 This function converts background scroll area coordinate point into |
|
702 body text editor coordinate point. |
|
703 */ |
|
704 QPointF NmEditorView::viewCoordinateToEditCoordinate(QPointF orgPoint) |
|
705 { |
|
706 QPointF contentWidgetPos = mScrollAreaContents->pos(); |
|
707 qreal y = orgPoint.y() - mHeaderWidget->headerHeight(); |
|
708 y -= contentWidgetPos.y(); |
|
709 qreal x = orgPoint.x() - contentWidgetPos.x(); |
|
710 return QPointF(x, y); |
|
711 } |
|
712 |
|
713 /*! |
|
714 Send mouse press event to body edit widget |
|
715 */ |
|
716 void NmEditorView::sendMousePressEventToScroll(QGraphicsSceneMouseEvent *event) |
|
717 { |
|
718 if (event && mEditWidget && mHeaderWidget) { |
|
719 event->setPos(viewCoordinateToEditCoordinate(event->pos())); |
|
720 event->setAccepted(true); |
|
721 mEditWidget->sendMousePressEvent(event); |
|
722 } |
|
723 } |
|
724 |
|
725 /*! |
|
726 Send mouse release event to body edit widget |
|
727 */ |
|
728 void NmEditorView::sendMouseReleaseEventToScroll(QGraphicsSceneMouseEvent *event) |
|
729 { |
|
730 if (event&& mEditWidget && mHeaderWidget) { |
|
731 event->setPos(viewCoordinateToEditCoordinate(event->pos())); |
|
732 event->setAccepted(true); |
|
733 mEditWidget->sendMouseReleaseEvent(event); |
|
734 } |
|
735 } |
|
736 |
|
737 /*! |
|
738 Send mouse move event to body edit widget |
|
739 */ |
|
740 void NmEditorView::sendMouseMoveEventToScroll(QGraphicsSceneMouseEvent *event) |
|
741 { |
|
742 if (event&& mEditWidget && mHeaderWidget) { |
|
743 event->setPos(viewCoordinateToEditCoordinate(event->pos())); |
|
744 event->setAccepted(true); |
|
745 mEditWidget->sendMouseMoveEvent(event); |
|
746 } |
|
747 } |
|
748 |
|
749 void NmEditorView::sendLongPressGesture(const QPointF &point) |
|
750 { |
|
751 if (mEditWidget && mHeaderWidget) { |
|
752 QPointF scenePos = mEditWidget->scenePos(); |
|
753 QPointF newPoint = QPointF(point.x()-scenePos.x(), point.y()-scenePos.y()); |
|
754 if(mEditWidget->contains(newPoint)) { |
|
755 mEditWidget->sendLongPressEvent(point); |
|
756 } |
|
757 } |
|
758 } |
|
759 |
|
760 |
|
761 /*! |
|
762 Sets all toolbar and VKB buttons dimmed state. All actions that have the |
|
763 availability condition NmSendable set, will be enabled/disabled. |
|
764 */ |
|
765 void NmEditorView::setButtonsDimming(bool enabled) |
|
766 { |
|
767 // Set the toolbar action states |
|
768 HbToolBar *tb = toolBar(); |
|
769 if (tb) { |
|
770 QList<QAction *> toolbarList = tb->actions(); |
|
771 int count = toolbarList.count(); |
|
772 for (int i = 0; i < count; i++) { |
|
773 NmAction *action = static_cast<NmAction *>(toolbarList[i]); |
|
774 if (action->availabilityCondition() == NmAction::NmSendable) { |
|
775 action->setEnabled(enabled); |
|
776 } |
|
777 } |
|
778 |
|
779 // Set the VKB action states |
|
780 // All editors of the view share the same action, so it is enough to set |
|
781 // this only to one of them. |
|
782 HbEditorInterface editorInterface(mContentWidget->editor()); |
|
783 QList<HbAction *> vkbList = editorInterface.actions(); |
|
784 count = vkbList.count(); |
|
785 for (int i = 0; i < count; i++) { |
|
786 NmAction *action = static_cast<NmAction *>(vkbList[i]); |
|
787 if (action->availabilityCondition() == NmAction::NmSendable) { |
|
788 action->setEnabled(enabled); |
|
789 } |
|
790 } |
|
791 } |
|
792 } |
|
793 |
|
794 /*! |
|
795 Initialize the Virtual Keyboard to show the "Send" button |
|
796 for all editors of the view. |
|
797 */ |
|
798 void NmEditorView::initializeVKB() |
|
799 { |
|
800 NmActionRequest request(this, NmActionVKB, NmActionContextViewEditor, |
|
801 NmActionContextDataNone, mStartParam->mailboxId(), mStartParam->folderId() ); |
|
802 NmUiExtensionManager &extMngr = mApplication.extManager(); |
|
803 if (&extMngr) { |
|
804 QList<NmAction *> list; |
|
805 extMngr.getActions(request, list); |
|
806 |
|
807 // VKB only supports one application key, but the responsibility of giving only one |
|
808 // action is left to the extension plugin. The rest are still attached to the VKB, but |
|
809 // they are not shown (unless VKB starts supporting more than one). |
|
810 for (int i = 0; i < list.count(); i++) { |
|
811 if( list[i]->availabilityCondition() == NmAction::NmSendable ) { |
|
812 list[i]->setEnabled(false); |
|
813 } |
|
814 list[i]->setIcon(NmIcons::getIcon(NmIcons::NmIconSend)); |
|
815 |
|
816 // Link VKB to the action. This must be done to all |
|
817 // editors that show the button in VKB. |
|
818 HbEditorInterface editorInterface(mContentWidget->editor()); |
|
819 editorInterface.addAction(list[i]); |
|
820 HbEditorInterface toEditorInterface(mContentWidget->header()->toField()); |
|
821 toEditorInterface.addAction(list[i]); |
|
822 HbEditorInterface ccEditorInterface(mContentWidget->header()->ccField()); |
|
823 ccEditorInterface.addAction(list[i]); |
|
824 HbEditorInterface bccEditorInterface(mContentWidget->header()->bccField()); |
|
825 bccEditorInterface.addAction(list[i]); |
|
826 HbEditorInterface subjectEditorInterface(mContentWidget->header()->subjectField()); |
|
827 subjectEditorInterface.addAction(list[i]); |
|
828 } |
|
829 } |
|
830 } |
|
831 |
|
832 /*! |
|
833 Set mailbox name to title |
|
834 */ |
|
835 void NmEditorView::setMailboxName() |
|
836 { |
|
837 if (mStartParam){ |
|
838 NmMailboxMetaData *meta = mUiEngine.mailboxById(mStartParam->mailboxId()); |
|
839 if (meta){ |
|
840 setTitle(meta->name()); |
|
841 } |
|
842 } |
|
843 } |
|
844 |
|
845 /*! |
|
846 Adds a prefix to the subject for reply or forward. |
|
847 Strips other occurrences of the prefix from the beginning. |
|
848 */ |
|
849 QString NmEditorView::addSubjectPrefix( NmUiEditorStartMode startMode, const QString &subject ) |
|
850 { |
|
851 QString newSubject(subject.trimmed()); |
|
852 |
|
853 if (startMode == NmUiEditorReply || startMode == NmUiEditorReplyAll || |
|
854 startMode == NmUiEditorForward) { |
|
855 QString rePrefix(QObject::tr("Re:", "txt_nmailui_reply_subject_prefix")); |
|
856 QString fwPrefix(QObject::tr("Fw:", "txt_nmailui_forward_subject_prefix")); |
|
857 |
|
858 // strip extra prefixes from beginning |
|
859 int rePrefixLength(rePrefix.length()); |
|
860 int fwPrefixLength(fwPrefix.length()); |
|
861 |
|
862 bool startswithRe(newSubject.startsWith(rePrefix, Qt::CaseInsensitive)); |
|
863 bool startswithFw(newSubject.startsWith(fwPrefix, Qt::CaseInsensitive)); |
|
864 |
|
865 while (startswithRe || startswithFw) { |
|
866 if (startswithRe) { |
|
867 newSubject.remove(0,rePrefixLength); |
|
868 newSubject = newSubject.trimmed(); |
|
869 } |
|
870 else if (startswithFw) { |
|
871 newSubject.remove(0,fwPrefixLength); |
|
872 newSubject = newSubject.trimmed(); |
|
873 } |
|
874 startswithRe = newSubject.startsWith(rePrefix, Qt::CaseInsensitive); |
|
875 startswithFw = newSubject.startsWith(fwPrefix, Qt::CaseInsensitive); |
|
876 } |
|
877 |
|
878 if (startMode == NmUiEditorReply || startMode == NmUiEditorReplyAll) { |
|
879 newSubject = rePrefix + " " + newSubject; |
|
880 } |
|
881 else if (startMode == NmUiEditorForward) { |
|
882 newSubject = fwPrefix + " " + newSubject; |
|
883 } |
|
884 } |
|
885 |
|
886 return newSubject; |
|
887 } |
|
888 |
|
889 #ifdef Q_OS_SYMBIAN |
|
890 |
|
891 void NmEditorView::attachImage() |
|
892 { |
|
893 XQAiwRequest *request; |
|
894 XQApplicationManager mAppmgr; |
|
895 request = mAppmgr.create(IMAGE_FETCHER_INTERFACE, FETCHER_OPERATION, true); |
|
896 |
|
897 if (request) { |
|
898 connect(request, SIGNAL(requestOk(const QVariant&)), this, SLOT(onAttachmentReqCompleted(const QVariant&))); |
|
899 } |
|
900 else { |
|
901 //create request failed |
|
902 NMLOG("appmgr: create request failed"); |
|
903 return; |
|
904 } |
|
905 |
|
906 if (!(request)->send()) { |
|
907 //sending request failed |
|
908 NMLOG("appmgr: send request failed"); |
|
909 } |
|
910 delete request; |
|
911 } |
|
912 |
|
913 /*! |
|
914 This slot is called when 'attachment picker' request has been performed succesfully |
|
915 Parameter 'value' contains file currently one file name but later list of the files. |
|
916 */ |
|
917 void NmEditorView::onAttachmentReqCompleted(const QVariant &value) |
|
918 { |
|
919 if (!value.isNull()) { |
|
920 addAttachments(value.toStringList()); |
|
921 } |
|
922 } |
|
923 |
|
924 #endif |
|
925 |
|
926 /*! |
|
927 Add list of attachments |
|
928 */ |
|
929 void NmEditorView::addAttachments(const QStringList& fileNames) |
|
930 { |
|
931 NMLOG("NmEditorView::addAttachments"); |
|
932 |
|
933 // Add attachment name into UI |
|
934 foreach (QString fileName, fileNames) { |
|
935 // At this phase attachment size and nmid are not known |
|
936 mHeaderWidget->addAttachment(fileName, QString("0"), NmId(0)); |
|
937 NMLOG(fileName); |
|
938 } |
|
939 |
|
940 // Start operation to attach file or list of files into mail message |
|
941 // This will also copy files into message store |
|
942 // Delete previous operation |
|
943 if (mAddAttachmentOperation) { |
|
944 if (!mAddAttachmentOperation->isRunning()) { |
|
945 delete mAddAttachmentOperation; |
|
946 mAddAttachmentOperation = NULL; |
|
947 } |
|
948 } |
|
949 if (!mAddAttachmentOperation) { |
|
950 mAddAttachmentOperation = mUiEngine.addAttachments(*mMessage, fileNames); |
|
951 |
|
952 if (mAddAttachmentOperation) { |
|
953 // Signal to inform completion of one attachment |
|
954 connect(mAddAttachmentOperation, |
|
955 SIGNAL(operationPartCompleted(const QString &, const NmId &, int)), |
|
956 this, |
|
957 SLOT(oneAttachmentAdded(const QString &, const NmId &, int))); |
|
958 |
|
959 // Signal to inform the completion of the whole operation |
|
960 connect(mAddAttachmentOperation, |
|
961 SIGNAL(operationCompleted(int)), |
|
962 this, |
|
963 SLOT(allAttachmentsAdded(int))); |
|
964 } |
|
965 } |
|
966 } |
|
967 |
|
968 /*! |
|
969 This slot is called when attachment has been deleted from UI |
|
970 */ |
|
971 void NmEditorView::removeAttachment(const NmId attachmentPartId) |
|
972 { |
|
973 // Delete previous operation |
|
974 if (mRemoveAttachmentOperation) { |
|
975 if (!mRemoveAttachmentOperation->isRunning()) { |
|
976 delete mRemoveAttachmentOperation; |
|
977 mRemoveAttachmentOperation = NULL; |
|
978 } |
|
979 } |
|
980 if (!mRemoveAttachmentOperation) { |
|
981 mRemoveAttachmentOperation = mUiEngine.removeAttachment(*mMessage, attachmentPartId); |
|
982 } |
|
983 } |
|
984 |
|
985 /*! |
|
986 This is signalled by mAddAttachmentOperation when the operation is |
|
987 completed for one attachment. |
|
988 */ |
|
989 void NmEditorView::oneAttachmentAdded(const QString &fileName, const NmId &msgPartId, int result) |
|
990 { |
|
991 if (result == NmNoError) { |
|
992 // Get file size from the message when it works |
|
993 mHeaderWidget->setAttachmentParameters(fileName, msgPartId, QString("0"), result); |
|
994 } |
|
995 else { |
|
996 // Attachment adding failed. Show an error note and remove from UI attachment list. |
|
997 NMLOG(QString("nmailui: attachment adding into message failed: %1").arg(fileName)); |
|
998 mHeaderWidget->removeAttachment(fileName); |
|
999 } |
|
1000 } |
|
1001 |
|
1002 /*! |
|
1003 This is signalled by mAddAttachmentOperation when the operation is |
|
1004 completed totally. |
|
1005 */ |
|
1006 void NmEditorView::allAttachmentsAdded(int result) |
|
1007 { |
|
1008 if (result != NmNoError) { |
|
1009 HbMessageBox::warning(hbTrId("txt_mail_dialog_unable_to_add_attachment")); |
|
1010 } |
|
1011 } |
|
1012 |
|
1013 /*! |
|
1014 This is signalled by mCheckOutboxOperation when the operation is complete. |
|
1015 */ |
|
1016 void NmEditorView::outboxChecked(int result) |
|
1017 { |
|
1018 bool messageInOutbox = false; |
|
1019 |
|
1020 if (result == NmNoError && mCheckOutboxOperation) { |
|
1021 |
|
1022 NmId messageId; |
|
1023 messageInOutbox = mCheckOutboxOperation->getMessageId(messageId); |
|
1024 |
|
1025 if (messageInOutbox) { |
|
1026 delete mMessage; |
|
1027 mMessage = NULL; |
|
1028 |
|
1029 mMessage = mUiEngine.message( |
|
1030 mStartParam->mailboxId(), |
|
1031 mUiEngine.standardFolderId( |
|
1032 mStartParam->mailboxId(), NmFolderOutbox), |
|
1033 messageId); |
|
1034 |
|
1035 fillEditorWithMessageContents(); |
|
1036 |
|
1037 if (mMessage) { |
|
1038 HbMessageBox::warning( |
|
1039 hbTrId("txt_mail_dialog_sending failed").arg( |
|
1040 NmUtilities::truncate( |
|
1041 mMessage->envelope().subject(), 20))); |
|
1042 } |
|
1043 } |
|
1044 } |
|
1045 |
|
1046 if (!messageInOutbox) { |
|
1047 startMessageCreation(mStartParam->editorStartMode()); |
|
1048 } |
|
1049 } |
|
1050 |
|
1051 /*! |
|
1052 Sets priority for the message object that is being edited |
|
1053 */ |
|
1054 void NmEditorView::setPriority(NmActionResponseCommand priority) |
|
1055 { |
|
1056 mHeaderWidget->setPriority(priority); |
|
1057 |
|
1058 if (mMessage) { |
|
1059 NmMessagePriority messagePriority = NmMessagePriorityNormal; |
|
1060 |
|
1061 if (priority == NmActionResponseCommandPriorityHigh) { |
|
1062 messagePriority = NmMessagePriorityHigh; |
|
1063 } |
|
1064 else if (priority == NmActionResponseCommandPriorityLow) { |
|
1065 messagePriority = NmMessagePriorityLow; |
|
1066 } |
|
1067 mMessage->envelope().setPriority(messagePriority); |
|
1068 } |
|
1069 } |
|
1070 |
|
1071 |
|
1072 /*! |
|
1073 Extracts the addresses from the given list into a string separated with a |
|
1074 delimiter. |
|
1075 |
|
1076 \param list The list containing the addresses. |
|
1077 \return String containing the addresses. |
|
1078 */ |
|
1079 QString NmEditorView::addressListToString(const QList<NmAddress*> &list) const |
|
1080 { |
|
1081 QString addressesString; |
|
1082 QList<NmAddress*>::const_iterator i = list.constBegin(); |
|
1083 |
|
1084 while (i != list.constEnd() && *i) { |
|
1085 if (i > list.constBegin()) { |
|
1086 // Add the delimiter. |
|
1087 addressesString += Delimiter; |
|
1088 } |
|
1089 |
|
1090 addressesString += (*i)->address(); |
|
1091 ++i; |
|
1092 } |
|
1093 |
|
1094 return addressesString; |
|
1095 } |
|
1096 |
|
1097 |
|
1098 /*! |
|
1099 Extracts the addresses from the given list into a string separated with a |
|
1100 delimiter. |
|
1101 |
|
1102 \param list The list containing the addresses. |
|
1103 \return String containing the addresses. |
|
1104 */ |
|
1105 QString NmEditorView::addressListToString(const QList<NmAddress> &list) const |
|
1106 { |
|
1107 QString addressesString; |
|
1108 QList<NmAddress>::const_iterator i = list.constBegin(); |
|
1109 |
|
1110 while (i != list.constEnd()) { |
|
1111 if (i > list.constBegin()) { |
|
1112 // Add the delimiter. |
|
1113 addressesString += Delimiter; |
|
1114 } |
|
1115 |
|
1116 addressesString += (*i).address(); |
|
1117 ++i; |
|
1118 } |
|
1119 |
|
1120 return addressesString; |
|
1121 } |
|
1122 |
|
1123 |
|
1124 // End of file. |