/*
* Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
* All rights reserved.
* This component and the accompanying materials are made available
* under the terms of "Eclipse Public License v1.0"
* which accompanies this distribution, and is available
* at the URL "http://www.eclipse.org/legal/epl-v10.html".
*
* Initial Contributors:
* Nokia Corporation - initial contribution.
*
* Contributors:
*
* Description:
*
*/
// SYSTEM INCLUDES
#include <e32base.h>
#include <HbMenu>
#include <HbAction>
#include <QGraphicsLinearLayout>
#include <QDateTime>
#include <QDir>
#include <QBuffer>
#include <QFile>
#include <QFileInfo>
#include <QApplication>
#include <xqconversions.h>
#include <HbNotificationDialog>
#include <HbDeviceNotificationDialog>
#include <HbMessageBox>
#include <HbMainWindow>
#include <xqaiwrequest.h>
#include <xqappmgr.h>
#include <HbStyleLoader>
#include<HbInputMethod>
#include <QTimer>
#include<QInputContext>
// QT Mobility for fetching business card
#include <qmobilityglobal.h>
#include <qversitwriter.h>
#include <qversitdocument.h>
#include <qcontact.h>
#include <qcontactmanager.h>
#include <qversitcontactexporter.h>
#include <cntservicescontact.h>
#include <commonphoneparser.h> // Common phone number validity checker
// USER INCLUDES
#include "debugtraces.h"
#include "msgunieditorview.h"
#include "msgunieditormonitor.h"
#include "msgsendutil.h"
#include "convergedmessageaddress.h"
#include "UniEditorGenUtils.h"
#include "unieditorpluginloader.h"
#include "unieditorplugininterface.h"
#include "msgsettingsview.h"
#include "msgcontacthandler.h"
#include "msgaudiofetcherdialog.h"
#include "msgunieditorscrollarea.h"
#include "msgunieditorbodyeditor.h"
QTM_USE_NAMESPACE
// Constants
// temporary folder for unieditor
const QString UNIFIED_EDITOR_TEMP_FOLDER("unifiededitor");
// invalid chars in vcard
const QString INVALID_FILENAME_CHARS("[?*<>/\"|\\:]");
// replacement char for invalid char
const QChar REPLACE_CHAR('_');
const int INVALID_MSGID = -1;
// vcard file extn.
const QString FILE_EXTN(".vcf");
// Max vcards inside a msg. Using a very large number.
// TODO: how we can avoid this?
const int MAX_VCARDS(1000);
// LOCALIZED CONSTANTS
#define LOC_TITLE hbTrId("txt_messaging_title_messaging")
//attach options
#define LOC_PHOTO hbTrId("txt_messaging_opt_attach_sub_photo")
#define LOC_SOUND hbTrId("txt_messaging_opt_attach_sub_sound")
#define LOC_BUSINESS_CARD hbTrId("txt_messaging_opt_sub_business_card")
////options menu.
#define LOC_ATTACH hbTrId("txt_messaging_opt_attach")
#define LOC_ADD_SUBJECT hbTrId("txt_messaging_opt_add_subject")
#define LOC_ADD_CC_BCC hbTrId("txt_messaging_opt_add_cc_bcc")
#define LOC_PRIORITY hbTrId("txt_messaging_opt_priority")
#define LOC_DELETE_MESSAGE hbTrId("txt_messaging_opt_delete_message")
//priority sub menu
#define LOC_HIGH hbTrId("txt_messaging_opt_attach_sub_high")
#define LOC_NORMAL hbTrId("txt_messaging_opt_attach_sub_normal")
#define LOC_LOW hbTrId("txt_messaging_opt_attach_sub_low")
//saved to draft note
#define LOC_SAVED_TO_DRAFTS hbTrId("txt_messaging_dpopinfo_saved_to_drafts")
//delete confermation
#define LOC_NOTE_DELETE_MESSAGE hbTrId("txt_messaging_dialog_delete_message")
//settings confirmation
#define LOC_DIALOG_SMS_SETTINGS_INCOMPLETE hbTrId("txt_messaging_dialog_sms_message_centre_does_not_e")
#define LOC_DIALOG_MMS_SETTINGS_INCOMPLETE hbTrId("txt_messaging_dialog_mms_access_point_not_defined")
#define LOC_NOTE_FILES_MISSED_DRAFTS hbTrId("txt_messaging_dpopinfo_some_files_in_the_message")
#define LOC_NOTE_FILES_MISSED_SEND hbTrId("txt_messaging_dialog_unable_to_send_message_some")
// LOCAL FUNCTIONS
//---------------------------------------------------------------
// editorTempPath
// @return fullPath of unified editor's temporary dir
//---------------------------------------------------------------
QString editorTempPath()
{
QDir tempDir;
QString tempPath(QDir::toNativeSeparators(tempDir.tempPath()));
tempPath.append(QDir::separator());
tempPath.append(UNIFIED_EDITOR_TEMP_FOLDER);
tempPath.append(QDir::separator());
return tempPath;
}
//---------------------------------------------------------------
// MsgUnifiedEditorView::MsgUnifiedEditorView
// @see header file
//---------------------------------------------------------------
MsgUnifiedEditorView::MsgUnifiedEditorView( QGraphicsItem *parent ) :
MsgBaseView(parent),
mMainLayout(0),
mMsgMonitor(0),
mPluginLoader(0),
mCanSaveToDrafts(true),
mVkbHost(NULL),
mDialog(NULL),
mOriginatingSC(0),
mOriginatingSME(0),
mReplyPath(false),
mSubjectAction(0),
mCcBccAction(0),
mPhotoAction(0),
mSoundAction(0),
mFocusedWidget(0),
mMinHeight(0.0),
mMaxHeight(0.0)
{
connect(this->mainWindow(),SIGNAL(viewReady()),this,SLOT(doDelayedConstruction()));
initView();
addMenu();
}
//---------------------------------------------------------------
// MsgUnifiedEditorView::~MsgUnifiedEditorView
// @see header file
//---------------------------------------------------------------
MsgUnifiedEditorView::~MsgUnifiedEditorView()
{
// clean editor's temporary contents before exiting
removeTempFolder();
//delete the popup dialog
delete mDialog;
}
//---------------------------------------------------------------
// MsgUnifiedEditorView::initView
// @see header file
//---------------------------------------------------------------
void MsgUnifiedEditorView::initView()
{
if (!HbStyleLoader::registerFilePath(":/layouts")) {
QDEBUG_WRITE("ERROR: MsgUnifiedEditorView -> HbStyleLoader::registerFilePath");
}
//message monitor.
mMsgMonitor = new MsgUnifiedEditorMonitor(this);
//Set the invalid msg id
mOpenedMessageId.setId(-1);
mMainLayout = new QGraphicsLinearLayout(Qt::Vertical, this);
qreal vTopSpacing = 0.0;
qreal vItemSpacing = 0.0;
style()->parameter("hb-param-margin-gene-top",vTopSpacing);
style()->parameter("hb-param-margin-gene-middle-vertical",vItemSpacing);
mMainLayout->setContentsMargins(0,vTopSpacing,0,0);
mMainLayout->setSpacing(vItemSpacing);
mScrollArea = new MsgUnifiedEditorScrollArea(mMsgMonitor,this);
mMainLayout->addItem(mScrollArea);
mBodyEditor = new MsgUnifiedEditorBodyEditor(this);
connect(mScrollArea,SIGNAL(enableMenuAction(int,bool)),this,SLOT(enableMenuAction(int,bool)));
connect(mBodyEditor, SIGNAL(contentsChanged(const QVariant&)),
mMsgMonitor,SLOT(handleContentsChanged(const QVariant&)));
connect(mMsgMonitor,SIGNAL(enableSend(bool)),
mBodyEditor,SLOT(enableSendButton(bool)));
connect(mBodyEditor,SIGNAL(sendMessage()),this,SLOT(send()));
connect(mMsgMonitor,SIGNAL(contentsChanged()),this,SLOT(onContentChanged()));
mMainLayout->addItem(mBodyEditor);
}
//---------------------------------------------------------------
// MsgUnifiedEditorView::addMenu
// @see header file
//---------------------------------------------------------------
void MsgUnifiedEditorView::addMenu()
{
//Create Menu Options
HbMenu* mainMenu = this->menu();
mainMenu->setFocusPolicy(Qt::NoFocus);
//attach sub menu
HbMenu* attachMenu = mainMenu->addMenu(LOC_ATTACH);
mPhotoAction = attachMenu->addAction(LOC_PHOTO,this,SLOT(fetchImages()));
mSoundAction = attachMenu->addAction(LOC_SOUND,this,SLOT(fetchAudio()));
attachMenu->addAction(LOC_BUSINESS_CARD,this,SLOT(fetchContacts()));
//subject action
mSubjectAction = mainMenu->addAction(LOC_ADD_SUBJECT);
//CcBcc action
mCcBccAction = mainMenu->addAction(LOC_ADD_CC_BCC);
connect(mSubjectAction,SIGNAL(triggered()),this, SLOT(addSubject()));
connect(mCcBccAction,SIGNAL(triggered()),this, SLOT(addCcBcc()));
//priority sub menu
HbMenu* prioritySubMenu = mainMenu->addMenu(LOC_PRIORITY);
HbAction* highPriorityAction = prioritySubMenu->addAction(LOC_HIGH);
highPriorityAction->setData(ConvergedMessage::High);
HbAction* normalPriorityAction = prioritySubMenu->addAction(LOC_NORMAL);
normalPriorityAction->setData(ConvergedMessage::Normal);
HbAction* lowPriorityAction = prioritySubMenu->addAction(LOC_LOW);
lowPriorityAction->setData(ConvergedMessage::Low);
connect(highPriorityAction, SIGNAL(triggered()), mScrollArea, SLOT(changePriority()));
connect(normalPriorityAction, SIGNAL(triggered()), mScrollArea, SLOT(changePriority()));
connect(lowPriorityAction, SIGNAL(triggered()), mScrollArea, SLOT(changePriority()));
HbAction* deleteMsgAction = mainMenu->addAction(LOC_DELETE_MESSAGE);
connect(deleteMsgAction,SIGNAL(triggered()),this, SLOT(deleteMessage()));
}
//---------------------------------------------------------------
// MsgUnifiedEditorView::openDraftsMessage
// @see header file
//---------------------------------------------------------------
void MsgUnifiedEditorView::openDraftsMessage(const QVariantList& editorData)
{
// unpack editor's data
// first arg is convergedmessage
QByteArray dataArray = editorData.at(0).toByteArray();
ConvergedMessage* messageDetails = new ConvergedMessage;
QDataStream stream(&dataArray, QIODevice::ReadOnly);
messageDetails->deserialize(stream);
ConvergedMessage::MessageType messageType = messageDetails->messageType();
ConvergedMessageId messageId = *(messageDetails->id());
delete messageDetails;
if(!mPluginLoader)
{
mPluginLoader = new UniEditorPluginLoader(this);
}
UniEditorPluginInterface* pluginInterface =
mPluginLoader->getUniEditorPlugin(messageType);
mOpenedMessageId.setId(messageId.getId());
//Fetch the converged message from the msgId
ConvergedMessage* msg = pluginInterface->convertFrom(messageId.getId());
if( msg != NULL )
{
mReplyPath = msg->replyPath();
if(mReplyPath)
{
mOriginatingSC = msg->originatingSC();
mOriginatingSME = msg->toAddressList().at(0)->address();
}
//Populate the content inside editor
populateContentIntoEditor(*msg,true,true); // true as it is draft message
delete msg;
}
mCanSaveToDrafts = false;
}
//---------------------------------------------------------------
// MsgUnifiedEditorView::fetchMessageFromStore
// @see header file
//---------------------------------------------------------------
void MsgUnifiedEditorView::fetchMessageFromStore(ConvergedMessageId& messageId,
ConvergedMessage::MessageType messageType, int editorOperation)
{
if(!mPluginLoader)
{
mPluginLoader = new UniEditorPluginLoader(this);
}
UniEditorPluginInterface* pluginInterface = NULL;
if( messageType == ConvergedMessage::Mms )
{
pluginInterface = mPluginLoader->getUniEditorPlugin(ConvergedMessage::Mms);
}
else // For sms,vcard,vcal cases
{
pluginInterface = mPluginLoader->getUniEditorPlugin(ConvergedMessage::Sms);
}
//Fetch the converged message from the msgId
ConvergedMessage* msg;
msg = pluginInterface->convertFrom(messageId.getId(),
(UniEditorPluginInterface::EditorOperation)editorOperation);
if( msg != NULL )
{
if(editorOperation == UniEditorPluginInterface::Reply)
{
mReplyPath = msg->replyPath();
if(mReplyPath)
{
mOriginatingSC = msg->originatingSC();
mOriginatingSME = msg->toAddressList().at(0)->address();
}
}
//Populate the content inside editor
populateContentIntoEditor(*msg,false,true);
delete msg;
}
}
//---------------------------------------------------------------
// MsgUnifiedEditorView::populateContent
// @see header file
//---------------------------------------------------------------
void MsgUnifiedEditorView::populateContent(const QVariantList& editorData)
{
// unpack editor's data
// first arg is convergedmessage
QByteArray dataArray = editorData.at(0).toByteArray();
ConvergedMessage* messageDetails = new ConvergedMessage;
QDataStream stream(&dataArray, QIODevice::ReadOnly);
messageDetails->deserialize(stream);
// get next arg i.e. editor operation command
int editorOp = 0;
if(editorData.length() > 1)
{
editorOp = editorData.at(1).toInt();
}
// population logic based on editor Operation command
switch(editorOp)
{
case MsgBaseView::ADD_SUBJECT:
{
//setfocus also.
addSubject(true);
}
break;
case MsgBaseView::ADD_VCARD:
{
contactsFetched(editorData.at(2));
}
break;
case MsgBaseView::FORWARD_MSG:
{
fetchMessageFromStore(*messageDetails->id(),
messageDetails->messageType(),
UniEditorPluginInterface::Forward);
return;
}
case MsgBaseView::REPLY_MSG:
{
fetchMessageFromStore(*messageDetails->id(),
messageDetails->messageType(),
UniEditorPluginInterface::Reply);
return;
}
case MsgBaseView::REPLY_ALL_MSG:
{
fetchMessageFromStore(*messageDetails->id(),
messageDetails->messageType(),
UniEditorPluginInterface::ReplyAll);
return;
}
default:
break;
}
populateContentIntoEditor(*messageDetails,false,false);
delete messageDetails;
}
//---------------------------------------------------------------
// MsgUnifiedEditorView::populateContentIntoEditor
// @see header file
//---------------------------------------------------------------
void MsgUnifiedEditorView::populateContentIntoEditor(const ConvergedMessage& messageDetails,
bool draftMessage,
bool checkForInline)
{
// skip first-time MMS type switch note for draft
if(draftMessage)
{
mMsgMonitor->setSkipNote(true);
}
//scrollarea populate contents
mScrollArea->populateContent(messageDetails,draftMessage,checkForInline);
//body editor populate contents
mBodyEditor->populateContent(messageDetails);
mMsgMonitor->setSkipNote(false);
}
//---------------------------------------------------------------
// MsgUnifiedEditorView::addSubject
// @see header file
//---------------------------------------------------------------
void MsgUnifiedEditorView::addSubject(bool needFocus)
{
//triggered from menu so need a focus.
HbAction* subjectAction = qobject_cast<HbAction*>(this->sender());
if(subjectAction)
{
needFocus = true;
}
mScrollArea->addSubject(needFocus);
}
//---------------------------------------------------------------
// MsgUnifiedEditorView::addCcBcc
// @see header file
//---------------------------------------------------------------
void MsgUnifiedEditorView::addCcBcc(bool needFocus)
{
//set focus to Cc field.
HbAction* ccBccAction = qobject_cast<HbAction*>(this->sender());
if(ccBccAction)
{
needFocus = true;
}
mScrollArea->addCcBcc(needFocus);
addSubject();
}
//---------------------------------------------------------------
// MsgUnifiedEditorView::deleteMessage
// @see header file
//---------------------------------------------------------------
void MsgUnifiedEditorView::deleteMessage()
{
HbMessageBox::question(LOC_NOTE_DELETE_MESSAGE,this,
SLOT(onDialogDeleteMsg(HbAction*)),
HbMessageBox::Delete | HbMessageBox::Cancel);
}
//---------------------------------------------------------------
// MsgUnifiedEditorView::addAttachments
// @see header file
//---------------------------------------------------------------
void MsgUnifiedEditorView::addAttachments(QStringList files)
{
mScrollArea->addAttachments(files);
}
//---------------------------------------------------------------
// MsgUnifiedEditorView::send
// @see header file
//---------------------------------------------------------------
void MsgUnifiedEditorView::send()
{
activateInputBlocker();
// first run the address validation tests
if(!mScrollArea->contactsValid() ||
!MsgUnifiedEditorMonitor::readyForSend())
{
deactivateInputBlocker();
return;
}
// converged msg for sending
ConvergedMessage msg;
ConvergedMessage::MessageType messageType = MsgUnifiedEditorMonitor::messageType();
msg.setMessageType(messageType);
//close vkb before switching view.
mVkbHost->closeKeypad(true);
int result = packMessage(msg);
if(result == KErrNotFound)
{
HbMessageBox::information(LOC_NOTE_FILES_MISSED_SEND, 0, 0, HbMessageBox::Ok);
deactivateInputBlocker();
return;
}
// send message
MsgSendUtil *sendUtil = new MsgSendUtil(this);
int sendResult = sendUtil->send(msg);
delete sendUtil;
// all checks and validations happen before send
if( sendResult == KErrNone)
{
//After sending the new content to drafts chk if the msg
//was originally opened from drafts and delete the opened entry
if( mOpenedMessageId.getId() != -1)
{
if(!mPluginLoader)
{
mPluginLoader = new UniEditorPluginLoader(this);
}
UniEditorPluginInterface* pluginInterface =
mPluginLoader->getUniEditorPlugin(messageType);
//delete the entry
pluginInterface->deleteDraftsEntry(mOpenedMessageId.getId());
}
ConvergedMessageAddressList addrList = msg.toAddressList() +
msg.ccAddressList() +
msg.bccAddressList();
int recepientCount = addrList.count();
QVariantList params;
if(recepientCount == 1 )
{
params << MsgBaseView::CV; // target view
params << MsgBaseView::UNIEDITOR; // source view
QString receipient = addrList.at(0)->address();
params << receipient;
}
else
{
params << MsgBaseView::CLV;// target view
params << MsgBaseView::UNIEDITOR; // source view
}
deactivateInputBlocker();
emit switchView(params);
mCanSaveToDrafts = false;
}
else
{
deactivateInputBlocker();
if(sendResult == KErrNotFound)
{
if (messageType == ConvergedMessage::Sms)
{
HbMessageBox::question(LOC_DIALOG_SMS_SETTINGS_INCOMPLETE,
this,SLOT(onDialogSmsSettings(HbAction*)),
HbMessageBox::Ok | HbMessageBox::Cancel);
}
else
{
HbMessageBox::question(LOC_DIALOG_MMS_SETTINGS_INCOMPLETE,
this,SLOT(onDialogMmsSettings(HbAction*)),
HbMessageBox::Ok | HbMessageBox::Cancel);
}
}
}
}
//---------------------------------------------------------------
// MsgUnifiedEditorView::packMessage
// @see header file
//---------------------------------------------------------------
int MsgUnifiedEditorView::packMessage(ConvergedMessage &msg, bool isSave)
{
// reset reply-path if originating SME constraint is broken
if(mReplyPath && isReplyPathBroken())
{
mReplyPath = false;
}
msg.setReplyPath(mReplyPath);
if(mReplyPath)
{
msg.setOriginatingSC(mOriginatingSC);
}
ConvergedMessage::MessageType messageType = MsgUnifiedEditorMonitor::messageType();
msg.setMessageType(messageType);
msg.setDirection(ConvergedMessage::Outgoing);
QDateTime time = QDateTime::currentDateTime();
msg.setTimeStamp(time.toTime_t());
//pack message data from mscrollarea.
int errorCode = mScrollArea->packMessage(msg,isSave);
//pack message data from body editor.
mBodyEditor->packMessage(msg);
return errorCode;
}
//---------------------------------------------------------------
// MsgUnifiedEditorView::saveContentToDrafts
// @see header file
//---------------------------------------------------------------
int MsgUnifiedEditorView::saveContentToDrafts()
{
if(!mCanSaveToDrafts)
{
return mOpenedMessageId.getId(); // return currently opened message id
}
activateInputBlocker();
ConvergedMessage::MessageType messageType = MsgUnifiedEditorMonitor::messageType();
UniEditorPluginInterface* pluginInterface = NULL;
if( mOpenedMessageId.getId() != -1)
{
if(!mPluginLoader)
{
mPluginLoader = new UniEditorPluginLoader(this);
}
pluginInterface = mPluginLoader->getUniEditorPlugin(messageType);
}
// if empty msg, do not save
if( MsgUnifiedEditorMonitor::messageSize() <= 0 &&
MsgUnifiedEditorMonitor::msgAddressCount() <= 0 )
{
if(mOpenedMessageId.getId() != -1)
{
pluginInterface->deleteDraftsEntry(mOpenedMessageId.getId());
}
deactivateInputBlocker();
return INVALID_MSGID;
}
ConvergedMessage msg;
int result = packMessage(msg, true);
if(result == KErrNotFound)
{
HbNotificationDialog::launchDialog(LOC_NOTE_FILES_MISSED_DRAFTS);
}
// save to drafts
MsgSendUtil *sendUtil = new MsgSendUtil(this);
int msgId = sendUtil->saveToDrafts(msg);
delete sendUtil;
// If saving new msg to draft succeeded and also if the originally
// opened msg was from drafts, then delete the original msg entry
if(msgId != -1 && mOpenedMessageId.getId() != -1)
{
pluginInterface->deleteDraftsEntry(mOpenedMessageId.getId());
mOpenedMessageId.setId(INVALID_MSGID);
}
deactivateInputBlocker();
bool res = ((msgId > INVALID_MSGID)? true : false);
if(res)
{
HbDeviceNotificationDialog::notification("", LOC_SAVED_TO_DRAFTS);
}
mCanSaveToDrafts = false;
return msgId;
}
//---------------------------------------------------------------
// MsgUnifiedEditorView::handleKeyEvent
// @see header file
//---------------------------------------------------------------
bool MsgUnifiedEditorView::handleKeyEvent(int key)
{
bool eventHandled = false;
if (Qt::Key_Yes == key) {
eventHandled = true;
send();
}
return eventHandled;
}
//---------------------------------------------------------------
// MsgUnifiedEditorView::createVCards
// @see header file
//---------------------------------------------------------------
int MsgUnifiedEditorView::createVCards(
const QVariant& value, QStringList& filelist)
{
// make sure that temp-folder is created for storing vcards
if(!createTempFolder())
{
return KErrGeneral;
}
// extract contact-list
QContactManager* contactManager = new QContactManager("symbian");
CntServicesContactList cntServicesContacts = qVariantValue<CntServicesContactList>(value);
int cntCount = cntServicesContacts.count();
QCRITICAL_WRITE_FORMAT("servicecontactlist count:",cntCount);
QList<QtMobility::QContact> contactList;
for(int i = 0; i < cntCount; i++ )
{
contactList << contactManager->contact( cntServicesContacts.at(i).mContactId );
}
delete contactManager;
// get list of all versit-documents
QVersitDocument::VersitType versitType(QVersitDocument::VCard21Type);
QVersitContactExporter exporter;
bool ret_val = exporter.exportContacts(contactList, versitType);
if(ret_val == false)
{
QCRITICAL_WRITE("QVersitContactExporter::exportContacts returned false");
return KErrGeneral;
}
// process the documents
QList<QtMobility::QVersitDocument> documentList = exporter.documents();
// loop though and create a vcard for each contact
QVersitWriter* writer = new QVersitWriter();
for(int i = 0; i < cntCount; i++ )
{
// buffer to hold vcard data fetched from contacts
QBuffer contactsbuf;
contactsbuf.open(QBuffer::ReadWrite);
writer->setDevice( &contactsbuf );
//write document data to buffer
QList<QtMobility::QVersitDocument> currDoc;
currDoc << documentList.at(i);
writer->startWriting(currDoc);
if(writer->waitForFinished())
{
// generate file name
QString displayLabel = contactList.at(i).displayLabel();
displayLabel.replace(QRegExp(INVALID_FILENAME_CHARS), REPLACE_CHAR);
QString filepath = generateFileName(displayLabel);
// create file
QFile file(filepath);
if(file.open(QIODevice::WriteOnly))
{
// trap ignore so that, incase of multiselection, other vcards are still created
QByteArray bufArr;
TRAP_IGNORE(
HBufC8* contactBuf8 = XQConversions::qStringToS60Desc8(contactsbuf.data());
if(contactBuf8)
{
CleanupStack::PushL(contactBuf8);
CBufBase* contactbufbase = CBufFlat::NewL(contactsbuf.size());
CleanupStack::PushL(contactbufbase);
contactbufbase->InsertL( contactbufbase->Size(), *contactBuf8);
TPtr8 ptrbuf(contactbufbase->Ptr(0));
bufArr = XQConversions::s60Desc8ToQByteArray(ptrbuf);
CleanupStack::PopAndDestroy(contactbufbase);
CleanupStack::PopAndDestroy(contactBuf8);
// write to file
file.write(bufArr);
filelist << filepath;
}
); // TRAP END
//close file
file.close();
}
}
}
// cleanup & return
delete writer;
return KErrNone;
}
//---------------------------------------------------------------
// MsgUnifiedEditorView::generateFileName
// @param suggestedName suggested name of the file
// @return fullPath of the generated filename
// @algo For multiselected 'Unnamed' contacts, the filename should
// be synthesized as unnamed.vcf, unnamed1.vcf, unnamed2.vcf etc.
//---------------------------------------------------------------
QString MsgUnifiedEditorView::generateFileName(QString& suggestedName)
{
QDir editorTempDir;
editorTempDir.cd(editorTempPath());
for(int i=0; i<MAX_VCARDS; i++)
{
QString searchFileName = suggestedName;
if(i>0)
{
searchFileName.append(QString::number(i));
}
searchFileName.append(FILE_EXTN);
// check if filename is already used by an attachment in container
if(!mScrollArea->fileNameInUse(searchFileName))
{
if(i>0)
{
suggestedName.append(QString::number(i));
}
break;
}
}
QString filepath(editorTempPath());
filepath.append(suggestedName);
filepath.append(FILE_EXTN);
return filepath;
}
//---------------------------------------------------------------
// MsgUnifiedEditorView::fetchContacts
// @see header file
//---------------------------------------------------------------
void MsgUnifiedEditorView::fetchContacts()
{
QString service("phonebookservices");
QString interface("com.nokia.symbian.IContactsFetch");
QString operation("multiFetch(QString,QString)");
XQApplicationManager appManager;
XQAiwRequest* request = appManager.create(service, interface, operation, true); //embedded
if ( request == NULL )
{
QCRITICAL_WRITE("AIW-ERROR: NULL request");
return;
}
request->setSynchronous(false); // asynchronous
// Result handlers
connect (request, SIGNAL(requestOk(const QVariant&)),
this, SLOT(contactsFetched(const QVariant&)));
connect (request, SIGNAL(requestError(int,const QString&)),
this, SLOT(serviceRequestError(int,const QString&)));
QList<QVariant> args;
args << LOC_TITLE;
args << KCntActionAll;
args << KCntFilterDisplayAll;
request->setArguments(args);
if(!request->send())
{
QDEBUG_WRITE_FORMAT("AIW-ERROR: Request Send failed:",request->lastError());
}
}
//---------------------------------------------------------------
// MsgUnifiedEditorView::fetchImages
// @see header file
//---------------------------------------------------------------
void MsgUnifiedEditorView::fetchImages()
{
QString service("photos");
QString interface("com.nokia.symbian.IImageFetch");
QString operation("fetch()");
XQApplicationManager appManager;
XQAiwRequest* request = appManager.create(service,interface, operation, true);//embedded
if(!request)
{
QCRITICAL_WRITE("AIW-ERROR: NULL request");
return;
}
request->setSynchronous(false); // asynchronous
connect(request, SIGNAL(requestOk(const QVariant&)),
this, SLOT(imagesFetched(const QVariant&)));
connect(request, SIGNAL(requestError(int,const QString&)),
this, SLOT(serviceRequestError(int,const QString&)));
// Make the request
if (!request->send())
{
QDEBUG_WRITE_FORMAT("AIW-ERROR: Request Send failed:" , request->lastError());
}
}
//---------------------------------------------------------------
// MsgUnifiedEditorView::fetchAudio
// @see header file
//---------------------------------------------------------------
void MsgUnifiedEditorView::fetchAudio()
{
// Launch Audio fetcher dialog
if(!mDialog)
{
mDialog = new MsgAudioFetcherDialog();
connect(mDialog,
SIGNAL(audioSelected(QString&)),
this,
SLOT(onAudioSelected(QString&)));
}
//show the dialog
mDialog->show();
}
//---------------------------------------------------------------
// MsgUnifiedEditorView::contactsFetched
// @see header file
//---------------------------------------------------------------
void MsgUnifiedEditorView::contactsFetched(const QVariant& value)
{
XQAiwRequest* request = qobject_cast<XQAiwRequest*>(this->sender());
if(request)
{
delete request;
}
// create a vcard for each contact fetched and add as attachment
QStringList attachmentList;
if(createVCards(value, attachmentList) == KErrNone)
{
addAttachments(attachmentList);
}
}
//---------------------------------------------------------------
// MsgUnifiedEditorView::imagesFetched
// @see header file
//---------------------------------------------------------------
void MsgUnifiedEditorView::imagesFetched(const QVariant& result )
{
XQAiwRequest* request = qobject_cast<XQAiwRequest*>(this->sender());
if(request)
{
delete request;
}
if(result.canConvert<QStringList>())
{
QStringList fileList = result.value<QStringList>();
if ( fileList.size()>0 )
{
QString filepath(QDir::toNativeSeparators(fileList.at(0)));
mScrollArea->setImage(filepath);
}
}
}
//---------------------------------------------------------------
// MsgUnifiedEditorView::serviceRequestError
// @see header file
//---------------------------------------------------------------
void MsgUnifiedEditorView::serviceRequestError(int errorCode, const QString& errorMessage)
{
QDEBUG_WRITE_FORMAT(errorMessage,errorCode);
Q_UNUSED(errorCode)
Q_UNUSED(errorMessage)
XQAiwRequest* request = qobject_cast<XQAiwRequest*>(this->sender());
if(request)
{
delete request;
}
}
//---------------------------------------------------------------
// MsgUnifiedEditorView::activateInputBlocker
// @see header file
//--------------------------------------------------------------
void MsgUnifiedEditorView::activateInputBlocker()
{
mainWindow()->setInteractive(false);
}
//---------------------------------------------------------------
// MsgUnifiedEditorView::deactivateInputBlocker
// @see header file
//--------------------------------------------------------------
void MsgUnifiedEditorView::deactivateInputBlocker()
{
mainWindow()->setInteractive(true);
}
//---------------------------------------------------------------
// MsgUnifiedEditorView::enableMenuAction
// @see header file
//--------------------------------------------------------------
void MsgUnifiedEditorView::enableMenuAction(int action, bool enable)
{
switch(action)
{
case SOUND:
{
if(mSoundAction)
{
mSoundAction->setEnabled(enable);
}
break;
}
case PHOTO:
{
if(mPhotoAction)
{
mPhotoAction->setEnabled(enable);
}
break;
}
case SUBJECT: //subject action has to be removed
{
// remove mainmenu's "Add Subject" action
if(mSubjectAction)
{
this->menu()->removeAction(mSubjectAction);
mSubjectAction->setParent(NULL);
delete mSubjectAction;
mSubjectAction = NULL;
}
break;
}
case CCBCC: //CcBcc action has to be removed
{
// remove mainmenu's "Add Cc/Bcc" & "Add Subject" actions
if(mCcBccAction)
{
this->menu()->removeAction(mCcBccAction);
mCcBccAction->setParent(NULL);
delete mCcBccAction;
mCcBccAction = NULL;
}
break;
}
default:
break;
}
}
//---------------------------------------------------------------
// MsgUnifiedEditorView::vkbOpened
// @see header file
//---------------------------------------------------------------
void MsgUnifiedEditorView::vkbOpened()
{
hideChrome(true);
//get focussed object.
HbInputMethod* im = HbInputMethod::activeInputMethod();
HbInputFocusObject* focusObj = im->focusObject();
if(focusObj)
{
QObject* obj = focusObj->object();
mFocusedWidget = qobject_cast<QGraphicsWidget*>(obj);
HbAbstractEdit* editor = qobject_cast<HbAbstractEdit*>(obj);
if(editor)
{
connect(editor,SIGNAL(contentsChanged()),this,SLOT(ensureVisibility()));
}
}
QRectF appRect = mVkbHost->applicationArea();
if(this->mainWindow()->orientation() == Qt::Vertical)
{
this->setMaximumHeight(appRect.height());
mScrollArea->resize(-1,-1);
}
else
{
if(mFocusedWidget)
{
qreal vItemSpacing = 0.0;
style()->parameter("hb-param-margin-gene-middle-vertical",vItemSpacing);
mMinHeight = mFocusedWidget->minimumHeight();
mMaxHeight = mFocusedWidget->maximumHeight();
qreal newHeight = appRect.height()-vItemSpacing-1;
mFocusedWidget->setMinimumHeight(newHeight);
mFocusedWidget->setMaximumHeight(newHeight);
mFocusedWidget->resize(mFocusedWidget->rect().width(),newHeight);
}
}
//do layouting after some delay.
QTimer::singleShot(50,this,SLOT(doLayout()));
disconnect(mVkbHost,SIGNAL(keypadOpened()),this,SLOT(vkbOpened()));
connect(this->mainWindow(),SIGNAL(aboutToChangeOrientation()),this,SLOT(resetLayout()));
}
//---------------------------------------------------------------
// MsgUnifiedEditorView::vkbClosed
// @see header file
//---------------------------------------------------------------
void MsgUnifiedEditorView::vkbClosed()
{
hideChrome(false);
resetLayout();
connect(mVkbHost,SIGNAL(keypadOpened()),this,SLOT(vkbOpened()));
disconnect(this->mainWindow(),SIGNAL(aboutToChangeOrientation()),this,SLOT(resetLayout()));
}
//---------------------------------------------------------------
// MsgUnifiedEditorView::doLayout
// @see header file
//---------------------------------------------------------------
void MsgUnifiedEditorView::doLayout()
{
if(mFocusedWidget && mVkbHost->keypadStatus() == HbVkbHost::HbVkbStatusOpened)
{
if(this->mainWindow()->orientation() == Qt::Vertical)
{
ensureVisibility();
}
else
{
qreal vItemSpacing = 0.0;
style()->parameter("hb-param-margin-gene-middle-vertical",vItemSpacing);
if(mScrollArea->isAncestorOf(mFocusedWidget))
{
QPointF p = mScrollArea->mapFromScene(mFocusedWidget->scenePos());
mScrollArea->scrollContentsTo(p,0);
mScrollArea->enableScrolling(false);
mScrollArea->setVerticalScrollBarPolicy(HbScrollArea::ScrollBarAlwaysOff);
}
QPointF pp = mapFromScene(mFocusedWidget->scenePos());
qreal delta = pp.y() - vItemSpacing;
this->setTransform(QTransform::fromTranslate(0, -delta), true);
}
}
}
//---------------------------------------------------------------
// MsgUnifiedEditorView::resetLayout
// @see header file
//---------------------------------------------------------------
void MsgUnifiedEditorView::resetLayout()
{
//disconnect signal from previously focused object.
if(mFocusedWidget)
{
HbAbstractEdit* editor = qobject_cast<HbAbstractEdit*>(mFocusedWidget);
disconnect(editor,SIGNAL(contentsChanged()),this,SLOT(ensureVisibility()));
}
if(this->mainWindow()->orientation() == Qt::Vertical)
{
this->setMaximumHeight(-1);
mScrollArea->scrollContentsTo(QPointF(0,0),0);
mScrollArea->resize(-1,-1);
}
else
{
this->resetTransform();
if(mFocusedWidget)
{
if(mMinHeight >0)
{
mFocusedWidget->setMinimumHeight(mMinHeight);
mMinHeight = 0.0;
}
if(mMaxHeight >0)
{
mFocusedWidget->setMaximumHeight(mMaxHeight);
mMaxHeight = 0.0;
}
if(mScrollArea->isAncestorOf(mFocusedWidget))
{
mScrollArea->enableScrolling(true);
mScrollArea->setVerticalScrollBarPolicy(HbScrollArea::ScrollBarAutoHide);
mScrollArea->scrollContentsTo(QPointF(0,0),0);
}
mFocusedWidget = NULL;
}
}
}
//---------------------------------------------------------------
// MsgUnifiedEditorView::ensureVisibility
// @see header file
//---------------------------------------------------------------
void MsgUnifiedEditorView::ensureVisibility()
{
if(mFocusedWidget && mVkbHost->keypadStatus() == HbVkbHost::HbVkbStatusOpened)
{
if(this->mainWindow()->orientation() == Qt::Vertical)
{
if ( mScrollArea->isAncestorOf(mFocusedWidget))
{
QRectF scrollRect = mScrollArea->sceneBoundingRect();
QRectF editorRect = mFocusedWidget->sceneBoundingRect();
if (!scrollRect.contains(editorRect) ||
scrollRect.intersects(editorRect))
{
if (editorRect.height() < scrollRect.height()) {
// Whole editor rect fits into scroll area. Move it there.
if (editorRect.bottom() > scrollRect.bottom()) {
// Scroll upwards.
mScrollArea->ensureVisible(mScrollArea->contentWidget()->mapFromScene(editorRect.bottomLeft()));
} else {
// Scroll downwards.
mScrollArea->ensureVisible(mScrollArea->contentWidget()->mapFromScene(editorRect.topLeft()));
}
}
}
}
}
}
}
//---------------------------------------------------------------
// MsgUnifiedEditorView::focusChanged
// @see header file
//---------------------------------------------------------------
void MsgUnifiedEditorView::focusChanged()
{
//disconnect signal from previously focused object.
if(mFocusedWidget)
{
HbAbstractEdit* editor = qobject_cast<HbAbstractEdit*>(mFocusedWidget);
disconnect(editor,SIGNAL(contentsChanged()),this,SLOT(ensureVisibility()));
}
//connect signal for newly focused object.
HbInputMethod* im = HbInputMethod::activeInputMethod();
HbInputFocusObject* focusObj = im->focusObject();
if(focusObj)
{
QObject* obj = focusObj->object();
mFocusedWidget = qobject_cast<QGraphicsWidget*>(obj);
HbAbstractEdit* editor = qobject_cast<HbAbstractEdit*>(obj);
if(editor)
{
connect(editor,SIGNAL(contentsChanged()),this,SLOT(ensureVisibility()));
}
}
}
//---------------------------------------------------------------
// MsgUnifiedEditorView::onAudioSelected
// @see header file
//---------------------------------------------------------------
void MsgUnifiedEditorView::onAudioSelected(QString& filePath)
{
if (!filePath.isEmpty())
{
mScrollArea->setAudio(filePath);
}
}
//---------------------------------------------------------------
// MsgUnifiedEditorView::hideChrome
//
//---------------------------------------------------------------
void MsgUnifiedEditorView::hideChrome(bool hide)
{
if(hide)
{
this->setContentFullScreen(true);
this->hideItems(Hb::StatusBarItem | Hb::TitleBarItem);
}
else
{
this->setContentFullScreen(false);
this->showItems(Hb::StatusBarItem | Hb::TitleBarItem);
}
}
//---------------------------------------------------------------
// MsgUnifiedEditorView::doDelayedConstruction
//
//---------------------------------------------------------------
void MsgUnifiedEditorView::doDelayedConstruction()
{
createTempFolder();
//Create VKB instance and listen to VKB open and close signals.
mVkbHost = new MsgUniEditorVkbHost(this);
connect(mVkbHost, SIGNAL(focusChanged()), this, SLOT(focusChanged()));
connect(mVkbHost, SIGNAL(keypadOpened()), this, SLOT(vkbOpened()));
connect(mVkbHost, SIGNAL(keypadClosed()), this, SLOT(vkbClosed()));
disconnect(this->mainWindow(),SIGNAL(viewReady()),this,SLOT(doDelayedConstruction()));
}
//---------------------------------------------------------------
// MsgUnifiedEditorView::createTempFolder
//
//---------------------------------------------------------------
bool MsgUnifiedEditorView::createTempFolder()
{
// create editor's temp folder
QDir tempDir;
QString tempPath(editorTempPath());
bool result = tempDir.mkpath(tempPath);
return result;
}
//---------------------------------------------------------------
// MsgUnifiedEditorView::removeTempFolder
//
//---------------------------------------------------------------
void MsgUnifiedEditorView::removeTempFolder()
{
QDir tempDir;
QString tempPath(editorTempPath());
tempDir.cd(tempPath);
QStringList contentList(tempDir.entryList());
int contentCount = contentList.count();
for(int i=0; i<contentCount; i++)
{
tempDir.remove(contentList.at(i));
}
tempDir.cdUp();
tempDir.rmdir(UNIFIED_EDITOR_TEMP_FOLDER);
}
//---------------------------------------------------------------
// MsgUnifiedEditorView::setFocus
//
//---------------------------------------------------------------
void MsgUnifiedEditorView::setFocus(MsgUnifiedEditorBaseWidget* item)
{
if(item)
{
item->setFocus();
}
}
//---------------------------------------------------------------
// MsgUnifiedEditorView::onContentChanged
//
//---------------------------------------------------------------
void MsgUnifiedEditorView::onContentChanged()
{
mCanSaveToDrafts = true;
}
//---------------------------------------------------------------
// MsgUnifiedEditorView::onDialogDeleteMsg
//---------------------------------------------------------------
void MsgUnifiedEditorView::onDialogDeleteMsg(HbAction* action)
{
HbMessageBox *dlg = qobject_cast<HbMessageBox*> (sender());
if (action == dlg->actions().at(0)) {
mCanSaveToDrafts = false;
//delete if draft entry opened
if (mOpenedMessageId.getId() != -1) {
if (!mPluginLoader) {
mPluginLoader = new UniEditorPluginLoader(this);
}
UniEditorPluginInterface* pluginInterface = mPluginLoader->getUniEditorPlugin(
MsgUnifiedEditorMonitor::messageType());
pluginInterface->deleteDraftsEntry(mOpenedMessageId.getId());
}
//trigger back action.
HbAction* backAction = this->navigationAction();
if (backAction) {
backAction->trigger();
}
}
}
//---------------------------------------------------------------
// MsgUnifiedEditorView::onDialogSmsSettings
//---------------------------------------------------------------
void MsgUnifiedEditorView::onDialogSmsSettings(HbAction* action)
{
HbMessageBox *dlg = qobject_cast<HbMessageBox*> (sender());
if (action == dlg->actions().at(0))
{
QVariantList params;
params << MsgBaseView::MSGSETTINGS;// target view
params << MsgBaseView::UNIEDITOR; // source view
params << MsgSettingsView::SMSView;
emit switchView(params);
}
}
//---------------------------------------------------------------
// MsgUnifiedEditorView::onDialogMmsSettings
//---------------------------------------------------------------
void MsgUnifiedEditorView::onDialogMmsSettings(HbAction* action)
{
HbMessageBox *dlg = qobject_cast<HbMessageBox*> (sender());
if (action == dlg->actions().at(0)) {
QVariantList params;
params << MsgBaseView::MSGSETTINGS;// target view
params << MsgBaseView::UNIEDITOR; // source view
params << MsgSettingsView::MMSView;
emit switchView(params);
}
}
// ----------------------------------------------------------------------------
// MsgUnifiedEditorView::isReplyPathBroken
// @see header
// ----------------------------------------------------------------------------
bool MsgUnifiedEditorView::isReplyPathBroken()
{
// 1. Never set for MMS
// 2. if additional recipients exits
if( (MsgUnifiedEditorMonitor::messageType() == ConvergedMessage::Mms) ||
(MsgUnifiedEditorMonitor::msgAddressCount() != 1) )
{
// broken
return true;
}
// 3. if only recipient is not same as originating SME
QString dispName;
int phCount;
int origCntLocalId = MsgContactHandler::resolveContactDisplayName(
mOriginatingSME, dispName, phCount);
int currCntLocalId = -1;
QString currAddress;
ConvergedMessageAddress* currentAdd = mScrollArea->currentAddress();
if(currentAdd)
{
currAddress = currentAdd->address();
delete currentAdd;
}
if(origCntLocalId != -1)
{
currCntLocalId = MsgContactHandler::resolveContactDisplayName(
currAddress, dispName, phCount);
}
if(currCntLocalId != -1)
{ // both are mapped contacts present in contacts db
if(currCntLocalId != origCntLocalId)
{
return true;
}
}
else
{ // atleast one contact is not present in contacts db
// direct compare
UniEditorGenUtils* genUtils = q_check_ptr(new UniEditorGenUtils);
bool compareResult = false;
TRAP_IGNORE(
compareResult = genUtils->MatchPhoneNumberL(
*XQConversions::qStringToS60Desc(mOriginatingSME),
*XQConversions::qStringToS60Desc(currAddress))
);
delete genUtils;
if(!compareResult)
{
return true;
}
}
return false;
}
//EOF