messagingapp/msgui/unifiededitor/src/msgunieditorview.cpp
author hgs
Tue, 19 Oct 2010 11:30:16 +0530
changeset 76 60a8a215b0ec
parent 73 ecf6a73a9186
permissions -rw-r--r--
201041

/*
 * 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