Viadeo Twitter Google Bookmarks ! Facebook Digg del.icio.us MySpace Yahoo MyWeb Blinklist Netvouz Reddit Simpy StumbleUpon Bookmarks Windows Live Favorites 
Logo Documentation Qt ·  Page d'accueil  ·  Toutes les classes  ·  Toutes les fonctions  ·  Vues d'ensemble  · 

qcontactlistmodel.cpp Example File
incomingcalls/qcontactlistmodel.cpp

    /****************************************************************************
    **
    ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
    ** All rights reserved.
    ** Contact: Nokia Corporation (qt-info@nokia.com)
    **
    ** This file is part of the Qt Mobility Components.
    **
    ** $QT_BEGIN_LICENSE:LGPL$
    ** No Commercial Usage
    ** This file contains pre-release code and may not be distributed.
    ** You may use this file in accordance with the terms and conditions
    ** contained in the Technology Preview License Agreement accompanying
    ** this package.
    **
    ** GNU Lesser General Public License Usage
    ** Alternatively, this file may be used under the terms of the GNU Lesser
    ** General Public License version 2.1 as published by the Free Software
    ** Foundation and appearing in the file LICENSE.LGPL included in the
    ** packaging of this file.  Please review the following information to
    ** ensure the GNU Lesser General Public License version 2.1 requirements
    ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
    **
    ** In addition, as a special exception, Nokia gives you certain additional
    ** rights.  These rights are described in the Nokia Qt LGPL Exception
    ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
    **
    ** If you have questions regarding the use of this file, please contact
    ** Nokia at qt-info@nokia.com.
    **
    **
    **
    **
    **
    **
    **
    **
    ** $QT_END_LICENSE$
    **
    ****************************************************************************/

    #include "qcontactlistmodel.h"
    #include "qcontactlistmodel_p.h"

    #include "qcontact.h"
    #include "qcontactmanager.h"
    #include "qcontactdetails.h"
    #include "qcontactrequests.h"
    #include "qcontactfilters.h"

    /*!
     * Constructs a new QContactListModel which will request data from the given \a manager
     * and cache approximately \a cacheSize contacts.
     *
     * \sa setManager(), setCacheSize()
     */
    QContactListModel::QContactListModel(QContactManager* manager, int cacheSize)
            : QAbstractListModel(),
            d(new QContactListModelPrivate)
    {
        setCacheSize(cacheSize);
        setManager(manager);
    }

    /*!
     * Constructs a new copy of the \a other model
     */
    QContactListModel::QContactListModel(const QContactListModel& other)
            : QAbstractListModel(), d(other.d)
    {
    }

    /*!
     * Assigns this model to be equal to \a other
     */
    QContactListModel& QContactListModel::operator=(const QContactListModel& other)
    {
        d = other.d;
        return *this;
    }

    /*!
     * Cleans up any memory in use by the model
     */
    QContactListModel::~QContactListModel()
    {
    }

    /*!
     * Returns a pointer to the manager from which this model requests contact data
     */
    QContactManager* QContactListModel::manager() const
    {
        return d->m_manager;
    }

    /*!
     * Sets the manager from which this model requests contact data to \a manager.
     * Any requests made of the old manager will be cancelled and deleted.
     *
     * \sa backendChanged()
     */
    void QContactListModel::setManager(QContactManager* manager)
    {
        // first, cancel and delete any requests made of the old manager
        QMap<QContactAbstractRequest*, int> updatedRequestCentreRows;
        QList<QContactAbstractRequest*> requests = d->m_requestCentreRows.keys();
        for (int i = 0; i < requests.size(); i++) {
            QContactAbstractRequest* current = requests.at(i);
            if (current->manager() == d->m_manager) {
                current->cancel();
                delete current;
            } else {
                updatedRequestCentreRows.insert(current, d->m_requestCentreRows.value(current));
            }
        }
        d->m_requestCentreRows = updatedRequestCentreRows;

        // secondly, disconnect the signals from the old manager
        if (d->m_manager)
            d->m_manager->disconnect(this);

        // then set up the new manager.
        d->m_manager = manager;
        delete d->m_idRequest;
        d->m_idRequest = new QContactLocalIdFetchRequest;
        connect(d->m_idRequest, SIGNAL(progress(QContactLocalIdFetchRequest*,bool)), this, SLOT(contactIdFetchRequestProgress(QContactLocalIdFetchRequest*,bool)));
        d->m_idRequest->setManager(manager);
        if (manager) {
            connect(manager, SIGNAL(contactsAdded(QList<QContactLocalId>)), this, SLOT(backendChanged()));
            connect(manager, SIGNAL(contactsChanged(QList<QContactLocalId>)), this, SLOT(backendChanged()));
            connect(manager, SIGNAL(contactsRemoved(QList<QContactLocalId>)), this, SLOT(backendChanged()));
        }

        // and kick of a request for the ids.
        backendChanged();
    }

    /*!
     * Returns the number of contacts that this model will cache
     */
    int QContactListModel::cacheSize() const
    {
        return (d->m_halfCacheSize * 2);
    }

    /*!
     * Sets the number of contacts that this model will cache to be approximately \a size contacts.
     * The exact size of the cache will be the next higher size which is divisible by 4, or
     * \a size if \a size is divisible by 4, unless the next higher size would cause integer overflow.
     * Returns true if the cache size was set successfully, and false if a non-positive \a size was
     * specified.
     *
     * \sa cacheSize()
     */
    bool QContactListModel::setCacheSize(int size)
    {
        // size will be rounded up to nearest where modulo 4 == 0,
        // except where doing so would result in integer overflow
        // (where it will be rounded down)
        if (size > 0) {
            // if the cache size is odd, round up to nearest even then test modulo 4
            // this allows us to cache m_halfCacheSize rows either side of currentRow
            int modulo4 = size % 4;
            if (modulo4 == 0) {
                d->m_halfCacheSize = size / 2;
                d->m_quarterCacheSize = size / 4;
            } else {
                int test = size + (4 - modulo4); // avoid integer overflow.
                d->m_halfCacheSize = (test < 0 ? (size - modulo4) : (size + 4 - modulo4));
                d->m_halfCacheSize = d->m_halfCacheSize / 2;
                d->m_quarterCacheSize = d->m_halfCacheSize / 2;
            }
            return true;
        }

        return false;
    }

    /*!
     * Returns the policy that the model uses to determine when asynchronous requests should be cleaned up.
     *
     * \sa setRequestPolicy()
     */
    QContactListModel::AsynchronousRequestPolicy QContactListModel::requestPolicy() const
    {
        return d->m_requestPolicy;
    }

    /*!
     * Sets the policy that the model uses to determine when to clean up asynchronous requests to \a policy.
     *
     * \sa requestPolicy()
     */
    void QContactListModel::setRequestPolicy(QContactListModel::AsynchronousRequestPolicy policy)
    {
        d->m_requestPolicy = policy;
    }

    /*!
     * Returns the definition name of the relevant data detail which is cached by the model
     *
     * \sa setRelevantDetailDefinitionAndFieldNames()
     */
    QString QContactListModel::relevantDefinitionName() const
    {
        return d->m_relevantDefinitionName;
    }

    /*!
     * Returns the name of the field of the relevant data detail which is cached by the model
     *
     * \sa setRelevantDetailDefinitionAndFieldNames()
     */
    QString QContactListModel::relevantFieldName() const
    {
        return d->m_relevantFieldName;
    }

    /*!
     * Sets the definition name of the relevant detail which is cached by the model to \a definitionName,
     * and the name of the field of such details which is cached to \a fieldName.
     *
     * \sa relevantDefinitionName(), relevantFieldName()
     */
    bool QContactListModel::setRelevantDetailDefinitionAndFieldNames(const QString& definitionName, const QString& fieldName)
    {
        if (definitionName.isEmpty() || fieldName.isEmpty())
            return false;

        d->m_relevantDefinitionName = definitionName;
        d->m_relevantFieldName = fieldName;
        return true;
    }

    /*!
     * \reimp
     */
    int QContactListModel::rowCount(const QModelIndex& parent) const
    {
        Q_UNUSED(parent);
        return d->m_rowsToIds.count();
    }

    /*!
     * \reimp
     */
    QVariant QContactListModel::data(const QModelIndex& index, int role) const
    {
        if (index.row() == -1)
            return QVariant();

        d->m_currentRow = index.row();
        QContact currentContact = d->m_cache.value(d->m_currentRow);

        // check to see if we need to update our cache
        bool cacheUpdateRequired = d->m_cache.isEmpty();
        if ((d->m_halfCacheSize * 2) < d->m_rowsToIds.count()) {
            // we cannot fit the entire dataset into our cache; calculate window size.
            int maxActiveCacheRow = d->m_currentRow + d->m_quarterCacheSize;
            int minActiveCacheRow = d->m_currentRow - d->m_quarterCacheSize;
            if (maxActiveCacheRow <= d->m_lastCacheCentreRow) {
                cacheUpdateRequired = true;
            } else if (minActiveCacheRow >= d->m_lastCacheCentreRow) {
                cacheUpdateRequired = true;
            }
        }

        if (cacheUpdateRequired) {
            // the current row will be our new cache centre point.
            d->m_lastCacheCentreRow = d->m_currentRow;

            // update our cache - first calculate the new cache window.
            int lowerBound = d->m_lastCacheCentreRow - d->m_halfCacheSize;
            int upperBound = d->m_lastCacheCentreRow + d->m_halfCacheSize;

            // create a list of rows we need to cache
            QList<int> newCacheRows;
            if ((upperBound - lowerBound) >= d->m_rowsToIds.count()) {
                // we can cache the entire dataset.
                newCacheRows = d->m_rowsToIds.keys();
            } else {
                // we can only cache a window of the entire dataset.
                for (int i = lowerBound; i <= upperBound; i++) {
                    // wrap-around at top and bottom of cache.
                    int rowNumber = i;
                    if (i < 0)
                        rowNumber += d->m_rowsToIds.count();
                    if (i >= d->m_rowsToIds.count())
                        rowNumber -= d->m_rowsToIds.count();
                    newCacheRows.append(rowNumber);
                }
            }

            // clean up any old requests depending on policy
            // please note that this branching is _slow_; might be best to remove it
            // and just always do the default (cancel on cache centrepoint miss)...
            if (d->m_requestPolicy != QContactListModel::NeverCancelPolicy) {
                QList<QContactAbstractRequest*> oldRequests = d->m_requestCentreRows.keys();
                bool cancelRequest = false;

                // we could pull the conditionals outside the loop for better performance...
                for (int i = 0; i < oldRequests.size(); i++) {
                    QContactAbstractRequest* current = oldRequests.at(i);
                    if (d->m_requestPolicy == QContactListModel::CancelOnCacheUpdatePolicy) {
                        // immediately cancel since update is required.
                        cancelRequest = true;
                    } else if (d->m_requestPolicy == QContactListModel::CancelOnCacheMissPolicy) {
                        // slow solution... should probably do bounds checking instead of .contains().
                        if (!newCacheRows.contains(d->m_requestCentreRows.value(current))) {
                            cancelRequest = true;
                        }
                    } else {
                        int cacheSize = d->m_halfCacheSize * 2;
                        int requestCentre = d->m_requestCentreRows.value(current);
                        int requestWindowMax = (requestCentre + d->m_halfCacheSize) % cacheSize;
                        int requestWindowMin = (requestCentre - d->m_halfCacheSize) % cacheSize;
                        // slow solution... should probably do bounds checking instead of .contains().
                        if (!newCacheRows.contains(requestWindowMax) && !newCacheRows.contains(requestWindowMin) && !newCacheRows.contains(requestCentre)) {
                            cancelRequest = true;
                        }
                    }

                    // cancel (and clean up) the request if required by the policy.
                    if (cancelRequest) {
                        current->cancel();
                        d->m_requestCentreRows.remove(current);
                        delete current;
                    }

                    // reset the control variable.
                    cancelRequest = false;
                }
            }

            // create "spots" for the cache entries in our cache map
            QList<int> oldCacheRows = d->m_cache.keys();
            foreach (int row, newCacheRows) {
                if (!d->m_cache.contains(row)) {
                    QContact temp;
                    QContactName loadingName;
                    loadingName.setCustomLabel(QString(tr("Loading...")));
                    temp.saveDetail(&loadingName);
                    d->m_cache.insert(row, temp);
                }
            }

            // remove any out-of-cache-window contacts we have stored
            // and remove from the newCacheRows any rows we already have cached
            foreach (int row, oldCacheRows) {
                if (row < lowerBound || row > upperBound) {
                    // don't want to cache this row.
                    d->m_cache.remove(row);
                }

                if (newCacheRows.contains(row)) {
                    // already have this row in cache.
                    newCacheRows.removeOne(row);
                }
            }

            // ensure that the current row's contact is cached; if not create a placeholder.
            if (!d->m_cache.contains(d->m_currentRow)) {
                QContactName loadingName;
                loadingName.setCustomLabel(QString(tr("Loading...")));
                currentContact.saveDetail(&loadingName);
            }

            // now fire off an asynchronous request to update our cache
            QContactFetchRequest* req = new QContactFetchRequest;
            d->m_requestCentreRows.insert(req, d->m_lastCacheCentreRow);
            QContactLocalIdFilter idFil;
            QList<QContactLocalId> newCacheIds;
            for (int i = 0; i < newCacheRows.size(); i++) {
                newCacheIds.append(d->m_rowsToIds.value(newCacheRows.at(i)));
            }
            idFil.setIds(newCacheIds);
            req->setFilter(idFil);
            req->setManager(d->m_manager);
            connect(req, SIGNAL(progress(QContactFetchRequest*, bool)), this, SLOT(contactFetchRequestProgress(QContactFetchRequest*,bool)));
            req->start();
        }

        // now return the data being requested.
        QVariant ret;
        switch (role) {
            case QContactListModel::DisplayLabelRole:
            {
                ret = currentContact.displayLabel();
            }
            break;

            case QContactListModel::IdRole:
            {
                ret = currentContact.id().localId();
            }
            break;

            case QContactListModel::AvatarRole:
            {
                ret = currentContact.detail(QContactAvatar::DefinitionName).value(QContactAvatar::FieldAvatar);
            }
            break;

            case QContactListModel::PresenceRole:
            {
                if (d->m_manager == 0) {
                    // the manager has not been initialised.
                    break;
                }

                // grab the possible presence values; they should be in order from (unknown) to least present to most present.
                QContactDetailDefinition presenceDef = d->m_manager->detailDefinition(QContactOnlineAccount::DefinitionName);
                QList<QVariant> allowablePresenceValues = presenceDef.fields().value(QContactOnlineAccount::FieldPresence).allowableValues();
                if (presenceDef.isEmpty() || allowablePresenceValues.isEmpty()) {
                    // the manager does not support presence details.
                    break;
                }

                // calculate the "global presence" of the contact in a naive way.
                int bestPresenceSoFar = 0; // unknown
                QList<QContactDetail> presenceDetails = currentContact.details(QContactOnlineAccount::DefinitionName);
                foreach (const QContactOnlineAccount& pres, presenceDetails) {
                    int index = allowablePresenceValues.indexOf(pres.presence());
                    if (index > bestPresenceSoFar) {
                        bestPresenceSoFar = index;
                    }
                }

                ret = QVariant(allowablePresenceValues.at(bestPresenceSoFar));
            }
            break;

            case QContactListModel::RelevantDataRole:
            {
                ret = currentContact.detail(d->m_relevantDefinitionName).value(d->m_relevantFieldName);
            }
            break;

            default:
            break;
        }

        // return the requested data, or a default-constructed QVariant if not available.
        return ret;
    }

    /*!
     * \reimp
     */
    QVariant QContactListModel::headerData(int section, Qt::Orientation orientation, int role) const
    {
        Q_UNUSED(section);
        Q_UNUSED(orientation);

        QVariant ret;
        switch (role) {
            case QContactListModel::DisplayLabelRole:
            {
                ret = QString(tr("Name"));
            }
            break;

            case QContactListModel::IdRole:
            {
                ret = QString(tr("Id"));
            }
            break;

            case QContactListModel::AvatarRole:
            {
                ret = QString(tr("Avatar"));
            }
            break;

            case QContactListModel::PresenceRole:
            {
                ret = QString(tr("Presence"));
            }
            break;

            case QContactListModel::RelevantDataRole:
            {
                // todo: take this as an argument in setRelevant()
                ret = QString(tr("Details"));
            }
            break;

            default:
            break;
        }

        return ret;
    }

    /*!
     * \reimp
     */
    bool QContactListModel::insertRows(int row, int count, const QModelIndex& parent)
    {
        beginInsertRows(parent, row, count);

        // insertion code here.

        endInsertRows();

        return false;
    }

    /*!
     * \reimp
     */
    bool QContactListModel::removeRows(int row, int count, const QModelIndex& parent)
    {
        beginRemoveRows(parent, row, count);

        // removal code here.

        endRemoveRows();

        return false;
    }

    /*!
     * Returns the row number at which the data of the contact with the given \a contactId is stored, or
     * -1 if no such contact exists in the model
     */
    int QContactListModel::contactRow(const QContactLocalId& contactId) const
    {
        return d->m_idsToRows.value(contactId, -1);
    }

    /*!
     * Returns the entire contact which exists in the model at the specified \a index
     */
    QContact QContactListModel::contact(const QModelIndex& index) const
    {
        if (d->m_manager)
            return d->m_manager->contact(d->m_rowsToIds.value(index.row()));
        return QContact();
    }

    /*!
     * Processes the progress of the \a request.
     * If the request is still valid, the results are placed in the cache at the required positions.
     * If the cache is updated, the dataChanged() signal is emitted.
     * This implementation ignores the \a appendOnly flag.
     */
    void QContactListModel::contactFetchRequestProgress(QContactFetchRequest* request, bool appendOnly)
    {
        Q_UNUSED(appendOnly);

        // first, check to make sure that the request is still valid.
        if (d->m_manager != request->manager() || request->status() == QContactAbstractRequest::Cancelled) {
            d->m_requestCentreRows.remove(request);
            delete request;
            return; // ignore these results.
        }

        QMap<int, int> rowMap; // sorted list of rows changed.
        QList<QContact> fetched = request->contacts();
        foreach (const QContact& c, fetched) {
            int fetchedRow = d->m_idsToRows.value(c.id().localId(), -1);

            // see if this row should be cached
            if (!d->m_cache.contains(fetchedRow))
                break; // shouldn't cache this row (or already cached); ignore the contact.

            // we need to cache this contact.
            d->m_cache.insert(fetchedRow, c);
            rowMap.insert(fetchedRow, fetchedRow);
        }

        // check to see if the request status is "finished" - clean up.
        if (request->status() == QContactAbstractRequest::Finished) {
            d->m_requestCentreRows.remove(request);
            delete request;
        }

        // emit data changed for those that have changed.
        QList<int> rows = rowMap.keys();
        while (rows.size() > 0) {
            // we want to emit the dataChanged signal as few times as possible
            // so, we coalesce the changes into lumps of contiguous changes.
            int lowestIndex = rows.at(0);
            int highestIndex = rows.at(0);
            int nbrAccountedFor = 1;
            int nbrRows = rows.size();
            while (nbrAccountedFor < nbrRows) {
                int temp = highestIndex;
                highestIndex = rows.at(nbrAccountedFor);
                if ((highestIndex - temp) > 1) {
                    highestIndex = temp;
                    break;
                }
                nbrAccountedFor += 1;
            }

            while (nbrAccountedFor > 0) {
                rows.removeFirst();
                nbrAccountedFor -= 1;
            }

            // calculate the indices of the boundaries, and emit the signal.
            QModelIndex lowerBound = QAbstractItemModel::createIndex(lowestIndex, 0);
            QModelIndex upperBound = QAbstractItemModel::createIndex(highestIndex, 0);
            emit dataChanged(lowerBound, upperBound);
        }
    }

    /*!
     * Processes the results of a contact id fetch request.
     * If the \a appendOnly flag is set, the new data is appended to the existing data
     * and the dataChanged() signal is emitted; otherwise, the model emits the reset() signal
     * once the new data has been loaded.
     */
    void QContactListModel::contactIdFetchRequestProgress(QContactLocalIdFetchRequest* request, bool appendOnly)
    {
        // first, if it's not append only, we need to rebuild the entire list + cache.
        if (!appendOnly) {
            d->m_cache.clear();
            d->m_rowsToIds.clear();
            d->m_idsToRows.clear();
        }

        // then get the results, calculate the start and end indices, and fill our data structures.
        QList<QContactLocalId> ids = request->ids();
        int startIndex = d->m_idsToRows.count();
        int endIndex = ids.size();
        for (int i = startIndex; i < endIndex; i++) {
            d->m_rowsToIds.insert(i, ids.at(i));
            d->m_idsToRows.insert(ids.at(i), i);
        }

        // and if we need to, emit the reset signals.
        if (!appendOnly)
            reset();
        else
            emit dataChanged(QAbstractItemModel::createIndex(startIndex,0), QAbstractItemModel::createIndex(endIndex,0));
    }

    /*!
     * Requests data from the new backend.
     */
    void QContactListModel::backendChanged()
    {
        d->m_idRequest->start();
        d->m_idRequest->waitForFinished();
    }

Publicité

Best Of

Actualités les plus lues

Semaine
Mois
Année
  1. « Quelque chose ne va vraiment pas avec les développeurs "modernes" », un développeur à "l'ancienne" critique la multiplication des bibliothèques 68
  2. Apercevoir la troisième dimension ou l'utilisation multithreadée d'OpenGL dans Qt, un article des Qt Quarterly traduit par Guillaume Belz 0
  3. Les développeurs ignorent-ils trop les failles découvertes dans leur code ? Prenez-vous en compte les remarques des autres ? 17
  4. BlackBerry 10 : premières images du prochain OS de RIM qui devrait intégrer des widgets et des tuiles inspirées de Windows Phone 0
  5. Quelles nouveautés de C++11 Visual C++ doit-il rapidement intégrer ? Donnez-nous votre avis 10
  6. Adieu qmake, bienvenue qbs : Qt Building Suite, un outil déclaratif et extensible pour la compilation de projets Qt 17
  7. La rubrique PyQt/PySide a besoin de vous ! 0
Page suivante

Le blog Digia au hasard

Logo

Créer des applications avec un style Metro avec Qt, exemples en QML et C++, un article de Digia Qt traduit par Thibaut Cuvelier

Le blog Digia est l'endroit privilégié pour la communication sur l'édition commerciale de Qt, où des réponses publiques sont apportées aux questions les plus posées au support. Lire l'article.

Communauté

Ressources

Liens utiles

Contact

  • Vous souhaitez rejoindre la rédaction ou proposer un tutoriel, une traduction, une question... ? Postez dans le forum Contribuez ou contactez-nous par MP ou par email (voir en bas de page).

Qt dans le magazine

Cette page est une traduction d'une page de la documentation de Qt, écrite par Nokia Corporation and/or its subsidiary(-ies). Les éventuels problèmes résultant d'une mauvaise traduction ne sont pas imputables à Nokia. Qt qtmobility-1.0-tp
Copyright © 2012 Developpez LLC. Tous droits réservés Developpez LLC. Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son contenu : textes, documents et images sans l'autorisation expresse de Developpez LLC. Sinon, vous encourez selon la loi jusqu'à 3 ans de prison et jusqu'à 300 000 E de dommages et intérêts. Cette page est déposée à la SACD.
Vous avez déniché une erreur ? Un bug ? Une redirection cassée ? Ou tout autre problème, quel qu'il soit ? Ou bien vous désirez participer à ce projet de traduction ? N'hésitez pas à nous contacter ou par MP !
 
 
 
 
Partenaires

Hébergement Web