diff -r c18f9fa7f42e -r 640d30f4fb64 phonebookui/cntlistmodel/cntlistmodel.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/phonebookui/cntlistmodel/cntlistmodel.cpp Fri Oct 15 12:24:46 2010 +0300 @@ -0,0 +1,777 @@ +/* +* 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/*! + \class CntListModel + \brief List model for list with contacts. + + CntListModel is a list model view for contacts database content. It uses + CntCache to fetch and cache entries displayed on the screen. + */ + +const uint dummyMyCardId = 0; + +/*! + 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 + \param filter a filter that selects the contacts to show in the list + \param showMyCard true if my card entry should be shown at the top of + the list, otherwise false + \param parent parent of this QObject + */ +CntListModel::CntListModel(QContactManager* manager, + const QContactFilter& filter, + bool showMyCard, + QObject *parent) + : QAbstractListModel(parent) +{ + CNT_ENTRY + + // set up data + d = new CntListModelData(manager, filter, showMyCard); + + // fetch IDs + updateContactIdsArray(); + + // get current setting how to show an item in the name list and subscribe for changes + d->mSettings = new XQSettingsManager; + d->mNameListRowSettingkey = new XQSettingsKey(XQSettingsKey::TargetCentralRepository, + KCRCntSettings.iUid, + KCntNameListRowSetting); + d->mCurrentRowSetting = d->mSettings->readItemValue(*d->mNameListRowSettingkey, + XQSettingsManager::TypeInt).toInt(); + d->mSettings->startMonitoring(*d->mNameListRowSettingkey, XQSettingsManager::TypeInt); + connect(d->mSettings, SIGNAL(valueChanged(const XQSettingsKey&, const QVariant&)), + this, SLOT(handleRowSettingChanged(const XQSettingsKey&, const QVariant&))); + + // listen to cache for changes in contacts + connect(d->mCache, SIGNAL(contactInfoUpdated(QContactLocalId)), + this, SLOT(handleContactInfoUpdated(QContactLocalId))); + connect(d->mCache, SIGNAL(contactsAdded(const QList&)), + this, SLOT(handleAdded(const QList&))); + connect(d->mCache, SIGNAL(contactsChanged(const QList&)), + this, SLOT(handleChanged(const QList&))); + connect(d->mCache, SIGNAL(contactsRemoved(const QList&)), + this, SLOT(handleRemoved(const QList&))); + connect(d->mCache, SIGNAL(dataChanged()), + this, SLOT(refreshModel())); + + // listen to contactmanager for changes in relationships or mycard + connect(d->mContactManager, SIGNAL(selfContactIdChanged(const QContactLocalId&, const QContactLocalId&)), + this, SLOT(handleMyCardChanged(const QContactLocalId&, const QContactLocalId&))); + connect(d->mContactManager, SIGNAL(relationshipsAdded(const QList&)), + this, SLOT(handleAddedRelationship(const QList&))); + connect(d->mContactManager, SIGNAL(relationshipsRemoved(const QList&)), + this, SLOT(handleRemovedRelationship(const QList&))); + + 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 + + QVariant returnData; + int row = index.row(); + + // check that row is ok + if (!isValidRow(row)) { + // invalid row + return QVariant(); + } + + // update current contact if needed + if (row != d->mCurrentRow) { + delete d->mCurrentContact; + if (d->mContactIds[row] == dummyMyCardId) { + // row contains dummy MyCard, so create dummy CntContactInfo + d->mCurrentContact = NULL; + } else { + d->mCurrentContact = d->mCache->fetchContactInfo(row, d->mContactIds); + } + d->mCurrentRow = row; + } + + if (role == Qt::DisplayRole) { + returnData = dataForRole(row, role); + } else if (role == Hb::IndexFeedbackRole) { + if (row > 0 || (d->mMyCardId != d->mContactIds[0] && dummyMyCardId != d->mContactIds[0])) { + returnData = dataForRole(row, role).toStringList().at(0).toUpper(); + } + } else if (role == Qt::BackgroundRole) { + if (d->mMyCardId == d->mContactIds[row] || dummyMyCardId == d->mContactIds[row]) { + returnData = HbFrameBackground("qtg_fr_list_parent_normal", HbFrameDrawer::NinePieces); + } + } else if (role == Qt::DecorationRole) { + if (d->mCurrentRowSetting == CntTwoRowsNameAndPhoneNumber) { + //icon fits only if user selected 2 rows in each name list item + QList icons; + + if (d->mCurrentContact != NULL && !d->mCurrentContact->icon1().isNull()) { + icons.append(d->mCurrentContact->icon1()); + } else if (d->mMyCardId == d->mContactIds[row] || dummyMyCardId == d->mContactIds[row]) { + icons.append(d->mDefaultMyCardIcon); + } else { + icons.append(d->mDefaultIcon); + } + + if (d->mCurrentContact != NULL && !d->mCurrentContact->icon2().isNull()) { + icons.append(d->mCurrentContact->icon2()); + } + + returnData = icons; + } + } + + CNT_EXIT + return returnData; +} + +/*! + 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->mContactIds.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 (!isValidRow(index.row())) { + return 0; + } + + CNT_EXIT + return d->mContactIds[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(row(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(row(contactId), 0); +} + +/*! + Set new filter and sort order for the model. + + \param contactFilter New contact filter. + */ +void CntListModel::setFilter(const QContactFilter& contactFilter) +{ + CNT_ENTRY + + d->setFilter(contactFilter); + + //refresh model + updateContactIdsArray(); + + beginResetModel(); + 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->mShowMyCard == enabled) { + return; + } + + QContactLocalId myCardId = d->mMyCardId; + if (enabled) { + //add MyCard to the list + if (myCardId <= 0) { + // create a placeholder for MyCard + d->mContactIds.insert(0, dummyMyCardId); + } else { + d->mContactIds.insert(0, myCardId); + } + } else { + // remove MyCard from the list + if (myCardId <= 0) { + d->mContactIds.removeOne(dummyMyCardId); + } else { + d->mContactIds.removeOne(myCardId); + } + } + d->mShowMyCard = enabled; + d->mCurrentRow = -1; + + beginResetModel(); + reset(); + endResetModel(); + + CNT_EXIT +} + +/*! + \return true if MyCard is shown, false otherwise. + */ +bool CntListModel::isMyCardShown() const +{ + return d->mShowMyCard; +} + +/*! + \return the id of the MyCard contact. + */ +QContactLocalId CntListModel::myCardId() const +{ + return d->mMyCardId; +} + +/*! + Gets the filtered list of the contact Ids in a sorted order + + \return Error status + */ +void CntListModel::updateContactIdsArray() +{ + CNT_ENTRY + + QContactDetailFilter* detailFilter = NULL; + + if (d->mFilter.type() == QContactFilter::ContactDetailFilter) { + detailFilter = static_cast(&d->mFilter); + } + + // special handling for all-contacts filter + if (detailFilter + && detailFilter->detailDefinitionName() == QContactType::DefinitionName + && detailFilter->detailFieldName() == QContactType::FieldType + && detailFilter->value() == QContactType::TypeContact) { + d->mContactIds = d->mCache->sortIdsByName(NULL); + } else if (detailFilter + && detailFilter->detailDefinitionName() == QContactDisplayLabel::DefinitionName + && detailFilter->detailFieldName() == QContactDisplayLabel::FieldLabel + && detailFilter->matchFlags() == Qt::MatchStartsWith) { + QStringList searchList = detailFilter->value().toStringList(); + d->mContactIds = d->mCache->sortIdsByName(searchList); + } else { + QSet filterIds = d->mContactManager->contactIds(d->mFilter).toSet(); + d->mContactIds = d->mCache->sortIdsByName(&filterIds); + } + + //find MyCard contact and move it to the first position + QContactLocalId myCardId = d->mMyCardId; + if (myCardId > 0) { + // MyCard exists + d->mContactIds.removeOne(myCardId); + if (d->mShowMyCard) { + d->mContactIds.insert(0, myCardId); + } + } else if (d->mShowMyCard) { + // create a placeholder for MyCard + d->mContactIds.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 (!isValidRow(row) || d->mContactIds[row] == dummyMyCardId) { + return QContact(); + } + + CNT_EXIT + return d->mContactManager->contact(d->mContactIds[row]); +} + +/*! + Verify specified row id is valid. + + \param row A row number + \return bool indicating validity of row id + */ +bool CntListModel::isValidRow(int row) const +{ + return (row >= 0 && row < rowCount()); +} + +/*! + Fetch the row containing the contact with the specified id. + + \param contactId The id of the contact + \return the row of the contact or -1 if no item matched. + */ +int CntListModel::row(const QContactLocalId &contactId) const +{ + return d->mContactIds.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->mContactIds[row]; + if (d->mMyCardId == id || dummyMyCardId == id) { + isSelfContact = true; + if (d->mCurrentContact == NULL) { + // 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->mCurrentContact->name(); + if (name.isEmpty()) { + name = hbTrId("txt_phob_list_unnamed"); + } + infoText = d->mCurrentContact->text(); + } + + if (role == Qt::DisplayRole) { + list << d->mFormat->formattedText(name, d->mFilter); + } else { + list << name; + } + + if (!isNonEmptySelfContact) { + if (d->mCurrentRowSetting == 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->mContactIds.contains(contactIds.at(k))) { + newContacts = true; + } + } + if (!newContacts) { + return; + } + + // invalidate cached contact + d->mCurrentRow = -1; + + QList oldIdList = d->mContactIds; + updateContactIdsArray(); + + QList newRows; + for (int i = 0; i < d->mContactIds.count(); i++) { + if (!oldIdList.contains(d->mContactIds.at(i))) { + newRows.append(i); + } + } + + if (newRows.size() == 1) { + beginInsertRows(QModelIndex(), newRows.at(0), newRows.at(0)); + 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->mCurrentRow = -1; + + int firstChangedContactPosBefore = row(contactIds.at(0)); + updateContactIdsArray(); + int firstChangedContactPosAfter = row(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->mContactIds; + 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(row(contactIds.at(i))); + } + } + + // invalidate cached contact + d->mCurrentRow = -1; + + int myCardRow = -1; + if (contactIds.contains(d->mMyCardId)) { + myCardRow = row(d->mMyCardId); + d->mMyCardId = 0; + } + + // remove rows starting from the bottom + qSort(removeRows.begin(), removeRows.end(), qGreater()); + foreach (int row, removeRows) { + if (row != myCardRow || !d->mShowMyCard) { + beginRemoveRows(QModelIndex(), row, row); + endRemoveRows(); + } + } + + foreach (QContactLocalId id, contactIds) { + d->mContactIds.removeOne(id); + } + + if (myCardRow != -1 && d->mShowMyCard) { + d->mContactIds.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->mCurrentRow = -1; + d->mMyCardId = 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->mGroupId)) { + foreach (QContactLocalId id, contactIds) { + if (id != d->mGroupId && !d->mContactIds.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->mGroupId)) { + foreach (QContactLocalId id, contactIds) { + if (d->mContactIds.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->mCurrentRow = -1; + + QList oldIdList = d->mContactIds; + updateContactIdsArray(); + + // find all changed rows + QList newRows, removedRows; + for (int i = 0; i < d->mContactIds.count(); i++) { + if (!oldIdList.contains(d->mContactIds.at(i))) { + newRows.append(i); + } + } + for (int i = 0; i < oldIdList.count(); i++) { + if (!d->mContactIds.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(row(contactId), 0); + if (index.row() == d->mCurrentRow) { + d->mCurrentRow = -1; + } + + if (isValidRow(index.row())) { + emit dataChanged(index, index); + } + + CNT_EXIT +} + +/*! + Handle a change in 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->mCurrentRowSetting = newSetting; + + if (rowCount() > 0) { + QModelIndex first = createIndex(0, 0); + QModelIndex last = createIndex(rowCount() - 1, 0); + emit dataChanged(first, last); + } + } +} + +/*! + Handle a change in data. + */ +void CntListModel::refreshModel() +{ + d->mCurrentRow = -1; + + updateContactIdsArray(); + + beginResetModel(); + reset(); + endResetModel(); +}