Enginio C++ Examples - Cloud Address Book

 /****************************************************************************
 **
 ** Copyright (C) 2015 The Qt Company Ltd.
 ** Contact: http://www.qt.io/licensing/
 **
 ** This file is part of the examples of the Qt Toolkit.
 **
 ** $QT_BEGIN_LICENSE:BSD$
 ** You may use this file under the terms of the BSD license as follows:
 **
 ** "Redistribution and use in source and binary forms, with or without
 ** modification, are permitted provided that the following conditions are
 ** met:
 **   * Redistributions of source code must retain the above copyright
 **     notice, this list of conditions and the following disclaimer.
 **   * Redistributions in binary form must reproduce the above copyright
 **     notice, this list of conditions and the following disclaimer in
 **     the documentation and/or other materials provided with the
 **     distribution.
 **   * Neither the name of The Qt Company Ltd nor the names of its
 **     contributors may be used to endorse or promote products derived
 **     from this software without specific prior written permission.
 **
 **
 ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 ** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 ** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 ** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
 **
 ** $QT_END_LICENSE$
 **
 ****************************************************************************/

 #include <QtCore/qhash.h>
 #include <QtCore/qbytearray.h>
 #include <QtCore/qjsondocument.h>
 #include <QtCore/qjsonvalue.h>
 #include <QtCore/qjsonobject.h>
 #include <QtCore/qjsonarray.h>

 #include <QtGui/qfont.h>

 #include "addressbookmodel.h"

 AddressBookModel::AddressBookModel(QObject *parent)
     : EnginioModel(parent)
     , _searchReply()
 {}

 AddressBookModel::~AddressBookModel()
 {}

 QVariant AddressBookModel::data(const QModelIndex &index, int role) const
 {
     if (role == Qt::DisplayRole) {
         // we assume here that column order is constant and it is the same as in AddressBookModel::Roles
         return EnginioModel::data(index, FirstNameRole + index.column()).value<QJsonValue>().toString();
     }
     if (role == Qt::FontRole) {
         // this role is used to mark items found in the full text search.
         QFont font;
         QString id = EnginioModel::data(index, Enginio::IdRole).value<QString>();
         font.setBold(_markedItems.contains(id));
         return font;
     }

     return EnginioModel::data(index, role);
 }

 bool AddressBookModel::setData(const QModelIndex &index, const QVariant &value, int role)
 {
     bool result = role == Qt::EditRole
             ? EnginioModel::setData(this->index(index.row()), value, FirstNameRole + index.column())
             : EnginioModel::setData(this->index(index.row()), value, role);

     // if the data was edited, the marked items set may not be valid anymore
     // so we need to clear it.
     if (result)
         _markedItems.clear();
     return result;
 }

 int AddressBookModel::columnCount(const QModelIndex &parent) const
 {
     return parent.isValid() ? 0 : 5;
 }

 QVariant AddressBookModel::headerData(int section, Qt::Orientation orientation, int role) const
 {
     if (orientation == Qt::Horizontal && role == Qt::DisplayRole) {
         switch (section) {
         case 0: return QStringLiteral("First name");
         case 1: return QStringLiteral("Last name");
         case 2: return QStringLiteral("Email");
         case 3: return QStringLiteral("Phone number");
         case 4: return QStringLiteral("Address");
         }
         return QVariant();
     }
     return EnginioModel::headerData(section, orientation, role);
 }

 QHash<int, QByteArray> AddressBookModel::roleNames() const
 {
     QHash<int, QByteArray> roles = EnginioModel::roleNames();
     roles.insert(FirstNameRole, "firstName");
     roles.insert(LastNameRole, "lastName");
     roles.insert(EmailRole, "email");
     roles.insert(PhoneRole, "phone");
     roles.insert(AddressRole, "address");
     return roles;
 }

 void AddressBookModel::newSearch(const QString &search)
 {
     if (search.isEmpty())
         return;

     // construct JSON object:
     //    {
     //        "objectTypes": ["objects.addressbook"],
     //        "search": { "phrase": "*PHRASE*",
     //            "properties": ["firstName", "lastName", "email", "phone", "address"]
     //        }
     //    }

     QJsonObject query;
     {
         QJsonArray objectTypes;
         objectTypes.append(QString::fromUtf8("objects.addressbook"));

         QJsonArray properties;
         properties.append(QString::fromUtf8("firstName"));
         properties.append(QString::fromUtf8("lastName"));
         properties.append(QString::fromUtf8("email"));
         properties.append(QString::fromUtf8("phone"));
         properties.append(QString::fromUtf8("address"));

         QJsonObject searchQuery;
         searchQuery.insert("phrase", "*" + search + "*");
         searchQuery.insert("properties", properties);

         query.insert("objectTypes", objectTypes);
         query.insert("search", searchQuery);
     }

     if (_searchReply) {
         // a new search is created before the old one was finished
         // we will not be interested in the results of the old search.
         _searchReply->disconnect(this);
     }

     _searchReply =  client()->fullTextSearch(query);
     QObject::connect(_searchReply, &EnginioReply::finished, this, &AddressBookModel::searchResultsArrived);
     QObject::connect(_searchReply, &EnginioReply::finished, _searchReply, &EnginioReply::deleteLater);
 }

 void AddressBookModel::searchResultsArrived()
 {
     // clear old marks.
     _markedItems.clear();

     // update marked ids.
     QJsonArray results = _searchReply->data()["results"].toArray();
     foreach (const QJsonValue &value, results) {
         QJsonObject person = value.toObject();
         _markedItems.insert(person["id"].toString());
     }

     QVector<int> roles;
     roles.append(Qt::FontRole);
     // We do not keep id -> row mapping, therefore it is easier to emit
     // data change signal for all items, even if it is not optimal from
     // the performance point of view.
     emit dataChanged(index(0), index(rowCount() - 1, columnCount() - 1) , roles);

     _searchReply = 0;
     emit searchFinished();
 }