diff -r 84d9eb65b26f -r 518b245aa84c messagingapp/msgui/unifiededitor/src/msgunieditoraddress.cpp --- a/messagingapp/msgui/unifiededitor/src/msgunieditoraddress.cpp Mon May 03 12:29:07 2010 +0300 +++ b/messagingapp/msgui/unifiededitor/src/msgunieditoraddress.cpp Fri Jun 25 15:47:40 2010 +0530 @@ -16,40 +16,55 @@ */ // INCLUDES -#include "debugtraces.h" +#include #include #include #include -#include +#include #include #include #include #include // KCRUidTelephonyConfiguration #include +#include +#include // Common phone number validity checker +#include // USER INCLUDES +#include "debugtraces.h" #include "msgunieditoraddress.h" -#include "msgunifiededitorlineedit.h" +#include "msgunieditorlineedit.h" +#include "msgunieditormonitor.h" +#include "UniEditorGenUtils.h" const QString PBK_ICON("qtg_mono_contacts"); -const QString SEND_ICON("qtg_mono_send"); +const QString REPLACEMENT_STR("; "); +const QString COMMA_SEPERATOR(","); // Constants const int KDefaultGsmNumberMatchLength = 7; //matching unique ph numbers +#define LOC_SMS_RECIPIENT_LIMIT_REACHED hbTrId("txt_messaging_dialog_number_of_recipients_exceeded") +#define LOC_MMS_RECIPIENT_LIMIT_REACHED hbTrId("txt_messaging_dpopinfo_unable_to_add_more_recipien") +#define LOC_DIALOG_OK hbTrId("txt_common_button_ok") +#define LOC_BUTTON_CANCEL hbTrId("txt_common_button_cancel") +#define LOC_INVALID_RECIPIENT hbTrId("txt_messaging_dialog_invalid_recipient_found") +#define LOC_INVALID_RECIPIENT_NOT_ADDED hbTrId("txt_messaging_dialog_invalid_recipient_not_added") +#define LOC_INVALID_RECIPIENTS_NOT_ADDED hbTrId("txt_messaging_dialog_invalid_recipients_not_added") + + + + MsgUnifiedEditorAddress::MsgUnifiedEditorAddress( const QString& label, - const QString& pluginPath, QGraphicsItem *parent ) : -HbWidget(parent), -mPluginPath(pluginPath) +MsgUnifiedEditorBaseWidget(parent), +mSkipMaxRecipientQuery(false), +mAboutToExceedMaxSmsRecipients(false), +mAboutToExceedMaxMmsRecipients(false), +mExceedsMaxMmsRecipientsBy(0) { - #ifdef _DEBUG_TRACES_ - qDebug() << "MsgUnifiedEditorAddress calling HbStyle::registerPlugin"; - #endif + this->setContentsMargins(0,0,0,0); - this->setContentsMargins(0,0,0,0); - setPluginBaseId(style()->registerPlugin(mPluginPath)); - mLaunchBtn = new HbPushButton(this); HbStyle::setItemName(mLaunchBtn,"launchBtn"); connect(mLaunchBtn,SIGNAL(clicked()),this,SLOT(fetchContacts())); @@ -61,24 +76,22 @@ mAddressEdit->setMaxRows(40); connect(mAddressEdit, SIGNAL(contentsChanged(const QString&)), - this, SLOT(onContentsAdded(const QString&))); + this, SLOT(onContentsChanged(const QString&))); - // add "Send" action in VKB - HbEditorInterface editorInterface(mAddressEdit); mAddressEdit->setInputMethodHints(Qt::ImhPreferNumbers); - HbAction *sendAction = new HbAction(HbIcon(SEND_ICON), QString(),this); - connect(sendAction, SIGNAL(triggered()),this, SIGNAL(sendMessage())); - editorInterface.addAction(sendAction); - - } +} MsgUnifiedEditorAddress::~MsgUnifiedEditorAddress() { - style()->unregisterPlugin(mPluginPath); + //TODO: Should remove this code depending on orbit's reply whether it is needed + //to unregister the same plugin registered on two different widgets twice. + //style()->unregisterPlugin(mPluginPath); } void MsgUnifiedEditorAddress::fetchContacts() { + mLaunchBtn->blockSignals(true); + QList args; QString serviceName("com.nokia.services.phonebookservices"); QString operation("fetch(QString,QString,QString)"); @@ -87,39 +100,48 @@ request = appManager.create(serviceName, "Fetch", operation, true); // embedded if ( request == NULL ) { - return; + return; } // Result handlers connect (request, SIGNAL(requestOk(const QVariant&)), this, SLOT(handleOk(const QVariant&))); connect (request, SIGNAL(requestError(int,const QString&)), this, SLOT(handleError(int,const QString&))); - - args << QString(tr("Phonebook")); + + args << QString(tr("Phonebook")); args << KCntActionAll; args << KCntFilterDisplayAll; - + request->setArguments(args); request->send(); delete request; + + //unblock click signal after some delay. + QTimer::singleShot(250,this,SLOT(unblockSignals())); } void MsgUnifiedEditorAddress::handleOk(const QVariant& value) { - CntServicesContactList contactList; - contactList = qVariantValue(value); + CntServicesContactList contactList = + qVariantValue(value); + int count = contactList.count(); - for(int i = 0; i < contactList.count(); i++ ) + ConvergedMessageAddressList addrlist; + for(int i = 0; i < count; i++ ) { - mAddressMap.insert(contactList[i].mPhoneNumber, contactList[i].mDisplayName); - if(!contactList[i].mDisplayName.isEmpty()) + ConvergedMessageAddress* address = + new ConvergedMessageAddress(); + if(!contactList[i].mPhoneNumber.isEmpty()) { - mAddressEdit->setText(contactList[i].mDisplayName); + address->setAddress(contactList[i].mPhoneNumber); } else { - mAddressEdit->setText(contactList[i].mPhoneNumber); + address->setAddress(contactList[i].mEmailAddress); } + address->setAlias(contactList[i].mDisplayName); + addrlist << address; } + setAddresses(addrlist); } void MsgUnifiedEditorAddress::handleError(int errorCode, const QString& errorMessage) @@ -128,31 +150,47 @@ Q_UNUSED(errorCode) } -ConvergedMessageAddressList MsgUnifiedEditorAddress::addresses() +// ---------------------------------------------------------------------------- +// MsgUnifiedEditorAddress::addresses +// @see header +// ---------------------------------------------------------------------------- +ConvergedMessageAddressList MsgUnifiedEditorAddress::addresses( + bool removeDuplicates) { - #ifdef _DEBUG_TRACES_ - qCritical() << "MsgUnifiedEditorAddress::address start"; +#ifdef _DEBUG_TRACES_ + qCritical() << "MsgUnifiedEditorAddress::addresses start"; #endif // sync-up map to account for user-actions on edit-field syncDeletionsToMap(); syncAdditionsToMap(); - QStringList uniqueAddr; - uniqueAddr = uniqueAddressList(); - ConvergedMessageAddressList addresses; - foreach(QString addr, uniqueAddr) + if(removeDuplicates) { - ConvergedMessageAddress* address = new ConvergedMessageAddress; - address->setAddress(addr); - if(!mAddressMap.value(addr).isEmpty()) + QStringList uniqueAddr; + uniqueAddr = uniqueAddressList(); + foreach(QString addr, uniqueAddr) { + ConvergedMessageAddress* address = new ConvergedMessageAddress; + address->setAddress(addr); address->setAlias(mAddressMap.value(addr)); - } - addresses.append(address); + addresses.append(address); + } } - #ifdef _DEBUG_TRACES_ - qCritical() << "MsgUnifiedEditorAddress::address end"; + else + { + QMap::iterator i = mAddressMap.begin(); + while (i != mAddressMap.end()) + { + ConvergedMessageAddress* address = new ConvergedMessageAddress; + address->setAddress(i.key()); + address->setAlias(i.value()); + addresses.append(address); + i++; + } + } +#ifdef _DEBUG_TRACES_ + qCritical() << "MsgUnifiedEditorAddress::addresses end"; #endif return addresses; } @@ -164,60 +202,177 @@ void MsgUnifiedEditorAddress::setAddresses(ConvergedMessageAddressList addrlist) { - int count = addrlist.count(); - for(int i = 0; i < count; i++ ) + // avoid processing if no info available + if(addrlist.count() == 0) { - mAddressMap.insert(addrlist[i]->address(), addrlist[i]->alias()); - if(!addrlist[i]->alias().isEmpty()) + return; + } + + // ensure flags are reset before starting the addr addition + mAboutToExceedMaxSmsRecipients = false; + mAboutToExceedMaxMmsRecipients = false; + mExceedsMaxMmsRecipientsBy = 0; + + // first, we check if MMS max-recipient count will exceed + int count = addrlist.count(); + int futureCount = count + MsgUnifiedEditorMonitor::msgAddressCount(); + if(futureCount > MsgUnifiedEditorMonitor::maxMmsRecipients()) + { + mAboutToExceedMaxMmsRecipients = true; + mExceedsMaxMmsRecipientsBy = + futureCount - MsgUnifiedEditorMonitor::maxMmsRecipients(); + } + // else, check if SMS max-recipient count will exceed + else if(!mSkipMaxRecipientQuery) + { + futureCount = count + addressCount(); + if( (addressCount() <= MsgUnifiedEditorMonitor::maxSmsRecipients()) && + (futureCount > MsgUnifiedEditorMonitor::maxSmsRecipients()) ) + { + mAboutToExceedMaxSmsRecipients = true; + } + } + + int invalidCount= 0; + QString invalidContacts; + for(int i = 0; i < count; i++ ) { - mAddressEdit->setText(addrlist[i]->alias()); - } - else + bool isValid = false; + isValid = checkValidAddress(addrlist.at(i)->address()); + if(!isValid) + { + invalidCount ++; + // append the comma till last but one contact. + // add the invalid ocntacts to the " , " seperated string. + if(invalidCount > 1) + { + invalidContacts.append(COMMA_SEPERATOR); + } + invalidContacts.append(addrlist.at(i)->alias()); + } + else + { + mAddressMap.insertMulti(addrlist[i]->address(), addrlist[i]->alias()); + if(!addrlist[i]->alias().isEmpty()) + { + mAddressEdit->setText(addrlist[i]->alias()); + } + else + { + mAddressEdit->setText(addrlist[i]->address(), false); + } + } + } + if(invalidCount) { - mAddressEdit->setText(addrlist[i]->address()); + QString invalidStr; + (invalidCount == 1)?(invalidStr = QString(LOC_INVALID_RECIPIENT_NOT_ADDED)) :(invalidStr = QString(LOC_INVALID_RECIPIENTS_NOT_ADDED)); + // append line seperator + invalidStr.append("
"); + invalidStr.append(invalidContacts); + HbMessageBox::information(invalidStr); } - } + + // addition operation complete, reset flags + mAboutToExceedMaxSmsRecipients = false; + mAboutToExceedMaxMmsRecipients = false; + mExceedsMaxMmsRecipientsBy = 0; } int MsgUnifiedEditorAddress::contactMatchDigits() - { +{ // Read the amount of digits to be used in contact matching - // The key is owned by PhoneApp - CRepository* repository = CRepository::NewLC(KCRUidTelConfiguration); + // The key is owned by PhoneApp int matchDigitCount = 0; - if ( repository->Get(KTelMatchDigits, matchDigitCount) == KErrNone ) + TRAP_IGNORE( + CRepository* repository = CRepository::NewLC(KCRUidTelConfiguration); + if ( repository->Get(KTelMatchDigits, matchDigitCount) == KErrNone ) { - // Min is 7 - matchDigitCount = Max(matchDigitCount, KDefaultGsmNumberMatchLength); + // Min is 7 + matchDigitCount = Max(matchDigitCount, KDefaultGsmNumberMatchLength); } - CleanupStack::PopAndDestroy(); // repository - + CleanupStack::PopAndDestroy(); // repository + ); return matchDigitCount; +} - } - -void MsgUnifiedEditorAddress::onContentsAdded(const QString& text) +void MsgUnifiedEditorAddress::onContentsChanged(const QString& text) { - if(!text.isEmpty()) + // Max MMS recipient count check + if( mAboutToExceedMaxMmsRecipients || + (MsgUnifiedEditorMonitor::msgAddressCount() >= MsgUnifiedEditorMonitor::maxMmsRecipients()) ) { + if(mAboutToExceedMaxMmsRecipients) + {// show discreet note only once + --mExceedsMaxMmsRecipientsBy; + if(!mExceedsMaxMmsRecipientsBy) + { + HbNotificationDialog::launchDialog( + LOC_MMS_RECIPIENT_LIMIT_REACHED); + } + resetToPrevious(); + } + else + { + // update monitor data + emit contentChanged(); + if(MsgUnifiedEditorMonitor::msgAddressCount() > MsgUnifiedEditorMonitor::maxMmsRecipients()) + { + HbNotificationDialog::launchDialog( + LOC_MMS_RECIPIENT_LIMIT_REACHED); + resetToPrevious(); + // reset monitor data + emit contentChanged(); + } + else + { + mPrevBuffer = text; + } + } + return; + } + + // Max SMS recipient count check + if( !mSkipMaxRecipientQuery && + (MsgUnifiedEditorMonitor::messageType() == ConvergedMessage::Sms) && + (mAddressEdit->addresses().count() > MsgUnifiedEditorMonitor::maxSmsRecipients()) ) + { + // when we show this dialog, we don't want the intermediate states + // to be signalled to us disconnect(mAddressEdit, SIGNAL(contentsChanged(const QString&)), - this, SLOT(onContentsAdded(const QString&))); + this, SLOT(onContentsChanged(const QString&))); + QTimer::singleShot(50, this, SLOT(handleRecipientLimitReached())); + } + else + { + if(!mAboutToExceedMaxSmsRecipients) + {// remember addresses before the block insertion started + mPrevBuffer = text; + } emit contentChanged(); - connect(mAddressEdit, SIGNAL(contentsChanged(const QString&)), - this, SLOT(onContentsRemoved(const QString&))); } } -void MsgUnifiedEditorAddress::onContentsRemoved(const QString& text) +void MsgUnifiedEditorAddress::handleRecipientLimitReached() { - if(text.isEmpty()) - { - disconnect(mAddressEdit, SIGNAL(contentsChanged(const QString&)), - this, SLOT(onContentsRemoved(const QString&))); - emit contentChanged(); - connect(mAddressEdit, SIGNAL(contentsChanged(const QString&)), - this, SLOT(onContentsAdded(const QString&))); - } + HbMessageBox* dlg = new HbMessageBox(HbMessageBox::MessageTypeQuestion); + dlg->setAttribute(Qt::WA_DeleteOnClose); + dlg->setFocusPolicy(Qt::NoFocus); + dlg->setTimeout(HbPopup::NoTimeout); + + dlg->setText(LOC_SMS_RECIPIENT_LIMIT_REACHED); + + dlg->clearActions(); + HbAction* okAction = new HbAction(LOC_DIALOG_OK,dlg); + dlg->addAction(okAction); + + HbAction* cancelAction = new HbAction(LOC_BUTTON_CANCEL,dlg); + dlg->addAction(cancelAction); + + dlg->open(this,SLOT(onMaxRecipientsReached(HbAction*))); + // reconnect to get back updates + connect(mAddressEdit, SIGNAL(contentsChanged(const QString&)), + this, SLOT(onContentsChanged(const QString&))); } void MsgUnifiedEditorAddress::syncDeletionsToMap() @@ -236,6 +391,18 @@ } else { + // ensure that the matched contact is removed from the + // address's list + int matchedIndex = addrList.indexOf(i.value()); + if(matchedIndex == -1) + { + matchedIndex = addrList.indexOf(i.key()); + } + if(matchedIndex != -1) + { + addrList.removeAt(matchedIndex); + } + // now go to next index in map ++i; } } @@ -243,19 +410,19 @@ void MsgUnifiedEditorAddress::syncAdditionsToMap() { - QStringList mapAddrList = mAddressMap.values(); - // remove already mapped addresses from edit-field QStringList userInputAddrList(mAddressEdit->addresses()); + QStringList mapAddrList = mAddressMap.values(); + mapAddrList << mAddressMap.keys(); foreach(QString addr, mapAddrList) { - userInputAddrList.removeAll(addr); + userInputAddrList.removeOne(addr); } // add the unmapped addresses to address-map foreach(QString addr, userInputAddrList) { - mAddressMap.insertMulti(addr, addr); + mAddressMap.insertMulti(addr, QString()); } } @@ -267,7 +434,7 @@ { for(int i =j+1;isetFocus(Qt::MouseFocusReason); +} + +// ---------------------------------------------------------------------------- +// MsgUnifiedEditorAddress::resetToPrevious +// @see header +// ---------------------------------------------------------------------------- +void MsgUnifiedEditorAddress::resetToPrevious() +{ + // when we do this reset operation, we don't want the intermediate states + // to be signalled to us + disconnect(mAddressEdit, SIGNAL(contentsChanged(const QString&)), + this, SLOT(onContentsChanged(const QString&))); + + mAddressEdit->clearContent(); + QStringList list = mPrevBuffer.split(REPLACEMENT_STR, + QString::SkipEmptyParts); + int count = list.count(); + QStringList valList = mAddressMap.values(); + for(int i=0; isetText(addr); + } + else + { + mAddressEdit->setText(addr, false); + } + } + syncDeletionsToMap(); + connect(mAddressEdit, SIGNAL(contentsChanged(const QString&)), + this, SLOT(onContentsChanged(const QString&))); +} + +// ---------------------------------------------------------------------------- +// MsgUnifiedEditorAddress::onMaxRecipientsReached +// @see header +// ---------------------------------------------------------------------------- +void MsgUnifiedEditorAddress::onMaxRecipientsReached(HbAction* action) +{ + HbMessageBox *dlg = qobject_cast (sender()); + if (action == dlg->actions().at(0)) { + // accept new content, update prev-buffer + emit contentChanged(); + mPrevBuffer = mAddressEdit->content(); + } + else { + // reject the new content, keep the old + resetToPrevious(); + } +} + +// ---------------------------------------------------------------------------- +// MsgUnifiedEditorAddress::validateContacts +// @see header +// ---------------------------------------------------------------------------- +bool MsgUnifiedEditorAddress::validateContacts() +{ + UniEditorGenUtils* genUtils = new UniEditorGenUtils; + + // sync-up map to account for user-actions on address-field + syncDeletionsToMap(); + syncAdditionsToMap(); + + // get the list of contacts in address-field + QStringList fieldAddresses(mAddressEdit->addresses()); + + bool isValid = true; + foreach(QString addr, fieldAddresses) + { + // run address validation only if address is unmapped + // (i.e. user-inserted) + if(mAddressMap.contains(addr)) + { + isValid = checkValidAddress(addr); + if(!isValid) + { + mAddressEdit->highlightInvalidString(addr); + QString invalidAddrStr = + QString(LOC_INVALID_RECIPIENT).arg(addr); + HbMessageBox* dlg = new HbMessageBox(invalidAddrStr, + HbMessageBox::MessageTypeInformation); + dlg->setDismissPolicy(HbPopup::TapInside); + dlg->setTimeout(HbPopup::NoTimeout); + dlg->setAttribute(Qt::WA_DeleteOnClose, true); + dlg->open(this, SLOT(handleInvalidContactDialog(HbAction*))); + break; + } + } + } + + return isValid; +} +// ---------------------------------------------------------------------------- +// MsgUnifiedEditorAddress::checkValidAddress +// @see header +// ---------------------------------------------------------------------------- +bool MsgUnifiedEditorAddress::checkValidAddress(const QString& addr) + { + bool isValid = false; + // 1. perform number validation + isValid = CommonPhoneParser::IsValidPhoneNumber( + *XQConversions::qStringToS60Desc(addr), + CommonPhoneParser::ESMSNumber ); + + // 2. if number validity fails, then perform email addr validation + UniEditorGenUtils* genUtils = new UniEditorGenUtils; + if(!isValid) + { // additional check for MMS only + isValid = genUtils->IsValidEmailAddress( + *XQConversions::qStringToS60Desc(addr) ); + } + delete genUtils; + return isValid; + } + +void MsgUnifiedEditorAddress::handleInvalidContactDialog( + HbAction* act) +{ + Q_UNUSED(act); + QTimer::singleShot(250, this, SLOT(setFocus())); +} + +void MsgUnifiedEditorAddress::unblockSignals() +{ + mLaunchBtn->blockSignals(false); +} + Q_IMPLEMENT_USER_METATYPE(CntServicesContact) Q_IMPLEMENT_USER_METATYPE_NO_OPERATORS(CntServicesContactList)