diff -r d4f567ce2e7c -r 5b6f26637ad3 phonebookengines/cntlistmodel/src/cntlistmodel.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/phonebookengines/cntlistmodel/src/cntlistmodel.cpp Tue Aug 31 15:05:21 2010 +0300 @@ -0,0 +1,867 @@ +/* +* 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: +* +*/ +#include "cntlistmodel_p.h" +#include "cntlistmodel.h" +#include "cntcache.h" +#include "cntdebug.h" + +#include +#include +#include + +#include +#include +#include +#include // for reading cenrep keys +#include + +const uint dummyMyCardId = 0; + +/*! + * Constuct a new contact model instance. The model will own its handle + * to the default persisted store. + */ +CntListModel::CntListModel(const QContactFilter& contactFilter, + bool showMyCard, + QObject *parent) + : QAbstractListModel(parent) +{ + CNT_ENTRY + m_defaultIcon = HbIcon("qtg_large_avatar"); + m_defaultMyCardIcon = HbIcon("qtg_large_avatar_mycard"); + + d = new CntListModelData(contactFilter, showMyCard); + d->m_contactManager = new QContactManager; + d->m_ownedContactManager = true; + d->m_cache = CntCache::instance(); + connect(d->m_cache, SIGNAL(contactInfoUpdated(QContactLocalId)), this, SLOT(handleContactInfoUpdated(QContactLocalId))); + d->m_myCardId = d->m_contactManager->selfContactId(); + if (doConstruct() != QContactManager::NoError) { + throw("exception"); + } + CNT_EXIT +} + +/*! + * Construct a new CntListModel object using manager as the QContactManager + * instance to communicate with the contacts database. + * + * \param manager A QContactManager instance to be used for + * communications with the contacts persistant store. + */ +CntListModel::CntListModel(QContactManager* manager, + const QContactFilter& contactFilter, + bool showMyCard, + QObject *parent) + : QAbstractListModel(parent) +{ + CNT_ENTRY + m_defaultIcon = HbIcon("qtg_large_avatar"); + m_defaultMyCardIcon = HbIcon("qtg_large_avatar_mycard"); + + d = new CntListModelData(contactFilter, showMyCard); + + d->m_contactManager = manager; + d->m_cache = CntCache::instance(); + + connect(d->m_cache, SIGNAL(contactInfoUpdated(QContactLocalId)), this, SLOT(handleContactInfoUpdated(QContactLocalId))); + d->m_myCardId = d->m_contactManager->selfContactId(); + + if (doConstruct() != QContactManager::NoError) { + throw("exception"); + } + CNT_EXIT +} + +CntListModel::~CntListModel() +{ +} + +/*! + * Return the data to be used by the view or delegates for a particular + * item and role. + * + * \param index The index of the item to return data about. + * \param role The data should be relevant for this particular purpose. + * \return QVariant The data for the specified index and role. + */ +QVariant CntListModel::data(const QModelIndex &index, int role) const +{ + CNT_ENTRY + int row = index.row(); + + // check that row is ok + if (!validRowId(row)) { + // invalid row + return QVariant(); + } + + // update current contact if needed + if (row != d->m_currentRow ) { + if (d->m_contactIds[row] == dummyMyCardId) { + // row contains dummy MyCard, so create dummy CntContactInfo + d->m_currentContact = CntContactInfo(); + } + else { + d->m_currentContact = d->m_cache->fetchContactInfo(row, d->m_contactIds); + } + d->m_currentRow = row; + } + + if (role == Qt::DisplayRole) { + return dataForRole(row, role); + } + else if (role == Hb::IndexFeedbackRole) { + if (row == 0 && (d->m_myCardId == d->m_contactIds[0] || dummyMyCardId == d->m_contactIds[0])) { + // do not include MyCard in index feedback + return QVariant(); + } + else { + return dataForRole(row, role).toStringList().at(0).toUpper(); + } + } + else if (role == Qt::BackgroundRole) { + if (d->m_myCardId == d->m_contactIds[row] || dummyMyCardId == d->m_contactIds[row]) { + return HbFrameBackground("qtg_fr_list_parent_normal", HbFrameDrawer::NinePieces); + } + } + else if (role == Qt::DecorationRole) { + if (d->m_currentRowSetting == CntTwoRowsNameAndPhoneNumber) { + //icon fits only if user selected 2 rows in each name list item + QList icons; + HbIcon avatar = d->m_currentContact.icon1(); + HbIcon statusIcon = d->m_currentContact.icon2(); + + if (!avatar.isNull()) { + icons.append(avatar); + } + else if (d->m_myCardId == d->m_contactIds[row] || dummyMyCardId == d->m_contactIds[row]) { + icons.append(m_defaultMyCardIcon); + } + else { + icons.append(m_defaultIcon); + } + + if (!statusIcon.isNull()) { + icons.append(statusIcon); + } + + return icons; + } + else { + return QVariant(); + } + } + CNT_EXIT + return QVariant(); +} + +/*! + * Get the number of rows (contacts) in this model. + * + * \param parent Optional parent index value. + * \return Number of contacts in this model. + */ +int CntListModel::rowCount(const QModelIndex& /*parent*/) const +{ + return d->m_contactIds.count(); +} + +/*! + * Read a full contact entry from the database for the given model + * index value. Only the row part of the index information will be + * read. This is just an overload of CntListModel::contact() that + * supports old behaviour and calls: + * CntListModel::contact(int row); + * + * The entry at the requested row will have its full contact information + * (all fields) read from the database and returned as a QContact instance. + * + * \param index Index for the sought contact entry in this model. + * \return A newly constructed QContact instance for this entry - ownership + * is transferred to the caller. + * + */ +QContact CntListModel::contact(const QModelIndex &index) const +{ + return contact(index.row()); +} + +/*! + * Returns the id for the contact at the requested row. + * + * \param index Index for the sought contact entry in this model. + * \return The id for the contact, 0 if invalid index. + * + */ +QContactLocalId CntListModel::contactId(const QModelIndex &index) const +{ + CNT_ENTRY + + if (!validRowId(index.row())) { + return 0; + } + + CNT_EXIT + return d->m_contactIds[index.row()]; +} + +/*! + * Return an index that points to the row relating to the supplied contact. + * E.g. if the contact is at row 7, the index with the following properties + * is returned: + * index.row() == 7 + + * \param contact The contact for whose row an index is required + * \return a QModelIndex with the row set to match that of the contact. + */ +QModelIndex CntListModel::indexOfContact(const QContact &contact) const +{ + return createIndex(rowId(contact.localId()), 0); +} + +/*! + * Return an index that points to the row relating to the supplied contact id. + * E.g. if the contact with this id is at row 7, the index with the following + * properties is returned: + * index.row() == 7 + + * \param contactId The id of the contact for whose row an index is required + * \return a QModelIndex with the row set to match that of the contact id. + */ +QModelIndex CntListModel::indexOfContactId(const QContactLocalId &contactId) const +{ + return createIndex(rowId(contactId), 0); +} + +/*! + * Return the contact manager used by this model. + * + * \return Reference to contact manager. + */ +QContactManager& CntListModel::contactManager() const +{ + return *d->m_contactManager; +} + +/*! + * Set new filter and sort order for the model. + * + * \param contactFilter New contact filter. + * \param contactSortOrders New sort order. + */ +void CntListModel::setFilter(const QContactFilter& contactFilter) +{ + CNT_ENTRY + + d->setFilter(contactFilter); + + //refresh model + updateContactIdsArray(); + + beginResetModel(); + reset(); + endResetModel(); + + CNT_EXIT +} + +void CntListModel::setSortOrder() +{ + XQSettingsKey nameOrderKey(XQSettingsKey::TargetCentralRepository, + KCRCntSettings.iUid, + KCntNameOrdering); + XQSettingsManager settingsMng; + int order = settingsMng.readItemValue(nameOrderKey, XQSettingsManager::TypeInt).toInt(); + + QContactSortOrder sortOrderFirstName; + sortOrderFirstName.setDetailDefinitionName(QContactName::DefinitionName, + QContactName::FieldFirstName); + sortOrderFirstName.setCaseSensitivity(Qt::CaseInsensitive); + + QContactSortOrder sortOrderLastName; + sortOrderLastName.setDetailDefinitionName(QContactName::DefinitionName, + QContactName::FieldLastName); + sortOrderLastName.setCaseSensitivity(Qt::CaseInsensitive); + + QList sortOrders; + if (order == CntOrderLastFirst || order == CntOrderLastCommaFirst) { + sortOrders.append(sortOrderLastName); + sortOrders.append(sortOrderFirstName); + } else { + sortOrders.append(sortOrderFirstName); + sortOrders.append(sortOrderLastName); + } + + d->m_sortOrders = sortOrders; +} + +void CntListModel::refreshModel() +{ + CNT_ENTRY + + setSortOrder(); + d->m_cache->clearCache(); + d->m_currentRow = -1; + + //refresh model + updateContactIdsArray(); + + beginResetModel(); + reset(); + endResetModel(); + + CNT_EXIT +} + +/*! + * Enable/disable MyCard appearance in the model. + * + * \param enabled Status of MyCard appearance in the model. + */ +void CntListModel::showMyCard(bool enabled) +{ + CNT_ENTRY + if (d->m_showMyCard == enabled) { + return; + } + + QContactLocalId myCardId = d->m_myCardId; + if (enabled) { + //add MyCard to the list + if (myCardId <= 0) { + // create a placeholder for MyCard + d->m_contactIds.insert(0, dummyMyCardId); + } + else { + d->m_contactIds.insert(0, myCardId); + } + } + else { + // remove MyCard from the list + if (myCardId <= 0) { + d->m_contactIds.removeOne(dummyMyCardId); + } + else { + d->m_contactIds.removeOne(myCardId); + } + } + d->m_showMyCard = enabled; + d->m_currentRow = -1; + + beginResetModel(); + reset(); + endResetModel(); + CNT_EXIT +} + +/*! + * Returns MyCard status: shown or not. + * + * \return true if MyCard is shown, false otherwise. + */ +bool CntListModel::myCardStatus() const +{ + return d->m_showMyCard; +} + +/*! + * Returns MyCard id. + * + * \return MyCard id. + */ +QContactLocalId CntListModel::myCardId() const +{ + return d->m_myCardId; +} + +/*! + * Initializes model data and attaches database notifications to handlers. + * + * \return Error status + */ +int CntListModel::doConstruct() +{ + CNT_ENTRY + + int error = initializeData(); + + // Attach database notifications to handlers. + connect(d->m_contactManager, SIGNAL(contactsAdded(const QList&)), this, SLOT(handleAdded(const QList&))); + connect(d->m_contactManager, SIGNAL(contactsChanged(const QList&)), this, SLOT(handleChanged(const QList&))); + connect(d->m_contactManager, SIGNAL(contactsRemoved(const QList&)), this, SLOT(handleRemoved(const QList&))); + connect(d->m_contactManager, SIGNAL(selfContactIdChanged(const QContactLocalId&, const QContactLocalId&)), this, SLOT(handleMyCardChanged(const QContactLocalId&, const QContactLocalId&))); + connect(d->m_contactManager, SIGNAL(dataChanged()), this, SLOT(refreshModel())); + connect(d->m_contactManager, SIGNAL(relationshipsAdded(const QList&)), this, SLOT(handleAddedRelationship(const QList&))); + connect(d->m_contactManager, SIGNAL(relationshipsRemoved(const QList&)), this, SLOT(handleRemovedRelationship(const QList&))); + + // Get current setting how to show an item in the name list and subscribe for changes + d->m_Settings = new XQSettingsManager; + + d->m_NameListRowSettingkey = new XQSettingsKey(XQSettingsKey::TargetCentralRepository, + KCRCntSettings.iUid, + KCntNameListRowSetting); + d->m_currentRowSetting = d->m_Settings->readItemValue(*d->m_NameListRowSettingkey, XQSettingsManager::TypeInt).toInt(); + d->m_Settings->startMonitoring(*d->m_NameListRowSettingkey, XQSettingsManager::TypeInt); + connect(d->m_Settings, SIGNAL(valueChanged(const XQSettingsKey&, const QVariant&)), this, SLOT(handleRowSettingChanged(const XQSettingsKey&, const QVariant&))); + + CNT_EXIT + + return error; +} + +/*! + * Initializes d->m_contactIds to contain contact Ids from the database. + * + * \return Error status + */ +int CntListModel::initializeData() +{ + CNT_ENTRY + int error(QContactManager::NoError); + + setSortOrder(); + + // Get a list of all contact IDs in the database. + updateContactIdsArray(); + + CNT_EXIT + return error; +} + +/*! + * Gets the filtered list of the contact Ids in a sorted order + * + * \return Error status + */ +void CntListModel::updateContactIdsArray() +{ + CNT_ENTRY + d->m_contactIds = d->m_contactManager->contactIds(d->m_filter, + d->m_sortOrders); + + //find MyCard contact and move it to the first position + QContactLocalId myCardId = d->m_myCardId; + if (myCardId > 0) { + // MyCard exists + d->m_contactIds.removeOne(myCardId); + if (d->m_showMyCard) { + d->m_contactIds.insert(0, myCardId); + } + } + else if (d->m_showMyCard) { + // create a placeholder for MyCard + d->m_contactIds.insert(0, dummyMyCardId); + } + CNT_EXIT +} + +/*! + * Read a full contact entry from the database for the row number. + * + * The entry at the requested row will have its full contact information + * (all fields) read from the database and returned as a QContact instance. + * + * \param row Row at which the sought contact entry is in this model. + * \return A newly constructed QContact instance for this entry - ownership + * is transferred to the caller. + * + */ +QContact CntListModel::contact(int row) const +{ + CNT_ENTRY + if (!validRowId(row) || d->m_contactIds[row] == dummyMyCardId) { + return QContact(); + } + CNT_EXIT + return d->m_contactManager->contact(d->m_contactIds[row]); +} + +/*! + * Verify specified row id is valid. + * + * \param row A row number + * \return bool indicating validity of row id + */ +bool CntListModel::validRowId(int row) const +{ + return (row >= 0 && row < rowCount()); +} + +/*! + * Fetch the id of the row containing the contact of the specified id. + * + * \param contactId The id of the contact + * \return the row id of the contact or -1 if no item matched. + */ +int CntListModel::rowId(const QContactLocalId &contactId) const +{ + return d->m_contactIds.indexOf(contactId); +} + +/*! + * Return the data to be used by the view for a display role. + * + * \param row The row of the item to return data about. + * \param column The column of the item to return data about. + * \return QVariant The data for the specified index. + */ +QVariant CntListModel::dataForRole(int row, int role) const +{ + CNT_ENTRY + QStringList list; + QString name; + QString infoText; + bool isSelfContact = false; + bool isNonEmptySelfContact = false; + + QContactLocalId id = d->m_contactIds[row]; + if (d->m_myCardId == id || dummyMyCardId == id) { + isSelfContact = true; + if (d->m_currentContact.id() == -1) { + // empty card + name = hbTrId("txt_phob_dblist_mycard"); + infoText = hbTrId("txt_phob_dblist_mycard_val_create_my_identity"); + } + else { + isNonEmptySelfContact = true; + } + } + + if (!isSelfContact || isNonEmptySelfContact) { + name = d->m_currentContact.name(); + if (name.isEmpty()) { + name = hbTrId("txt_phob_list_unnamed"); + } + infoText = d->m_currentContact.text(); + } + + if ( role == Qt::DisplayRole ) + { + list << d->m_Format->formattedText(name, d->m_filter); + } + else + { + list << name; + } + + if (!isNonEmptySelfContact) { + if (d->m_currentRowSetting == CntTwoRowsNameAndPhoneNumber) { + //add additional text only if user wants 2 rows in each name list item + list << infoText; + } + } + CNT_EXIT + return list; +} + +/*! + * Handle adding of contacts. + * + * \param contactIds Ids of contacts added. + */ +void CntListModel::handleAdded(const QList& contactIds) +{ + CNT_ENTRY + + //if contacts are added already, no need to do anything + bool newContacts = false; + for (int k = 0; k < contactIds.count() && !newContacts; k++) { + if(!d->m_contactIds.contains(contactIds.at(k))) { + newContacts = true; + } + } + if (!newContacts) { + return; + } + + //invalidate cached contact + d->m_currentRow = -1; + + QList oldIdList = d->m_contactIds; + updateContactIdsArray(); + + //Find all new contacts (=rows) + QList newRows; + for(int i = 0; i < d->m_contactIds.count(); i++) { + if (!oldIdList.contains(d->m_contactIds.at(i))) { + newRows.append(i); + } + } + + // only 1 (or no) contact is added, it can be handled by beginInsertRows() + // and endInsertRows() + if (newRows.count() <= 1) { + for(int j = 0; j < newRows.count(); j++) { + beginInsertRows(QModelIndex(), newRows.at(j), newRows.at(j)); + endInsertRows(); + } + } + else { + beginResetModel(); + reset(); + endResetModel(); + } + CNT_EXIT +} + +/*! + * Handle changes in contacts. + * + * \param contactIds Ids of contacts changed. + */ +void CntListModel::handleChanged(const QList& contactIds) +{ + CNT_ENTRY + + if (contactIds.count() == 0) { + return; + } + + //invalidate cached contact + d->m_currentRow = -1; + + int firstChangedContactPosBefore = rowId(contactIds.at(0)); + updateContactIdsArray(); + int firstChangedContactPosAfter = rowId(contactIds.at(0)); + + // if only one contact was updated and its position didn't change, + // refresh the corresponding row + if (contactIds.count() == 1 && + firstChangedContactPosBefore == firstChangedContactPosAfter && + firstChangedContactPosBefore >= 0) { + QModelIndex top = index(firstChangedContactPosBefore); + QModelIndex bottom = index(firstChangedContactPosBefore); + emit dataChanged(top, bottom); + } + else { + beginResetModel(); + reset(); + endResetModel(); + } + + CNT_EXIT +} + +/*! + * Handle removing of contacts. + * + * \param contactIds Ids of contacts removed. + */ +void CntListModel::handleRemoved(const QList& contactIds) +{ + CNT_ENTRY + + bool removeContacts = false; + QList idList = d->m_contactIds; + for (int k = 0; k < contactIds.count() && !removeContacts; k++) { + if(idList.contains(contactIds.at(k))) { + removeContacts = true; + } + } + if (!removeContacts) { + return; + } + + //Find contacts to remove (=rows) + QList removeRows; + for(int i = 0; i < contactIds.count(); i++) { + if (idList.contains(contactIds.at(i))) { + removeRows.append(rowId(contactIds.at(i))); + } + } + + // invalidate cached contact + d->m_currentRow = -1; + + int myCardRow = -1; + if (contactIds.contains(d->m_myCardId)) { + myCardRow = rowId(d->m_myCardId); + d->m_myCardId = 0; + } + + // remove rows starting from the bottom + qSort(removeRows.begin(), removeRows.end(), qGreater()); + for (int j = 0; j < removeRows.count(); j++) { + if (removeRows.at(j) != myCardRow || !d->m_showMyCard) { + beginRemoveRows(QModelIndex(), removeRows.at(j), removeRows.at(j)); + endRemoveRows(); + } + } + + foreach (QContactLocalId id, contactIds) { + d->m_contactIds.removeOne(id); + } + + if (myCardRow != -1 && d->m_showMyCard) { + d->m_contactIds.insert(0, dummyMyCardId); + QModelIndex index = createIndex(0, 0); + emit dataChanged(index, index); + } + + CNT_EXIT +} + +/*! + * Handle my card change. + * + * \param oldId Id of the old MyCard. + * \param newId Id of the new MyCard. + */ +void CntListModel::handleMyCardChanged(const QContactLocalId& /*oldId*/, const QContactLocalId& newId) +{ + CNT_ENTRY + + //invalidate cached contact + d->m_currentRow = -1; + d->m_myCardId = newId; + + updateContactIdsArray(); + + beginResetModel(); + reset(); + endResetModel(); + + CNT_EXIT +} + +/*! + * Handle added relationships. + * + * \param contactIds Ids of contacts added (group id and contact ids). + */ +void CntListModel::handleAddedRelationship(const QList& contactIds) +{ + CNT_ENTRY + + if (contactIds.contains(d->m_groupId)) { + foreach (QContactLocalId id, contactIds) { + if (id != d->m_groupId && !d->m_contactIds.contains(id)) { + // at least one new contact id has been added to this group, + // so update the model + updateRelationships(); + break; + } + } + } + + CNT_EXIT +} + +/*! + * Handle removed relationships. + * + * \param contactIds Ids of contacts removed from a relationship (group id and contact ids). + */ +void CntListModel::handleRemovedRelationship(const QList& contactIds) +{ + CNT_ENTRY + + if (contactIds.contains(d->m_groupId)) { + foreach (QContactLocalId id, contactIds) { + if (d->m_contactIds.contains(id)) { + // at least one new contact id has been removed from this group, + // so update the model + updateRelationships(); + break; + } + } + } + + CNT_EXIT +} + +/*! + * Updates the model to reflect changes in the relationships. + */ +void CntListModel::updateRelationships() +{ + CNT_ENTRY + + //invalidate cached contact + d->m_currentRow = -1; + + QList oldIdList = d->m_contactIds; + updateContactIdsArray(); + + // find all changed rows + QList newRows, removedRows; + for (int i = 0; i < d->m_contactIds.count(); i++) { + if (!oldIdList.contains(d->m_contactIds.at(i))) { + newRows.append(i); + } + } + for (int i = 0; i < oldIdList.count(); i++) { + if (!d->m_contactIds.contains(oldIdList.at(i))) { + removedRows.append(i); + } + } + + // currently only one-row-changes are handled with beginInsertRows/beginRemoveRows + // if there are more than one change, the whole model is reset + if (removedRows.count() == 1 && newRows.count() == 0) { + beginRemoveRows(QModelIndex(), removedRows.at(0), removedRows.at(0)); + endRemoveRows(); + } + else if (newRows.count() == 1 && removedRows.count() == 0) { + beginInsertRows(QModelIndex(), newRows.at(0), newRows.at(0)); + endInsertRows(); + } + else { + beginResetModel(); + endResetModel(); + } + + CNT_EXIT +} + +/*! +* Notify views that info for a contact has become +* available or has changed. +* +* \param contactId the id of the contact +*/ +void CntListModel::handleContactInfoUpdated(QContactLocalId contactId) +{ + CNT_ENTRY + + QModelIndex index = createIndex(rowId(contactId), 0); + if (index.row() == d->m_currentRow) { + d->m_currentRow = -1; + } + emit dataChanged(index, index); + + CNT_EXIT +} + +/*! +* Handle a change how name list item should be represented +* +* \param key Central repository key +* \param value New value in the key +*/ +void CntListModel::handleRowSettingChanged(const XQSettingsKey& /*key*/, const QVariant& value) +{ + bool ok = false; + int newSetting = value.toInt(&ok); + if (ok) { + d->m_currentRowSetting = newSetting; + beginResetModel(); + reset(); + endResetModel(); + } +}