[Merge] lp:~boiko/history-service/keep_models_sorted into lp:history-service
Tiago Salem Herrmann
tiago.herrmann at canonical.com
Fri Sep 12 19:55:21 UTC 2014
Review: Needs Information
Diff comments:
> === modified file 'Ubuntu/History/CMakeLists.txt'
> --- Ubuntu/History/CMakeLists.txt 2014-09-09 23:16:29 +0000
> +++ Ubuntu/History/CMakeLists.txt 2014-09-09 23:16:29 +0000
> @@ -4,30 +4,30 @@
> contactmatcher.cpp
> historyeventmodel.cpp
> historygroupedeventsmodel.cpp
> - historythreadgroupingproxymodel.cpp
> - historyqmltexteventattachment.cpp
> + historygroupedthreadsmodel.cpp
> + historymodel.cpp
> historyqmlfilter.cpp
> historyqmlintersectionfilter.cpp
> historyqmlplugin.cpp
> historyqmlsort.cpp
> + historyqmltexteventattachment.cpp
> historyqmlunionfilter.cpp
> historythreadmodel.cpp
> - sortproxymodel.cpp
> )
>
> set(plugin_HDRS
> contactmatcher_p.h
> historyeventmodel.h
> historygroupedeventsmodel.h
> - historythreadgroupingproxymodel.h
> - historyqmltexteventattachment.h
> + historygroupedthreadsmodel.h
> + historymodel.cpp
> historyqmlfilter.h
> historyqmlintersectionfilter.h
> historyqmlplugin.h
> historyqmlsort.h
> + historyqmltexteventattachment.h
> historyqmlunionfilter.h
> historythreadmodel.h
> - sortproxymodel.h
> )
>
> include_directories(
>
> === modified file 'Ubuntu/History/historyeventmodel.cpp'
> --- Ubuntu/History/historyeventmodel.cpp 2014-09-09 23:16:29 +0000
> +++ Ubuntu/History/historyeventmodel.cpp 2014-09-09 23:16:29 +0000
> @@ -20,44 +20,23 @@
> */
>
> #include "historyeventmodel.h"
> -#include "historyqmlfilter.h"
> -#include "historyqmlsort.h"
> #include "eventview.h"
> -#include "intersectionfilter.h"
> +#include "historyqmltexteventattachment.h"
> #include "manager.h"
> -#include "thread.h"
> -#include "textevent.h"
> -#include "texteventattachment.h"
> -#include "historyqmltexteventattachment.h"
> -#include "phoneutils_p.h"
> -#include "thread.h"
> -#include "types.h"
> -#include "voiceevent.h"
> -#include "contactmatcher_p.h"
> +#include <QDBusMetaType>
> #include <QDebug>
> #include <QTimerEvent>
> -#include <QDBusMetaType>
>
> HistoryEventModel::HistoryEventModel(QObject *parent) :
> - QAbstractListModel(parent), mCanFetchMore(true), mFilter(0),
> - mSort(new HistoryQmlSort(this)), mType(HistoryThreadModel::EventTypeText),
> - mMatchContacts(false), mEventWritingTimer(0), mUpdateTimer(0)
> + HistoryModel(parent), mCanFetchMore(true), mEventWritingTimer(0)
> {
> - connect(this, SIGNAL(rowsInserted(QModelIndex,int,int)), this, SIGNAL(countChanged()));
> - connect(this, SIGNAL(rowsRemoved(QModelIndex,int,int)), this, SIGNAL(countChanged()));
> - connect(this, SIGNAL(modelReset()), this, SIGNAL(countChanged()));
> -
> // configure the roles
> - mRoles[AccountIdRole] = "accountId";
> - mRoles[ThreadIdRole] = "threadId";
> - mRoles[ParticipantsRole] = "participants";
> - mRoles[TypeRole] = "type";
> + mRoles = HistoryModel::roleNames();
> mRoles[EventIdRole] = "eventId";
> mRoles[SenderIdRole] = "senderId";
> mRoles[TimestampRole] = "timestamp";
> mRoles[DateRole] = "date";
> mRoles[NewEventRole] = "newEvent";
> - mRoles[PropertiesRole] = "properties";
> mRoles[TextMessageRole] = "textMessage";
> mRoles[TextMessageTypeRole] = "textMessageType";
> mRoles[TextMessageStatusRole] = "textMessageStatus";
> @@ -66,13 +45,6 @@
> mRoles[TextReadSubjectRole] = "textSubject";
> mRoles[CallMissedRole] = "callMissed";
> mRoles[CallDurationRole] = "callDuration";
> -
> - connect(ContactMatcher::instance(),
> - SIGNAL(contactInfoChanged(QString,QVariantMap)),
> - SLOT(onContactInfoChanged(QString,QVariantMap)));
> -
> - // create the view and get some objects
> - triggerQueryUpdate();
> }
>
> int HistoryEventModel::rowCount(const QModelIndex &parent) const
> @@ -90,8 +62,12 @@
> return QVariant();
> }
>
> - return eventData(mEvents[index.row()], role);
> -}
> + QVariant result = eventData(mEvents[index.row()], role);
> + if (result.isNull()) {
> + result = HistoryModel::data(index, role);
> + }
> + return result;
> + }
>
> QVariant HistoryEventModel::eventData(const History::Event &event, int role) const
> {
> @@ -110,22 +86,6 @@
> QVariant result;
>
> switch (role) {
> - case AccountIdRole:
> - result = event.accountId();
> - break;
> - case ThreadIdRole:
> - result = event.threadId();
> - break;
> - case ParticipantsRole:
> - if (mMatchContacts) {
> - result = ContactMatcher::instance()->contactInfo(event.participants());
> - } else {
> - result = event.participants();
> - }
> - break;
> - case TypeRole:
> - result = event.type();
> - break;
> case EventIdRole:
> result = event.eventId();
> break;
> @@ -215,7 +175,6 @@
>
> History::Events events = fetchNextPage();
>
> - qDebug() << "Got events:" << events.count();
> if (events.isEmpty()) {
> mCanFetchMore = false;
> Q_EMIT canFetchMoreChanged();
> @@ -231,97 +190,6 @@
> return mRoles;
> }
>
> -HistoryQmlFilter *HistoryEventModel::filter() const
> -{
> - return mFilter;
> -}
> -
> -void HistoryEventModel::setFilter(HistoryQmlFilter *value)
> -{
> - if (mFilter) {
> - mFilter->disconnect(this);
> - }
> -
> - mFilter = value;
> - if (mFilter) {
> - connect(mFilter,
> - SIGNAL(filterChanged()),
> - SLOT(triggerQueryUpdate()));
> - }
> -
> - Q_EMIT filterChanged();
> - triggerQueryUpdate();
> -}
> -
> -HistoryQmlSort *HistoryEventModel::sort() const
> -{
> - return mSort;
> -}
> -
> -void HistoryEventModel::setSort(HistoryQmlSort *value)
> -{
> - // disconnect the previous sort
> - if (mSort) {
> - mSort->disconnect(this);
> - }
> -
> - mSort = value;
> - if (mSort) {
> - connect(mSort,
> - SIGNAL(sortChanged()),
> - SLOT(triggerQueryUpdate()));
> - }
> -
> - Q_EMIT sortChanged();
> - triggerQueryUpdate();
> -}
> -
> -HistoryThreadModel::EventType HistoryEventModel::type() const
> -{
> - return mType;
> -}
> -
> -void HistoryEventModel::setType(HistoryThreadModel::EventType value)
> -{
> - mType = value;
> - Q_EMIT typeChanged();
> - triggerQueryUpdate();
> -}
> -
> -bool HistoryEventModel::matchContacts() const
> -{
> - return mMatchContacts;
> -}
> -
> -void HistoryEventModel::setMatchContacts(bool value)
> -{
> - mMatchContacts = value;
> - Q_EMIT matchContactsChanged();
> -
> - // mark all indexes as changed
> - if (rowCount() > 0) {
> - Q_EMIT dataChanged(index(0), index(rowCount()-1));
> - }
> -}
> -
> -QString HistoryEventModel::threadIdForParticipants(const QString &accountId, int eventType, const QStringList &participants, int matchFlags, bool create)
> -{
> - if (participants.isEmpty()) {
> - return QString::null;
> - }
> -
> - History::Thread thread = History::Manager::instance()->threadForParticipants(accountId,
> - (History::EventType)eventType,
> - participants,
> - (History::MatchFlags)matchFlags,
> - create);
> - if (!thread.isNull()) {
> - return thread.threadId();
> - }
> -
> - return QString::null;
> -}
> -
> bool HistoryEventModel::removeEvent(const QString &accountId, const QString &threadId, const QString &eventId, int eventType)
> {
> History::Event event = History::Manager::instance()->getSingleEvent((History::EventType)eventType, accountId, threadId, eventId);
> @@ -333,7 +201,7 @@
> History::TextEvent textEvent;
> History::Event event = History::Manager::instance()->getSingleEvent((History::EventType)eventType, accountId, threadId, eventId);
> if (event.type() != History::EventTypeText) {
> - qDebug() << "Trying to remove an attachment from a non text event";
> + qWarning() << "Trying to remove an attachment from a non text event";
> return false;
> }
> QVariantMap properties = event.properties();
> @@ -347,7 +215,7 @@
> }
> }
> if (count == attachmentProperties.size()) {
> - qDebug() << "No attachment found for id " << attachmentId;
> + qWarning() << "No attachment found for id " << attachmentId;
> return false;
> }
> properties[History::FieldAttachments] = QVariant::fromValue(newAttachmentProperties);
> @@ -435,18 +303,17 @@
> return;
> }
>
> - // filter the list for items already in the model
> - History::Events filteredEvents;
> Q_FOREACH(const History::Event &event, events) {
> - if (!mEvents.contains(event)) {
> - filteredEvents << event;
> + // if the event is already on the model, skip it
> + if (mEvents.contains(event)) {
> + continue;
> }
> +
> + int pos = positionForItem(event.properties());
> + beginInsertRows(QModelIndex(), pos, pos);
> + mEvents.insert(pos, event);
> + endInsertRows();
> }
> -
> - //FIXME: handle sorting
> - beginInsertRows(QModelIndex(), mEvents.count(), mEvents.count() + filteredEvents.count() - 1);
> - mEvents << filteredEvents;
> - endInsertRows();
> }
>
> void HistoryEventModel::onEventsModified(const History::Events &events)
> @@ -489,36 +356,9 @@
> // should be handle internally in History::EventView?
> }
>
> -void HistoryEventModel::onContactInfoChanged(const QString &phoneNumber, const QVariantMap &contactInfo)
> -{
> - Q_UNUSED(contactInfo)
> - if (!mMatchContacts) {
> - return;
> - }
> -
> - QList<QModelIndex> changedIndexes;
> - int count = rowCount();
> - for (int i = 0; i < count; ++i) {
> - // WARNING: do not use mEvents directly to verify which indexes to change as there is the
> - // HistoryGroupedEventsModel which is based on this model and handles the items in a different way
> - QModelIndex idx = index(i);
> - QVariantMap eventProperties = idx.data(PropertiesRole).toMap();
> - QStringList participants = eventProperties[History::FieldParticipants].toStringList();
> - Q_FOREACH(const QString &participant, participants) {
> - if (PhoneUtils::comparePhoneNumbers(participant, phoneNumber)) {
> - changedIndexes << idx;
> - }
> - }
> - }
> -
> - // now emit the dataChanged signal to all changed indexes
> - Q_FOREACH(const QModelIndex &idx, changedIndexes) {
> - Q_EMIT dataChanged(idx, idx);
> - }
> -}
> -
> void HistoryEventModel::timerEvent(QTimerEvent *event)
> {
> + HistoryModel::timerEvent(event);
> if (event->timerId() == mEventWritingTimer) {
> killTimer(mEventWritingTimer);
> mEventWritingTimer = 0;
> @@ -532,10 +372,6 @@
> qDebug() << "... succeeded!";
> mEventWritingQueue.clear();
> }
> - } else if (event->timerId() == mUpdateTimer) {
> - killTimer(mUpdateTimer);
> - mUpdateTimer = 0;
> - updateQuery();
> }
> }
>
> @@ -543,21 +379,3 @@
> {
> return mView->nextPage();
> }
> -
> -QVariant HistoryEventModel::get(int row) const
> -{
> - if (row >= this->rowCount() || row < 0) {
> - return QVariant();
> - }
> -
> - return QVariant(mEvents[row].properties());
> -}
> -
> -void HistoryEventModel::triggerQueryUpdate()
> -{
> - if (mUpdateTimer) {
> - killTimer(mUpdateTimer);
> - }
> - // delay the loading of the model data until the settings settle down
> - mUpdateTimer = startTimer(100);
> -}
>
> === modified file 'Ubuntu/History/historyeventmodel.h'
> --- Ubuntu/History/historyeventmodel.h 2014-09-09 23:16:29 +0000
> +++ Ubuntu/History/historyeventmodel.h 2014-09-09 23:16:29 +0000
> @@ -22,32 +22,22 @@
> #ifndef HISTORYEVENTMODEL_H
> #define HISTORYEVENTMODEL_H
>
> -#include <QAbstractListModel>
> +#include "historymodel.h"
> +#include "textevent.h"
> +#include "voiceevent.h"
> #include <QStringList>
> -#include "historythreadmodel.h"
>
> -class HistoryEventModel : public QAbstractListModel
> +class HistoryEventModel : public HistoryModel
> {
> Q_OBJECT
> - Q_PROPERTY(int count READ rowCount NOTIFY countChanged)
> - Q_PROPERTY(HistoryQmlFilter *filter READ filter WRITE setFilter NOTIFY filterChanged)
> - Q_PROPERTY(HistoryQmlSort *sort READ sort WRITE setSort NOTIFY sortChanged)
> - Q_PROPERTY(HistoryThreadModel::EventType type READ type WRITE setType NOTIFY typeChanged)
> - Q_PROPERTY(bool matchContacts READ matchContacts WRITE setMatchContacts NOTIFY matchContactsChanged)
> - Q_PROPERTY(bool canFetchMore READ canFetchMore NOTIFY canFetchMoreChanged)
> - Q_ENUMS(Role)
> + Q_ENUMS(EventRole)
> public:
> - enum Role {
> - AccountIdRole = Qt::UserRole,
> - ThreadIdRole,
> - ParticipantsRole,
> - TypeRole,
> - EventIdRole,
> + enum EventRole {
> + EventIdRole = HistoryModel::LastRole,
> SenderIdRole,
> TimestampRole,
> DateRole,
> NewEventRole,
> - PropertiesRole,
> TextMessageRole,
> TextMessageTypeRole,
> TextMessageStatusRole,
> @@ -56,7 +46,7 @@
> TextMessageAttachmentsRole,
> CallMissedRole,
> CallDurationRole,
> - LastRole
> + LastEventRole
> };
>
> explicit HistoryEventModel(QObject *parent = 0);
> @@ -70,45 +60,15 @@
>
> virtual QHash<int, QByteArray> roleNames() const;
>
> - HistoryQmlFilter *filter() const;
> - void setFilter(HistoryQmlFilter *value);
> -
> - HistoryQmlSort *sort() const;
> - void setSort(HistoryQmlSort *value);
> -
> - HistoryThreadModel::EventType type() const;
> - void setType(HistoryThreadModel::EventType value);
> -
> - bool matchContacts() const;
> - void setMatchContacts(bool value);
> -
> - Q_INVOKABLE QString threadIdForParticipants(const QString &accountId,
> - int eventType,
> - const QStringList &participants,
> - int matchFlags = (int)History::MatchCaseSensitive,
> - bool create = false);
> -
> Q_INVOKABLE bool removeEvent(const QString &accountId, const QString &threadId, const QString &eventId, int eventType);
> Q_INVOKABLE bool markEventAsRead(const QString &accountId, const QString &threadId, const QString &eventId, int eventType);
> -
> Q_INVOKABLE bool removeEventAttachment(const QString &accountId, const QString &threadId, const QString &eventId, int eventType, const QString &attachmentId);
> - Q_INVOKABLE virtual QVariant get(int row) const;
> -
> -Q_SIGNALS:
> - void countChanged();
> - void filterChanged();
> - void sortChanged();
> - void typeChanged();
> - void matchContactsChanged();
> - void canFetchMoreChanged();
>
> protected Q_SLOTS:
> - void triggerQueryUpdate();
> virtual void updateQuery();
> virtual void onEventsAdded(const History::Events &events);
> virtual void onEventsModified(const History::Events &events);
> virtual void onEventsRemoved(const History::Events &events);
> - void onContactInfoChanged(const QString &phoneNumber, const QVariantMap &contactInfo);
>
> protected:
> void timerEvent(QTimerEvent *event);
> @@ -118,15 +78,10 @@
> History::EventViewPtr mView;
> History::Events mEvents;
> bool mCanFetchMore;
> - HistoryQmlFilter *mFilter;
> - HistoryQmlSort *mSort;
> - HistoryThreadModel::EventType mType;
> - bool mMatchContacts;
> QHash<int, QByteArray> mRoles;
> mutable QMap<History::TextEvent, QList<QVariant> > mAttachmentCache;
> History::Events mEventWritingQueue;
> int mEventWritingTimer;
> - int mUpdateTimer;
> };
>
> #endif // HISTORYEVENTMODEL_H
>
> === modified file 'Ubuntu/History/historygroupedeventsmodel.cpp'
> --- Ubuntu/History/historygroupedeventsmodel.cpp 2014-08-21 19:01:40 +0000
> +++ Ubuntu/History/historygroupedeventsmodel.cpp 2014-09-09 23:16:29 +0000
> @@ -63,6 +63,11 @@
> break;
> }
>
> + if (result.isNull()) {
> + // for the shared roles
> + result = HistoryModel::data(index, role);
> + }
> +
> return result;
> }
>
> @@ -88,7 +93,8 @@
> found = true;
> addEventToGroup(event, group, pos);
> break;
> - } else if (isAscending() ? lessThan(group.displayedEvent, event) : lessThan(event, group.displayedEvent)) {
> + } else if (isAscending() ? lessThan(group.displayedEvent.properties(), event.properties()) :
> + lessThan(event.properties(), group.displayedEvent.properties())) {
> break;
> }
> }
> @@ -134,7 +140,7 @@
> }
>
> Q_FOREACH(const History::Event &event, events) {
> - int pos = positionForEvent(event);
> + int pos = positionForItem(event.properties());
>
> // check if the event belongs to the group at the position
> if (pos >= 0 && pos < mEventGroups.count()) {
> @@ -167,7 +173,7 @@
> void HistoryGroupedEventsModel::onEventsRemoved(const History::Events &events)
> {
> Q_FOREACH(const History::Event &event, events) {
> - int pos = positionForEvent(event);
> + int pos = positionForItem(event.properties());
> if (pos < 0 || pos >= rowCount()) {
> continue;
> }
> @@ -179,25 +185,6 @@
> }
> }
>
> -bool HistoryGroupedEventsModel::compareParticipants(const QStringList &list1, const QStringList &list2)
> -{
> - if (list1.count() != list2.count()) {
> - return false;
> - }
> -
> - int found = 0;
> - Q_FOREACH(const QString &participant, list1) {
> - Q_FOREACH(const QString &item, list2) {
> - if (PhoneUtils::comparePhoneNumbers(participant, item)) {
> - found++;
> - break;
> - }
> - }
> - }
> -
> - return found == list1.count();
> -}
> -
> bool HistoryGroupedEventsModel::areOfSameGroup(const History::Event &event1, const History::Event &event2)
> {
> QVariantMap props1 = event1.properties();
> @@ -231,7 +218,8 @@
> bool append = true;
> for (int i = 0; i < group.events.count(); ++i) {
> History::Event &otherEvent = group.events[i];
> - if (isAscending() ? lessThan(event, otherEvent) : lessThan(otherEvent, event)) {
> + if (isAscending() ? lessThan(event.properties(), otherEvent.properties()) :
> + lessThan(otherEvent.properties(), event.properties())) {
> group.events.insert(i, event);
> append = false;
> break;
> @@ -270,7 +258,8 @@
> // check what is the event that should be displayed
> group.displayedEvent = group.events.first();
> Q_FOREACH(const History::Event &other, group.events) {
> - if (isAscending() ? lessThan(other, group.displayedEvent) : lessThan(group.displayedEvent, other)) {
> + if (isAscending() ? lessThan(other.properties(), group.displayedEvent.properties()) :
> + lessThan(group.displayedEvent.properties(), other.properties())) {
> group.displayedEvent = other;
> }
> }
> @@ -279,45 +268,6 @@
> Q_EMIT dataChanged(idx, idx);
> }
>
> -bool HistoryGroupedEventsModel::lessThan(const History::Event &left, const History::Event &right) const
> -{
> -
> - QVariant leftValue = left.properties()[sort()->sortField()];
> - QVariant rightValue = right.properties()[sort()->sortField()];
> - return leftValue < rightValue;
> -}
> -
> -int HistoryGroupedEventsModel::positionForEvent(const History::Event &event) const
> -{
> - // do a binary search for the item position on the list
> - int lowerBound = 0;
> - int upperBound = mEventGroups.count() - 1;
> - if (upperBound < 0) {
> - return 0;
> - }
> -
> - while (true) {
> - int pos = (upperBound + lowerBound) / 2;
> - const History::Event &posEvent = mEventGroups[pos].displayedEvent;
> - if (lowerBound == pos) {
> - if (isAscending() ? lessThan(event, posEvent) : lessThan(posEvent, event)) {
> - return pos;
> - }
> - }
> - if (isAscending() ? lessThan(posEvent, event) : lessThan(event, posEvent)) {
> - lowerBound = pos + 1; // its in the upper
> - if (lowerBound > upperBound) {
> - return pos += 1;
> - }
> - } else if (lowerBound > upperBound) {
> - return pos;
> - } else {
> - upperBound = pos - 1; // its in the lower
> - }
> - }
> -
> -}
> -
> QVariant HistoryGroupedEventsModel::get(int row) const
> {
> if (row >= rowCount() || row < 0) {
> @@ -339,8 +289,3 @@
> Q_EMIT groupingPropertiesChanged();
> triggerQueryUpdate();
> }
> -
> -bool HistoryGroupedEventsModel::isAscending() const
> -{
> - return sort() && sort()->sort().sortOrder() == Qt::AscendingOrder;
> -}
>
> === modified file 'Ubuntu/History/historygroupedeventsmodel.h'
> --- Ubuntu/History/historygroupedeventsmodel.h 2014-08-16 22:11:30 +0000
> +++ Ubuntu/History/historygroupedeventsmodel.h 2014-09-09 23:16:29 +0000
> @@ -39,7 +39,7 @@
> Q_ENUMS(GroupedRole)
> public:
> enum GroupedRole {
> - EventsRole = HistoryEventModel::LastRole,
> + EventsRole = HistoryEventModel::LastEventRole,
> EventCountRole
> };
>
> @@ -55,8 +55,6 @@
> QStringList groupingProperties() const;
> void setGroupingProperties(const QStringList &properties);
>
> - bool isAscending() const;
> -
> Q_SIGNALS:
> void groupingPropertiesChanged();
>
> @@ -67,12 +65,9 @@
> void onEventsRemoved(const History::Events &events);
>
> protected:
> - bool compareParticipants(const QStringList &list1, const QStringList &list2);
> bool areOfSameGroup(const History::Event &event1, const History::Event &event2);
> void addEventToGroup(const History::Event &event, HistoryEventGroup &group, int row);
> void removeEventFromGroup(const History::Event &event, HistoryEventGroup &group, int row);
> - bool lessThan(const History::Event &left, const History::Event &right) const;
> - int positionForEvent(const History::Event &event) const;
>
> private:
> QStringList mGroupingProperties;
>
> === renamed file 'Ubuntu/History/historythreadgroupingproxymodel.cpp' => 'Ubuntu/History/historygroupedthreadsmodel.cpp'
> --- Ubuntu/History/historythreadgroupingproxymodel.cpp 2014-07-22 19:13:53 +0000
> +++ Ubuntu/History/historygroupedthreadsmodel.cpp 2014-09-09 23:16:29 +0000
> @@ -19,339 +19,290 @@
> * along with this program. If not, see <http://www.gnu.org/licenses/>.
> */
>
> -#include "historythreadgroupingproxymodel.h"
> -#include "historythreadmodel.h"
> +#include "historygroupedthreadsmodel.h"
> #include "phoneutils_p.h"
> #include <QTimer>
> #include <QDebug>
>
> -HistoryThreadGroupingProxyModel::HistoryThreadGroupingProxyModel(QObject *parent) :
> - SortProxyModel(parent)
> +HistoryGroupedThreadsModel::HistoryGroupedThreadsModel(QObject *parent) :
> + HistoryThreadModel(parent)
> {
> - connect(this, SIGNAL(sourceModelChanged()), SLOT(onSourceModelChanged()));
> + mRoles = HistoryThreadModel::roleNames();
> + mRoles[ThreadsRole] = "threads";
> }
>
> -QVariant HistoryThreadGroupingProxyModel::data(const QModelIndex &index, int role) const
> +QVariant HistoryGroupedThreadsModel::data(const QModelIndex &index, int role) const
> {
> if (!index.isValid()) {
> return QVariant();
> }
>
> - QModelIndex sourceIndex = mapToSource(index);
> - HistoryThreadGroup group = groupForSourceIndex(sourceIndex);
> + const HistoryThreadGroup &group = mGroups[index.row()];
>
> - // fill the result using the standard QSortFilterProxyModel data function
> - // and overwrite it if necessary
> - QVariant result = SortProxyModel::data(index, role);
> + // get the data from the latest thread, and overwrite if necessary
> + QVariant result = threadData(group.displayedThread, role);
> switch (role) {
> case HistoryThreadModel::CountRole: {
> int count = 0;
> - Q_FOREACH(const QPersistentModelIndex &row, group.rows) {
> - count += row.data(HistoryThreadModel::CountRole).toInt();
> + Q_FOREACH(const History::Thread &thread, group.threads) {
> + count += thread.count();
> }
> result = count;
> break;
> }
> case HistoryThreadModel::UnreadCountRole: {
> int count = 0;
> - Q_FOREACH(const QPersistentModelIndex &row, group.rows) {
> - count += row.data(HistoryThreadModel::UnreadCountRole).toInt();
> + Q_FOREACH(const History::Thread &thread, group.threads) {
> + count += thread.unreadCount();
> }
> result = count;
> break;
> }
> case ThreadsRole: {
> QVariantList threads;
> - Q_FOREACH(const QPersistentModelIndex &row, group.rows) {
> - threads << row.data(HistoryThreadModel::PropertiesRole).toMap();
> + Q_FOREACH(const History::Thread &thread, group.threads) {
> + threads << thread.properties();
> }
> result = threads;
> break;
> }
> }
>
> + if (result.isNull()) {
> + // get the shared roles
> + result = HistoryModel::data(index, role);
> + }
> +
> return result;
> }
>
> -QHash<int, QByteArray> HistoryThreadGroupingProxyModel::roleNames() const
> +void HistoryGroupedThreadsModel::fetchMore(const QModelIndex &parent)
> +{
> + if (!canFetchMore(parent)) {
> + return;
> + }
> +
> + const History::Threads &threads = fetchNextPage();
> + Q_FOREACH(const History::Thread &thread, threads) {
> + processThreadGrouping(thread);
> + }
> + notifyDataChanged();
> +}
> +
> +QHash<int, QByteArray> HistoryGroupedThreadsModel::roleNames() const
> {
> return mRoles;
> }
>
> -bool HistoryThreadGroupingProxyModel::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const
> -{
> - QModelIndex sourceIndex = sourceModel()->index(sourceRow, 0, sourceParent);
> - const HistoryThreadModel *model = qobject_cast<const HistoryThreadModel*>(sourceModel());
> -
> - if (!model || !sourceIndex.isValid()) {
> - return false;
> - }
> -
> - HistoryThreadGroup group = groupForSourceIndex(sourceIndex);
> - return (group.displayedIndex == sourceIndex);
> -}
> -
> -HistoryThreadGroup & HistoryThreadGroupingProxyModel::groupForEntry(const QVariant &propertyValue) const
> -{
> - QString finalValue;
> - if (mGroupingProperty == "participants") {
> - QStringList participants = propertyValue.toStringList();
> - HistoryThreadGroupMap::iterator it = mGroups.begin();
> - for (; it != mGroups.end(); ++it) {
> - if (compareParticipants(it.value().participants, participants)) {
> - finalValue = it.key();
> - break;
> - }
> - }
> -
> - if (finalValue.isEmpty()) {
> - // FIXME: find a separator that is not used in any IM service id
> - finalValue = participants.join("||");
> - HistoryThreadGroup &group = mGroups[finalValue];
> - // set participants, otherwise they will be empty and further phone comparison will fail
> - group.participants = propertyValue.toStringList();
> - return group;
> - }
> - }
> - if (finalValue.isEmpty()) {
> - finalValue = propertyValue.toString();
> - }
> - HistoryThreadGroup &group = mGroups[finalValue];
> - return group;
> -}
> -
> -void HistoryThreadGroupingProxyModel::removeGroup(const QVariant &propertyValue)
> -{
> - if (mGroupingProperty == "participants") {
> - QStringList participants = propertyValue.toStringList();
> - HistoryThreadGroupMap::iterator it = mGroups.begin();
> - for (; it != mGroups.end(); ++it) {
> - if (compareParticipants(it.value().participants, participants)) {
> - mGroups.erase(it);
> - break;
> - }
> - }
> +QVariant HistoryGroupedThreadsModel::get(int row) const
> +{
> + if (row >= rowCount() || row < 0) {
> + return QVariant();
> + }
> +
> + return data(index(row), ThreadsRole);
> +}
> +
> +int HistoryGroupedThreadsModel::existingPositionForEntry(const QVariant &propertyValue) const
> +{
> + int pos = -1;
> + for (int i = 0; i < mGroups.count(); ++i) {
> + const HistoryThreadGroup &group = mGroups[i];
> + if (mGroupingProperty == "participants") {
Don't we have a macro for "participants" ?
> + QStringList participants = propertyValue.toStringList();
> + if (compareParticipants(group.displayedThread.participants(), participants)) {
> + pos = i;
> + break;
> + }
> + } else if (propertyValue == group.displayedThread.properties()[mGroupingProperty]) {
> + pos = i;
> + break;
> + }
> + }
> +
> + return pos;
> +}
> +
> +void HistoryGroupedThreadsModel::removeGroup(const HistoryThreadGroup &group)
> +{
> + int pos = mGroups.indexOf(group);
> + if (pos >= 0){
> + beginRemoveRows(QModelIndex(), pos, pos);
> + mGroups.removeAt(pos);
> + endRemoveRows();
> + }
> +}
> +
> +void HistoryGroupedThreadsModel::updateDisplayedThread(HistoryThreadGroup &group)
> +{
> + int pos = mGroups.indexOf(group);
> + if (pos < 0) {
> + qWarning() << "Group not found!!";
> + return;
> + }
> +
> + History::Thread displayedThread = group.threads.first();
> + QVariantMap displayedProperties = displayedThread.properties();
> + Q_FOREACH(const History::Thread &other, group.threads) {
> + if (isAscending() ? lessThan(other.properties(), displayedProperties) :
> + lessThan(displayedProperties, other.properties())) {
> + displayedThread = other;
> + displayedProperties = displayedThread.properties();
> + }
> + }
> +
> + // check if we need to update the order
> + int newPos = positionForItem(displayedProperties);
> +
> + // NOTE: only set the new displayedThread AFTER calling positionForItem
> + group.displayedThread = displayedThread;
> +
> + // the positionForItem function might return the pos+1 value for the current item as it considers
> + // this to be the position for a new insertion
> + if (newPos != pos && newPos != pos+1) {
> + beginMoveRows(QModelIndex(), pos, pos, QModelIndex(), newPos);
> + // QList::move() behaves in a different way than the QAbstractItemModel moving functions
> + // that's why the delta was added
> + mGroups.move(pos, newPos > pos ? newPos-1 : newPos);
> + endMoveRows();
> + }
> +}
> +
> +void HistoryGroupedThreadsModel::updateQuery()
> +{
> + // remove all entries and call the query update
> + if (!mGroups.isEmpty()) {
> + beginRemoveRows(QModelIndex(), 0, rowCount() - 1);
> + mGroups.clear();
> + endRemoveRows();
> + }
> +
> + HistoryThreadModel::updateQuery();
> +}
> +
> +void HistoryGroupedThreadsModel::onThreadsAdded(const History::Threads &threads)
> +{
> + Q_FOREACH(const History::Thread &thread, threads) {
> + processThreadGrouping(thread);
> + }
> +
> + notifyDataChanged();
> +}
> +
> +void HistoryGroupedThreadsModel::onThreadsModified(const History::Threads &threads)
> +{
> + Q_FOREACH(const History::Thread &thread, threads) {
> + processThreadGrouping(thread);
> + }
> +
> + notifyDataChanged();
> +}
> +
> +void HistoryGroupedThreadsModel::onThreadsRemoved(const History::Threads &threads)
> +{
> + Q_FOREACH(const History::Thread &thread, threads) {
> + removeThreadFromGroup(thread);
> + }
> +
> + notifyDataChanged();
> +}
> +
> +void HistoryGroupedThreadsModel::processThreadGrouping(const History::Thread &thread)
> +{
> + QVariantMap properties = thread.properties();
> + int pos = existingPositionForEntry(properties[mGroupingProperty]);
> +
> + // if the group is empty, we need to insert it into the map
> + if (pos < 0) {
> + HistoryThreadGroup group;
> + int newPos = positionForItem(thread.properties());
> + group.threads.append(thread);
> + group.displayedThread = thread;
> + beginInsertRows(QModelIndex(), newPos, newPos);
> + mGroups.insert(newPos, group);
> + endInsertRows();
> + return;
> + }
> +
> + HistoryThreadGroup &group = mGroups[pos];
> + if (!group.threads.contains(thread)) {
> + group.threads.append(thread);
> } else {
> - mGroups.remove(propertyValue.toString());
> - }
> -}
> -
> -void HistoryThreadGroupingProxyModel::processGrouping()
> -{
> - HistoryThreadModel *model = qobject_cast<HistoryThreadModel*>(sourceModel());
> - if (!model) {
> - return;
> - }
> -
> - mGroups.clear();
> - int count = model->rowCount();
> - for (int row = 0; row < count; ++row) {
> - processRowGrouping(row);
> - }
> -}
> -
> -void HistoryThreadGroupingProxyModel::processRowGrouping(int sourceRow)
> -{
> - HistoryThreadModel *model = qobject_cast<HistoryThreadModel*>(sourceModel());
> - if (!model) {
> - return;
> - }
> -
> - QModelIndex sourceIndex = model->index(sourceRow, 0, QModelIndex());
> -
> - QVariantMap properties = sourceIndex.data(HistoryThreadModel::PropertiesRole).toMap();
> - HistoryThreadGroup &group = groupForEntry(properties[mGroupingProperty]);
> -
> - if (!group.rows.contains(sourceIndex)) {
> - group.rows.append(sourceIndex);
> - }
> -
> - QDateTime timestamp = sourceIndex.data(HistoryThreadModel::LastEventTimestampRole).toDateTime();
> - if (timestamp > group.latestTime || !group.displayedIndex.isValid()) {
> - QPersistentModelIndex oldDisplayedIndex = group.displayedIndex;
> - group.latestTime = timestamp;
> - group.displayedIndex = sourceIndex;
> -
> - if (oldDisplayedIndex.isValid() && oldDisplayedIndex != group.displayedIndex) {
> - markIndexAsChanged(oldDisplayedIndex);
> - }
> - markIndexAsChanged(group.displayedIndex);
> - }
> -}
> -
> -void HistoryThreadGroupingProxyModel::removeRowFromGroup(int sourceRow)
> -{
> - HistoryThreadModel *model = qobject_cast<HistoryThreadModel*>(sourceModel());
> - if (!model) {
> - return;
> - }
> -
> - QModelIndex sourceIndex = model->index(sourceRow, 0, QModelIndex());
> - QVariantMap properties = sourceIndex.data(HistoryThreadModel::PropertiesRole).toMap();
> -
> - HistoryThreadGroup &group = groupForEntry(properties[mGroupingProperty]);
> -
> - group.rows.removeAll(sourceIndex);
> - if (group.displayedIndex == sourceIndex) {
> - QDateTime latestTimestamp;
> - QPersistentModelIndex latestIndex;
> - Q_FOREACH(QPersistentModelIndex index, group.rows) {
> - QDateTime timestamp = index.data(HistoryThreadModel::LastEventTimestampRole).toDateTime();
> - if (timestamp > latestTimestamp) {
> - latestTimestamp = timestamp;
> - latestIndex = index;
> - }
> - }
> -
> - if (group.rows.isEmpty()) {
> - removeGroup(properties[mGroupingProperty]);
> + // save the updated copy of the thread
> + group.threads.removeAll(thread);
> + group.threads.append(thread);
> + }
> +
> + updateDisplayedThread(group);
> + markGroupAsChanged(group);
> +}
> +
> +void HistoryGroupedThreadsModel::removeThreadFromGroup(const History::Thread &thread)
> +{
> + QVariantMap properties = thread.properties();
> +
> + int pos = existingPositionForEntry(properties[mGroupingProperty]);
> + if (pos < 0) {
> + qWarning() << "Could not find group for property " << properties[mGroupingProperty];
> + return;
> + }
> +
> + HistoryThreadGroup &group = mGroups[pos];
> + group.threads.removeAll(thread);
> +
> + if (group.threads.isEmpty()) {
> + removeGroup(group);
> + } else if (group.displayedThread == thread) {
> + updateDisplayedThread(group);
> + markGroupAsChanged(group);
> + }
> +}
> +
> +void HistoryGroupedThreadsModel::markGroupAsChanged(const HistoryThreadGroup &group)
> +{
> + if (!mChangedGroups.contains(group)) {
> + mChangedGroups.append(group);
> + }
> +}
> +
> +void HistoryGroupedThreadsModel::notifyDataChanged()
> +{
> + Q_FOREACH(const HistoryThreadGroup &group, mChangedGroups) {
> + int pos = mGroups.indexOf(group);
> + if (pos >= 0) {
> + QModelIndex idx = index(pos);
> + Q_EMIT dataChanged(idx, idx);
> } else {
> - group.displayedIndex = latestIndex;
> - group.latestTime = latestTimestamp;
> - markIndexAsChanged(group.displayedIndex);
> - }
> - }
> -}
> -
> -void HistoryThreadGroupingProxyModel::triggerDataChanged()
> -{
> - if (mDataChangedTriggered) {
> - return;
> - }
> -
> - QTimer::singleShot(0, this, SLOT(notifyDataChanged()));
> - mDataChangedTriggered = true;
> -}
> -
> -void HistoryThreadGroupingProxyModel::markIndexAsChanged(const QModelIndex &index)
> -{
> - if (!mChangedIndexes.contains(index)) {
> - mChangedIndexes.append(index);
> - }
> -}
> -
> -void HistoryThreadGroupingProxyModel::notifyDataChanged()
> -{
> - QAbstractItemModel *model = sourceModel();
> - Q_FOREACH(const QPersistentModelIndex &index, mChangedIndexes) {
> - if (index.isValid()) {
> - Q_EMIT model->dataChanged(index, index);
> - }
> - }
> - mChangedIndexes.clear();
> - mDataChangedTriggered = false;
> -}
> -
> -void HistoryThreadGroupingProxyModel::onRowsInserted(const QModelIndex &parent, int start, int end)
> -{
> - // we don't support tree models yet
> - if (parent.isValid()) {
> - return;
> - }
> -
> -
> - // update the group for the added indexes
> - for (int row = start; row <= end; ++row) {
> - processRowGrouping(row);
> - }
> -
> - triggerDataChanged();
> -}
> -
> -void HistoryThreadGroupingProxyModel::onRowsRemoved(const QModelIndex &parent, int start, int end)
> -{
> - // we don't support tree models yet
> - if (parent.isValid()) {
> - return;
> - }
> -
> - for (int row = start; row <= end; ++row) {
> - removeRowFromGroup(row);
> - }
> -
> - triggerDataChanged();
> -}
> -
> -void HistoryThreadGroupingProxyModel::onDataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight)
> -{
> - // we don't support tree models yet
> - if (topLeft.parent().isValid() || bottomRight.parent().isValid()) {
> - return;
> - }
> -
> - int start = topLeft.row();
> - int end = bottomRight.row();
> -
> - for (int row = start; row <= end; ++row) {
> - processRowGrouping(row);
> - }
> - triggerDataChanged();
> -}
> -
> -void HistoryThreadGroupingProxyModel::onSourceModelChanged()
> -{
> - QAbstractItemModel *model = sourceModel();
> - if (model) {
> -
> - connect(model,
> - SIGNAL(rowsInserted(QModelIndex,int,int)),
> - SLOT(onRowsInserted(QModelIndex,int,int)));
> - connect(model,
> - SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)),
> - SLOT(onRowsRemoved(QModelIndex,int,int)));
> - connect(model,
> - SIGNAL(modelReset()),
> - SLOT(processGrouping()));
> - connect(model,
> - SIGNAL(dataChanged(QModelIndex,QModelIndex)),
> - SLOT(onDataChanged(QModelIndex,QModelIndex)));
> - Q_EMIT threadModelChanged();
> - }
> -
> - mRoles = SortProxyModel::roleNames();
> - mRoles[ThreadsRole] = "threads";
> -
> - processGrouping();
> - triggerDataChanged();
> -}
> -
> -bool HistoryThreadGroupingProxyModel::compareParticipants(const QStringList &list1, const QStringList &list2) const
> -{
> - // FIXME: add support for match flags
> - if (list1.count() != list2.count()) {
> - return false;
> - }
> -
> - int found = 0;
> - Q_FOREACH(const QString &participant, list1) {
> - Q_FOREACH(const QString &item, list2) {
> - if (PhoneUtils::comparePhoneNumbers(participant, item)) {
> - found++;
> - break;
> - }
> - }
> - }
> -
> - return found == list1.count();
> -}
> -
> -
> -HistoryThreadGroup HistoryThreadGroupingProxyModel::groupForSourceIndex(const QModelIndex &sourceIndex) const
> -{
> - QVariantMap properties = sourceIndex.data(HistoryThreadModel::PropertiesRole).toMap();
> - return groupForEntry(properties[mGroupingProperty]);
> -}
> -
> -
> -QString HistoryThreadGroupingProxyModel::groupingProperty() const
> + qWarning() << "Group not found!";
> + }
> + }
> + mChangedGroups.clear();
> +}
> +
> +QString HistoryGroupedThreadsModel::groupingProperty() const
> {
> return mGroupingProperty;
> }
>
> -void HistoryThreadGroupingProxyModel::setGroupingProperty(const QString &value)
> +void HistoryGroupedThreadsModel::setGroupingProperty(const QString &value)
> {
> mGroupingProperty = value;
> Q_EMIT groupingPropertyChanged();
> - processGrouping();
> - triggerDataChanged();
> +
> + triggerQueryUpdate();
> +}
> +
> +int HistoryGroupedThreadsModel::rowCount(const QModelIndex &parent) const
> +{
> + if (parent.isValid()) {
> + return 0;
> + }
> +
> + return mGroups.count();
> +}
> +
> +
> +bool HistoryThreadGroup::operator==(const HistoryThreadGroup &other) const
> +{
> + return displayedThread == other.displayedThread;
> }
>
> === renamed file 'Ubuntu/History/historythreadgroupingproxymodel.h' => 'Ubuntu/History/historygroupedthreadsmodel.h'
> --- Ubuntu/History/historythreadgroupingproxymodel.h 2014-07-22 18:02:40 +0000
> +++ Ubuntu/History/historygroupedthreadsmodel.h 2014-09-09 23:16:29 +0000
> @@ -19,25 +19,23 @@
> * along with this program. If not, see <http://www.gnu.org/licenses/>.
> */
>
> -#ifndef HISTORYTHREADGROUPINGPROXYMODEL_H
> -#define HISTORYTHREADGROUPINGPROXYMODEL_H
> +#ifndef HISTORYGROUPEDTHREADSMODEL_H
> +#define HISTORYGROUPEDTHREADSMODEL_H
>
> -#include "sortproxymodel.h"
> +#include "historythreadmodel.h"
> #include <QDateTime>
>
> -class HistoryThreadModel;
> -
> class HistoryThreadGroup {
> public:
> - QStringList participants;
> - QDateTime latestTime;
> - QPersistentModelIndex displayedIndex;
> - QList<QPersistentModelIndex> rows;
> + History::Thread displayedThread;
> + History::Threads threads;
> +
> + bool operator==(const HistoryThreadGroup &other) const;
> };
>
> -typedef QMap<QString, HistoryThreadGroup> HistoryThreadGroupMap;
> +typedef QList<HistoryThreadGroup> HistoryThreadGroupList;
>
> -class HistoryThreadGroupingProxyModel : public SortProxyModel
> +class HistoryGroupedThreadsModel : public HistoryThreadModel
> {
> Q_OBJECT
> Q_PROPERTY(QString groupingProperty
> @@ -48,50 +46,46 @@
>
> public:
> enum CustomRoles {
> - ThreadsRole = (Qt::UserRole + 100),
> + ThreadsRole = LastThreadRole
> };
>
> - explicit HistoryThreadGroupingProxyModel(QObject *parent = 0);
> + explicit HistoryGroupedThreadsModel(QObject *parent = 0);
>
> QString groupingProperty() const;
> void setGroupingProperty(const QString &value);
>
> + virtual int rowCount(const QModelIndex &parent = QModelIndex()) const;
> virtual QVariant data(const QModelIndex &index, int role) const;
> + Q_INVOKABLE void fetchMore(const QModelIndex &parent = QModelIndex());
> virtual QHash<int, QByteArray> roleNames() const;
> + Q_INVOKABLE QVariant get(int row) const;
>
> Q_SIGNALS:
> - void threadModelChanged();
> void groupingPropertyChanged();
>
> protected:
> - bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const;
> - HistoryThreadGroup groupForSourceIndex(const QModelIndex &sourceIndex) const;
> - HistoryThreadGroup &groupForEntry(const QVariant &propertyValue) const;
> - void removeGroup(const QVariant &propertyValue);
> + int existingPositionForEntry(const QVariant &propertyValue) const;
> + void removeGroup(const HistoryThreadGroup &group);
> + void updateDisplayedThread(HistoryThreadGroup &group);
> +
> +protected Q_SLOTS:
> + virtual void updateQuery();
> + virtual void onThreadsAdded(const History::Threads &threads);
> + virtual void onThreadsModified(const History::Threads &threads);
> + virtual void onThreadsRemoved(const History::Threads &threads);
>
> private Q_SLOTS:
> - void processGrouping();
> - void processRowGrouping(int sourceRow);
> - void removeRowFromGroup(int sourceRow);
> - void triggerDataChanged();
> - void markIndexAsChanged(const QModelIndex &index);
> + void processThreadGrouping(const History::Thread &thread);
> + void removeThreadFromGroup(const History::Thread &thread);
> + void markGroupAsChanged(const HistoryThreadGroup &group);
> void notifyDataChanged();
>
> - void onRowsInserted(const QModelIndex &parent, int start, int end);
> - void onRowsRemoved(const QModelIndex &parent, int start, int end);
> - void onDataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight);
> - void onSourceModelChanged();
> -
> -protected:
> - bool compareParticipants(const QStringList &list1, const QStringList &list2) const;
> -
> private:
> - bool mDataChangedTriggered;
> QString mGroupingProperty;
>
> - mutable HistoryThreadGroupMap mGroups;
> - QList<QPersistentModelIndex> mChangedIndexes;
> + HistoryThreadGroupList mGroups;
> + QList<HistoryThreadGroup> mChangedGroups;
> QHash<int, QByteArray> mRoles;
> };
>
> -#endif // HISTORYTHREADGROUPINGPROXYMODEL_H
> +#endif // HISTORYGROUPEDTHREADSMODEL_H
>
> === added file 'Ubuntu/History/historymodel.cpp'
> --- Ubuntu/History/historymodel.cpp 1970-01-01 00:00:00 +0000
> +++ Ubuntu/History/historymodel.cpp 2014-09-09 23:16:29 +0000
> @@ -0,0 +1,311 @@
> +/*
> + * Copyright (C) 2013-2014 Canonical, Ltd.
> + *
> + * Authors:
> + * Gustavo Pichorim Boiko <gustavo.boiko at canonical.com>
> + *
> + * This file is part of history-service.
> + *
> + * history-service is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; version 3.
> + *
> + * history-service is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program. If not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +#include "historymodel.h"
> +#include "historyqmlfilter.h"
> +#include "historyqmlsort.h"
> +#include "contactmatcher_p.h"
> +#include "phoneutils_p.h"
> +#include "thread.h"
> +#include "manager.h"
> +#include <QTimerEvent>
> +#include <QDebug>
> +
> +HistoryModel::HistoryModel(QObject *parent) :
> + QAbstractListModel(parent), mFilter(0), mSort(new HistoryQmlSort(this)),
> + mType(EventTypeText), mMatchContacts(false), mUpdateTimer(0)
> +{
> + // configure the roles
> + mRoles[AccountIdRole] = "accountId";
> + mRoles[ThreadIdRole] = "threadId";
> + mRoles[ParticipantsRole] = "participants";
> + mRoles[TypeRole] = "type";
> + mRoles[PropertiesRole] = "properties";
> +
> + connect(this, SIGNAL(rowsInserted(QModelIndex,int,int)), this, SIGNAL(countChanged()));
> + connect(this, SIGNAL(rowsRemoved(QModelIndex,int,int)), this, SIGNAL(countChanged()));
> + connect(this, SIGNAL(modelReset()), this, SIGNAL(countChanged()));
> + connect(ContactMatcher::instance(),
> + SIGNAL(contactInfoChanged(QString,QVariantMap)),
> + SLOT(onContactInfoChanged(QString,QVariantMap)));
> +
> + // create the view and get some objects
> + triggerQueryUpdate();
> +}
> +
> +bool HistoryModel::canFetchMore(const QModelIndex &parent) const
> +{
> + return false;
> +}
> +
> +void HistoryModel::fetchMore(const QModelIndex &parent)
> +{
> + Q_UNUSED(parent)
> + // do nothing, just make the method invokable
> +}
> +
> +QHash<int, QByteArray> HistoryModel::roleNames() const
> +{
> + return mRoles;
> +}
> +
> +QVariant HistoryModel::data(const QModelIndex &index, int role) const
> +{
> + if (!index.isValid() || index.row() < 0 || index.row() >= rowCount()) {
> + return QVariant();
> + }
> +
> + QVariantMap properties = index.data(PropertiesRole).toMap();
> + QVariant result;
> + switch (role) {
> + case AccountIdRole:
> + result = properties[History::FieldAccountId];
> + break;
> + case ThreadIdRole:
> + result = properties[History::FieldThreadId];
> + break;
> + case TypeRole:
> + result = properties[History::FieldType];
> + break;
> + case ParticipantsRole:
> + if (mMatchContacts) {
> + result = ContactMatcher::instance()->contactInfo(properties[History::FieldParticipants].toStringList());
> + } else {
> + result = properties[History::FieldParticipants];
> + }
> + break;
> + }
> + return result;
> +}
> +
> +HistoryQmlFilter *HistoryModel::filter() const
> +{
> + return mFilter;
> +}
> +
> +void HistoryModel::setFilter(HistoryQmlFilter *value)
> +{
> + if (mFilter) {
> + mFilter->disconnect(this);
> + }
> +
> + mFilter = value;
> + if (mFilter) {
> + connect(mFilter,
> + SIGNAL(filterChanged()),
> + SLOT(triggerQueryUpdate()));
> + }
> +
> + Q_EMIT filterChanged();
> + triggerQueryUpdate();
> +}
> +
> +HistoryQmlSort *HistoryModel::sort() const
> +{
> + return mSort;
> +}
> +
> +void HistoryModel::setSort(HistoryQmlSort *value)
> +{
> + // disconnect the previous sort
> + if (mSort) {
> + mSort->disconnect(this);
> + }
> +
> + mSort = value;
> + if (mSort) {
> + connect(mSort,
> + SIGNAL(sortChanged()),
> + SLOT(triggerQueryUpdate()));
> + }
> +
> + Q_EMIT sortChanged();
> + triggerQueryUpdate();
> +}
> +
> +HistoryModel::EventType HistoryModel::type() const
> +{
> + return mType;
> +}
> +
> +void HistoryModel::setType(EventType value)
> +{
> + mType = value;
> + Q_EMIT typeChanged();
> + triggerQueryUpdate();
> +}
> +
> +bool HistoryModel::matchContacts() const
> +{
> + return mMatchContacts;
> +}
> +
> +void HistoryModel::setMatchContacts(bool value)
> +{
> + mMatchContacts = value;
> + Q_EMIT matchContactsChanged();
> +
> + // mark all indexes as changed
> + if (rowCount() > 0) {
> + Q_EMIT dataChanged(index(0), index(rowCount()-1));
> + }
> +}
> +
> +QString HistoryModel::threadIdForParticipants(const QString &accountId, int eventType, const QStringList &participants, int matchFlags, bool create)
> +{
> + if (participants.isEmpty()) {
> + return QString::null;
> + }
> +
> + History::Thread thread = History::Manager::instance()->threadForParticipants(accountId,
> + (History::EventType)eventType,
> + participants,
> + (History::MatchFlags)matchFlags,
> + create);
> + if (!thread.isNull()) {
> + return thread.threadId();
> + }
> +
> + return QString::null;
> +}
> +
> +void HistoryModel::onContactInfoChanged(const QString &phoneNumber, const QVariantMap &contactInfo)
> +{
> + Q_UNUSED(contactInfo)
> + if (!mMatchContacts) {
> + return;
> + }
> +
> + QList<QModelIndex> changedIndexes;
> + int count = rowCount();
> + for (int i = 0; i < count; ++i) {
> + // WARNING: do not use mEvents directly to verify which indexes to change as there is the
> + // HistoryGroupedEventsModel which is based on this model and handles the items in a different way
> + QModelIndex idx = index(i);
> + QVariantMap properties = idx.data(PropertiesRole).toMap();
> + QStringList participants = properties[History::FieldParticipants].toStringList();
> + Q_FOREACH(const QString &participant, participants) {
> + if (PhoneUtils::comparePhoneNumbers(participant, phoneNumber)) {
> + changedIndexes << idx;
> + }
> + }
> + }
> +
> + // now emit the dataChanged signal to all changed indexes
> + Q_FOREACH(const QModelIndex &idx, changedIndexes) {
> + Q_EMIT dataChanged(idx, idx);
> + }
> +}
> +
> +void HistoryModel::timerEvent(QTimerEvent *event)
> +{
> + if (event->timerId() == mUpdateTimer) {
> + killTimer(mUpdateTimer);
> + mUpdateTimer = 0;
> + updateQuery();
> + }
> +}
> +
> +
> +bool HistoryModel::compareParticipants(const QStringList &list1, const QStringList &list2) const
> +{
> + if (list1.count() != list2.count()) {
> + return false;
> + }
> +
> + int found = 0;
> + Q_FOREACH(const QString &participant, list1) {
> + Q_FOREACH(const QString &item, list2) {
> + if (PhoneUtils::comparePhoneNumbers(participant, item)) {
> + found++;
> + break;
> + }
> + }
> + }
> +
> + return found == list1.count();
> +}
> +
> +bool HistoryModel::lessThan(const QVariantMap &left, const QVariantMap &right) const
> +{
> + QVariant leftValue = left[sort()->sortField()];
> + QVariant rightValue = right[sort()->sortField()];
> +
> + return leftValue < rightValue;
> +}
> +
> +int HistoryModel::positionForItem(const QVariantMap &item) const
> +{
> + // do a binary search for the item position on the list
> + int lowerBound = 0;
> + int upperBound = rowCount() - 1;
> + if (upperBound < 0) {
> + return 0;
> + }
> +
> + while (true) {
> + int pos = (upperBound + lowerBound) / 2;
> + const QVariantMap posItem = index(pos).data(PropertiesRole).toMap();
> + if (lowerBound == pos) {
> + if (isAscending() ? lessThan(item, posItem) : lessThan(posItem, item)) {
> + return pos;
> + }
> + }
> + if (isAscending() ? lessThan(posItem, item) : lessThan(item, posItem)) {
> + lowerBound = pos + 1; // its in the upper
> + if (lowerBound > upperBound) {
> + return pos += 1;
> + }
> + } else if (lowerBound > upperBound) {
> + return pos;
> + } else {
> + upperBound = pos - 1; // its in the lower
> + }
> + }
> +}
> +
> +bool HistoryModel::isAscending() const
> +{
> + return mSort && mSort->sort().sortOrder() == Qt::AscendingOrder;
> +}
> +
> +QVariant HistoryModel::get(int row) const
> +{
> + QVariantMap data;
> + QModelIndex idx = index(row, 0);
> + if (idx.isValid()) {
> + QHash<int, QByteArray> roles = roleNames();
> + Q_FOREACH(int role, roles.keys()) {
> + data.insert(roles[role], idx.data(role));
> + }
> + }
> +
> + return data;
> +}
> +
> +void HistoryModel::triggerQueryUpdate()
> +{
> + if (mUpdateTimer) {
> + killTimer(mUpdateTimer);
> + }
> + // delay the loading of the model data until the settings settle down
> + mUpdateTimer = startTimer(100);
> +}
>
> === added file 'Ubuntu/History/historymodel.h'
> --- Ubuntu/History/historymodel.h 1970-01-01 00:00:00 +0000
> +++ Ubuntu/History/historymodel.h 2014-09-09 23:16:29 +0000
> @@ -0,0 +1,136 @@
> +/*
> + * Copyright (C) 2013-2014 Canonical, Ltd.
> + *
> + * Authors:
> + * Gustavo Pichorim Boiko <gustavo.boiko at canonical.com>
> + *
> + * This file is part of history-service.
> + *
> + * history-service is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; version 3.
> + *
> + * history-service is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program. If not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +#ifndef HISTORYMODEL_H
> +#define HISTORYMODEL_H
> +
> +#include "types.h"
> +#include "historyqmlfilter.h"
> +#include "historyqmlsort.h"
> +#include <QAbstractListModel>
> +#include <QStringList>
> +
> +class HistoryModel : public QAbstractListModel
> +{
> + Q_OBJECT
> + Q_PROPERTY(int count READ rowCount NOTIFY countChanged)
> + Q_PROPERTY(HistoryQmlFilter *filter READ filter WRITE setFilter NOTIFY filterChanged)
> + Q_PROPERTY(HistoryQmlSort *sort READ sort WRITE setSort NOTIFY sortChanged)
> + Q_PROPERTY(EventType type READ type WRITE setType NOTIFY typeChanged)
> + Q_PROPERTY(bool matchContacts READ matchContacts WRITE setMatchContacts NOTIFY matchContactsChanged)
> + Q_PROPERTY(bool canFetchMore READ canFetchMore NOTIFY canFetchMoreChanged)
> + Q_ENUMS(EventType)
> + Q_ENUMS(MatchFlag)
> + Q_ENUMS(MessageStatus)
> + Q_ENUMS(Role)
> +
> +public:
> + enum EventType {
> + EventTypeText = History::EventTypeText,
> + EventTypeVoice = History::EventTypeVoice
> + };
> +
> + enum MatchFlag {
> + MatchCaseSensitive = History::MatchCaseSensitive,
> + MatchCaseInsensitive = History::MatchCaseInsensitive,
> + MatchContains = History::MatchContains,
> + MatchPhoneNumber = History::MatchPhoneNumber
> + };
> +
> + enum MessageStatus
> + {
> + MessageStatusUnknown = History::MessageStatusUnknown,
> + MessageStatusDelivered = History::MessageStatusDelivered,
> + MessageStatusTemporarilyFailed = History::MessageStatusTemporarilyFailed,
> + MessageStatusPermanentlyFailed = History::MessageStatusPermanentlyFailed,
> + MessageStatusAccepted = History::MessageStatusAccepted,
> + MessageStatusRead = History::MessageStatusRead,
> + MessageStatusDeleted = History::MessageStatusDeleted,
> + MessageStatusPending = History::MessageStatusPending // pending attachment download
> + };
> +
> + enum Role {
> + AccountIdRole = Qt::UserRole,
> + ThreadIdRole,
> + ParticipantsRole,
> + TypeRole,
> + PropertiesRole,
> + LastRole
> + };
> +
> + explicit HistoryModel(QObject *parent = 0);
> +
> + Q_INVOKABLE virtual bool canFetchMore(const QModelIndex &parent = QModelIndex()) const;
> + Q_INVOKABLE virtual void fetchMore(const QModelIndex &parent = QModelIndex());
> + virtual QHash<int, QByteArray> roleNames() const;
> + virtual QVariant data(const QModelIndex &index, int role) const;
> +
> + HistoryQmlFilter *filter() const;
> + void setFilter(HistoryQmlFilter *value);
> +
> + HistoryQmlSort *sort() const;
> + void setSort(HistoryQmlSort *value);
> +
> + EventType type() const;
> + void setType(EventType value);
> +
> + bool matchContacts() const;
> + void setMatchContacts(bool value);
> +
> + Q_INVOKABLE QString threadIdForParticipants(const QString &accountId,
> + int eventType,
> + const QStringList &participants,
> + int matchFlags = (int)History::MatchCaseSensitive,
> + bool create = false);
> +
> + Q_INVOKABLE virtual QVariant get(int row) const;
> +
> +Q_SIGNALS:
> + void countChanged();
> + void filterChanged();
> + void sortChanged();
> + void typeChanged();
> + void matchContactsChanged();
> + void canFetchMoreChanged();
> +
> +protected Q_SLOTS:
> + void triggerQueryUpdate();
> + virtual void updateQuery() = 0;
> + void onContactInfoChanged(const QString &phoneNumber, const QVariantMap &contactInfo);
> +
> +protected:
> + virtual void timerEvent(QTimerEvent *event);
> + bool compareParticipants(const QStringList &list1, const QStringList &list2) const;
> + bool lessThan(const QVariantMap &left, const QVariantMap &right) const;
> + int positionForItem(const QVariantMap &item) const;
> + bool isAscending() const;
> +
> + HistoryQmlFilter *mFilter;
> + HistoryQmlSort *mSort;
> + EventType mType;
> + bool mMatchContacts;
> +
> +private:
> + QHash<int, QByteArray> mRoles;
> + int mUpdateTimer;
> +};
> +
> +#endif // HISTORYMODEL_H
>
> === modified file 'Ubuntu/History/historyqmlplugin.cpp'
> --- Ubuntu/History/historyqmlplugin.cpp 2014-08-12 13:54:32 +0000
> +++ Ubuntu/History/historyqmlplugin.cpp 2014-09-09 23:16:29 +0000
> @@ -25,11 +25,10 @@
> #include "historyqmlsort.h"
> #include "historyqmlunionfilter.h"
> #include "historythreadmodel.h"
> -#include "historythreadgroupingproxymodel.h"
> +#include "historygroupedthreadsmodel.h"
> #include "historyeventmodel.h"
> #include "historygroupedeventsmodel.h"
> #include "historyqmltexteventattachment.h"
> -#include "sortproxymodel.h"
> #include <QQmlEngine>
> #include <qqml.h>
>
> @@ -46,12 +45,11 @@
> qmlRegisterType<HistoryEventModel>(uri, 0, 1, "HistoryEventModel");
> qmlRegisterType<HistoryGroupedEventsModel>(uri, 0, 1, "HistoryGroupedEventsModel");
> qmlRegisterType<HistoryThreadModel>(uri, 0, 1, "HistoryThreadModel");
> + qmlRegisterType<HistoryGroupedThreadsModel>(uri, 0, 1, "HistoryGroupedThreadsModel");
> qmlRegisterType<HistoryQmlFilter>(uri, 0, 1, "HistoryFilter");
> qmlRegisterType<HistoryQmlIntersectionFilter>(uri, 0, 1, "HistoryIntersectionFilter");
> qmlRegisterType<HistoryQmlSort>(uri, 0, 1, "HistorySort");
> qmlRegisterType<HistoryQmlUnionFilter>(uri, 0, 1, "HistoryUnionFilter");
> - qmlRegisterType<SortProxyModel>(uri, 0, 1, "SortProxyModel");
> - qmlRegisterType<HistoryThreadGroupingProxyModel>(uri, 0, 1, "HistoryThreadGroupingProxyModel");
> qmlRegisterUncreatableType<HistoryQmlTextEventAttachment>(uri, 0, 1, "HistoryTextEventAttachment", "");
> qmlRegisterUncreatableType<QAbstractItemModel>(uri, 0, 1, "QAbstractItemModel", "");
> }
>
> === modified file 'Ubuntu/History/historythreadmodel.cpp'
> --- Ubuntu/History/historythreadmodel.cpp 2014-09-09 23:16:29 +0000
> +++ Ubuntu/History/historythreadmodel.cpp 2014-09-09 23:16:29 +0000
> @@ -20,34 +20,20 @@
> */
>
> #include "historythreadmodel.h"
> -#include "thread.h"
> -#include "historyqmlfilter.h"
> -#include "historyqmlsort.h"
> +#include "historyqmltexteventattachment.h"
> #include "manager.h"
> #include "threadview.h"
> -#include "textevent.h"
> -#include "texteventattachment.h"
> -#include "historyqmltexteventattachment.h"
> #include "voiceevent.h"
> -#include "contactmatcher_p.h"
> -#include "phoneutils_p.h"
> -#include <QDebug>
> -#include <QTimerEvent>
>
> Q_DECLARE_METATYPE(History::TextEventAttachments)
>
> HistoryThreadModel::HistoryThreadModel(QObject *parent) :
> - QAbstractListModel(parent), mCanFetchMore(true), mFilter(0), mSort(0),
> - mType(EventTypeText), mMatchContacts(false), mUpdateTimer(0)
> + HistoryModel(parent), mCanFetchMore(true)
> {
> // configure the roles
> - mRoles[AccountIdRole] = "accountId";
> - mRoles[ThreadIdRole] = "threadId";
> - mRoles[TypeRole] = "type";
> - mRoles[ParticipantsRole] = "participants";
> + mRoles = HistoryModel::roleNames();
> mRoles[CountRole] = "count";
> mRoles[UnreadCountRole] = "unreadCount";
> - mRoles[PropertiesRole] = "properties";
>
> // roles related to the thread´s last event
> mRoles[LastEventIdRole] = "eventId";
> @@ -63,16 +49,6 @@
> mRoles[LastEventTextSubjectRole] = "eventTextSubject";
> mRoles[LastEventCallMissedRole] = "eventCallMissed";
> mRoles[LastEventCallDurationRole] = "eventCallDuration";
> -
> - connect(this, SIGNAL(rowsInserted(QModelIndex,int,int)), SIGNAL(countChanged()));
> - connect(this, SIGNAL(rowsRemoved(QModelIndex,int,int)), SIGNAL(countChanged()));
> - connect(this, SIGNAL(modelReset()), SIGNAL(countChanged()));
> - connect(ContactMatcher::instance(),
> - SIGNAL(contactInfoChanged(QString,QVariantMap)),
> - SLOT(onContactInfoChanged(QString,QVariantMap)));
> -
> - // create the results view
> - triggerQueryUpdate();
> }
>
> int HistoryThreadModel::rowCount(const QModelIndex &parent) const
> @@ -91,6 +67,16 @@
> }
>
> History::Thread thread = mThreads[index.row()];
> + QVariant result = threadData(thread, role);
> + if (result.isNull()) {
> + result = HistoryModel::data(index, role);
> + }
> +
> + return result;
> +}
> +
> +QVariant HistoryThreadModel::threadData(const History::Thread &thread, int role) const
> +{
> History::Event event = thread.lastEvent();
> History::TextEvent textEvent;
> History::VoiceEvent voiceEvent;
> @@ -108,22 +94,6 @@
>
> QVariant result;
> switch (role) {
> - case AccountIdRole:
> - result = thread.accountId();
> - break;
> - case ThreadIdRole:
> - result = thread.threadId();
> - break;
> - case TypeRole:
> - result = (int) thread.type();
> - break;
> - case ParticipantsRole:
> - if (mMatchContacts) {
> - result = ContactMatcher::instance()->contactInfo(thread.participants());
> - } else {
> - result = thread.participants();
> - }
> - break;
> case CountRole:
> result = thread.count();
> break;
> @@ -214,7 +184,7 @@
>
> bool HistoryThreadModel::canFetchMore(const QModelIndex &parent) const
> {
> - if (parent.isValid() || !mFilter) {
> + if (parent.isValid() || !mFilter || mThreadView.isNull()) {
> return false;
> }
>
> @@ -227,7 +197,7 @@
> return;
> }
>
> - History::Threads threads = mThreadView->nextPage();
> + History::Threads threads = fetchNextPage();
> if (threads.isEmpty()) {
> mCanFetchMore = false;
> Q_EMIT canFetchMoreChanged();
> @@ -243,121 +213,12 @@
> return mRoles;
> }
>
> -HistoryQmlFilter *HistoryThreadModel::filter() const
> -{
> - return mFilter;
> -}
> -
> -void HistoryThreadModel::setFilter(HistoryQmlFilter *value)
> -{
> - // disconnect the previous filter
> - if (mFilter) {
> - mFilter->disconnect(this);
> - }
> -
> - mFilter = value;
> - if (mFilter) {
> - connect(mFilter,
> - SIGNAL(filterChanged()),
> - SLOT(triggerQueryUpdate()));
> - }
> -
> - Q_EMIT filterChanged();
> - triggerQueryUpdate();
> -}
> -
> -HistoryQmlSort *HistoryThreadModel::sort() const
> -{
> - return mSort;
> -}
> -
> -void HistoryThreadModel::setSort(HistoryQmlSort *value)
> -{
> - // disconnect the previous sort
> - if (mSort) {
> - mSort->disconnect(this);
> - }
> -
> - mSort = value;
> - if (mSort) {
> - connect(mSort,
> - SIGNAL(sortChanged()),
> - SLOT(triggerQueryUpdate()));
> - }
> -
> - Q_EMIT sortChanged();
> - triggerQueryUpdate();
> -}
> -
> -HistoryThreadModel::EventType HistoryThreadModel::type() const
> -{
> - return mType;
> -}
> -
> -void HistoryThreadModel::setType(HistoryThreadModel::EventType value)
> -{
> - mType = value;
> - Q_EMIT typeChanged();
> - triggerQueryUpdate();
> -}
> -
> -bool HistoryThreadModel::matchContacts() const
> -{
> - return mMatchContacts;
> -}
> -
> -void HistoryThreadModel::setMatchContacts(bool value)
> -{
> - mMatchContacts = value;
> - Q_EMIT matchContactsChanged();
> -
> - // mark all indexes as changed
> - if (rowCount() > 0) {
> - Q_EMIT dataChanged(index(0), index(rowCount()-1));
> - }
> -}
> -
> -QString HistoryThreadModel::threadIdForParticipants(const QString &accountId, int eventType, const QStringList &participants, int matchFlags, bool create)
> -{
> - if (participants.isEmpty()) {
> - return QString::null;
> - }
> -
> - History::Thread thread = History::Manager::instance()->threadForParticipants(accountId,
> - (History::EventType)eventType,
> - participants,
> - (History::MatchFlags)matchFlags,
> - create);
> - if (!thread.isNull()) {
> - return thread.threadId();
> - }
> -
> - return QString::null;
> -}
> -
> bool HistoryThreadModel::removeThread(const QString &accountId, const QString &threadId, int eventType)
> {
> History::Thread thread = History::Manager::instance()->getSingleThread((History::EventType)eventType, accountId, threadId);
> return History::Manager::instance()->removeThreads(History::Threads() << thread);
> }
>
> -QVariant HistoryThreadModel::get(int row) const
> -{
> - if (row < 0 || row >= mThreads.count()) {
> - return QVariant();
> - }
> -
> - return mThreads[row].properties();
> -}
> -
> -void HistoryThreadModel::triggerQueryUpdate()
> -{
> - if (mUpdateTimer) {
> - killTimer(mUpdateTimer);
> - }
> - mUpdateTimer = startTimer(100);
> -}
> -
> void HistoryThreadModel::updateQuery()
> {
> // remove all events from the model
> @@ -419,25 +280,37 @@
> return;
> }
>
> - // FIXME: handle sorting
> - beginInsertRows(QModelIndex(), mThreads.count(), mThreads.count() + threads.count() - 1);
> - mThreads << threads;
> - endInsertRows();
> + Q_FOREACH(const History::Thread &thread, threads) {
> + // if the thread is already inserted, skip it
> + if (mThreads.contains(thread)) {
> + continue;
> + }
> +
> + int pos = positionForItem(thread.properties());
> + beginInsertRows(QModelIndex(), pos, pos);
> + mThreads.insert(pos, thread);
> + endInsertRows();
> + }
> }
>
> void HistoryThreadModel::onThreadsModified(const History::Threads &threads)
> {
> + History::Threads newThreads;
> Q_FOREACH(const History::Thread &thread, threads) {
> int pos = mThreads.indexOf(thread);
> if (pos >= 0) {
> mThreads[pos] = thread;
> QModelIndex idx = index(pos);
> Q_EMIT dataChanged(idx, idx);
> + } else {
> + newThreads << thread;
> }
> }
>
> - // FIXME: append modified threads that are not loaded yet and make sure they don´t
> - // get added twice to the model
> + // add threads that were not yet on the model
> + if (!newThreads.isEmpty()) {
> + onThreadsAdded(newThreads);
> + }
> }
>
> void HistoryThreadModel::onThreadsRemoved(const History::Threads &threads)
> @@ -456,40 +329,7 @@
> // should be handle internally in History::ThreadView?
> }
>
> -void HistoryThreadModel::timerEvent(QTimerEvent *event)
> -{
> - if (event->timerId() == mUpdateTimer) {
> - killTimer(mUpdateTimer);
> - mUpdateTimer = 0;
> - updateQuery();
> - }
> -}
> -
> -
> -void HistoryThreadModel::onContactInfoChanged(const QString &phoneNumber, const QVariantMap &contactInfo)
> -{
> - Q_UNUSED(contactInfo)
> - if (!mMatchContacts) {
> - return;
> - }
> -
> - QList<QModelIndex> changedIndexes;
> - int count = rowCount();
> - for (int i = 0; i < count; ++i) {
> - // WARNING: do not use mEvents directly to verify which indexes to change as there is the
> - // HistoryGroupedEventsModel which is based on this model and handles the items in a different way
> - QModelIndex idx = index(i);
> - QVariantMap eventProperties = idx.data(PropertiesRole).toMap();
> - QStringList participants = eventProperties[History::FieldParticipants].toStringList();
> - Q_FOREACH(const QString &participant, participants) {
> - if (PhoneUtils::comparePhoneNumbers(participant, phoneNumber)) {
> - changedIndexes << idx;
> - }
> - }
> - }
> -
> - // now emit the dataChanged signal to all changed indexes
> - Q_FOREACH(const QModelIndex &idx, changedIndexes) {
> - Q_EMIT dataChanged(idx, idx);
> - }
> +History::Threads HistoryThreadModel::fetchNextPage()
> +{
> + return mThreadView->nextPage();
> }
>
> === modified file 'Ubuntu/History/historythreadmodel.h'
> --- Ubuntu/History/historythreadmodel.h 2014-09-09 23:16:29 +0000
> +++ Ubuntu/History/historythreadmodel.h 2014-09-09 23:16:29 +0000
> @@ -22,7 +22,7 @@
> #ifndef HISTORYTHREADMODEL_H
> #define HISTORYTHREADMODEL_H
>
> -#include <QAbstractListModel>
> +#include "historymodel.h"
> #include "types.h"
> #include "textevent.h"
> #include "thread.h"
> @@ -30,52 +30,16 @@
> class HistoryQmlFilter;
> class HistoryQmlSort;
>
> -class HistoryThreadModel : public QAbstractListModel
> +class HistoryThreadModel : public HistoryModel
> {
> Q_OBJECT
> - Q_PROPERTY(HistoryQmlFilter *filter READ filter WRITE setFilter NOTIFY filterChanged)
> - Q_PROPERTY(HistoryQmlSort *sort READ sort WRITE setSort NOTIFY sortChanged)
> - Q_PROPERTY(EventType type READ type WRITE setType NOTIFY typeChanged)
> - Q_PROPERTY(bool matchContacts READ matchContacts WRITE setMatchContacts NOTIFY matchContactsChanged)
> - Q_PROPERTY(int count READ rowCount NOTIFY countChanged)
> - Q_PROPERTY(bool canFetchMore READ canFetchMore NOTIFY canFetchMoreChanged)
> - Q_ENUMS(EventType)
> - Q_ENUMS(Role)
> - Q_ENUMS(MatchFlag)
> - Q_ENUMS(MessageStatus)
> + Q_ENUMS(ThreadRole)
> +
> public:
> - enum EventType {
> - EventTypeText = History::EventTypeText,
> - EventTypeVoice = History::EventTypeVoice
> - };
> -
> - enum MatchFlag {
> - MatchCaseSensitive = History::MatchCaseSensitive,
> - MatchCaseInsensitive = History::MatchCaseInsensitive,
> - MatchContains = History::MatchContains,
> - MatchPhoneNumber = History::MatchPhoneNumber
> - };
> -
> - enum MessageStatus
> - {
> - MessageStatusUnknown = History::MessageStatusUnknown,
> - MessageStatusDelivered = History::MessageStatusDelivered,
> - MessageStatusTemporarilyFailed = History::MessageStatusTemporarilyFailed,
> - MessageStatusPermanentlyFailed = History::MessageStatusPermanentlyFailed,
> - MessageStatusAccepted = History::MessageStatusAccepted,
> - MessageStatusRead = History::MessageStatusRead,
> - MessageStatusDeleted = History::MessageStatusDeleted,
> - MessageStatusPending = History::MessageStatusPending // pending attachment download
> - };
> -
> - enum Role {
> - AccountIdRole = Qt::UserRole,
> - ThreadIdRole,
> - TypeRole,
> - ParticipantsRole,
> - CountRole,
> +
> + enum ThreadRole {
> + CountRole = HistoryModel::LastRole,
> UnreadCountRole,
> - PropertiesRole,
> LastEventIdRole,
> LastEventSenderIdRole,
> LastEventTimestampRole,
> @@ -88,69 +52,38 @@
> LastEventTextSubjectRole,
> LastEventTextAttachmentsRole,
> LastEventCallMissedRole,
> - LastEventCallDurationRole
> + LastEventCallDurationRole,
> + LastThreadRole
> };
>
> explicit HistoryThreadModel(QObject *parent = 0);
>
> - int rowCount(const QModelIndex &parent = QModelIndex()) const;
> - QVariant data(const QModelIndex &index, int role) const;
> + virtual int rowCount(const QModelIndex &parent = QModelIndex()) const;
> + virtual QVariant data(const QModelIndex &index, int role) const;
> + QVariant threadData(const History::Thread &thread, int role) const;
>
> bool canFetchMore(const QModelIndex &parent = QModelIndex()) const;
> void fetchMore(const QModelIndex &parent);
>
> - QHash<int, QByteArray> roleNames() const;
> -
> - HistoryQmlFilter *filter() const;
> - void setFilter(HistoryQmlFilter *value);
> -
> - HistoryQmlSort *sort() const;
> - void setSort(HistoryQmlSort *value);
> -
> - EventType type() const;
> - void setType(EventType value);
> -
> - bool matchContacts() const;
> - void setMatchContacts(bool value);
> -
> - Q_INVOKABLE QString threadIdForParticipants(const QString &accountId,
> - int eventType,
> - const QStringList &participants,
> - int matchFlags = (int)History::MatchCaseSensitive,
> - bool create = false);
> + virtual QHash<int, QByteArray> roleNames() const;
> +
> Q_INVOKABLE bool removeThread(const QString &accountId, const QString &threadId, int eventType);
> - Q_INVOKABLE QVariant get(int row) const;
> -
> -Q_SIGNALS:
> - void filterChanged();
> - void sortChanged();
> - void typeChanged();
> - void matchContactsChanged();
> - void countChanged();
> - void canFetchMoreChanged();
>
> protected Q_SLOTS:
> - void triggerQueryUpdate();
> - void updateQuery();
> - void onThreadsAdded(const History::Threads &threads);
> - void onThreadsModified(const History::Threads &threads);
> - void onThreadsRemoved(const History::Threads &threads);
> - void onContactInfoChanged(const QString &phoneNumber, const QVariantMap &contactInfo);
> + virtual void updateQuery();
> + virtual void onThreadsAdded(const History::Threads &threads);
> + virtual void onThreadsModified(const History::Threads &threads);
> + virtual void onThreadsRemoved(const History::Threads &threads);
>
> protected:
> - void timerEvent(QTimerEvent *event);
> + History::Threads fetchNextPage();
>
> private:
> History::ThreadViewPtr mThreadView;
> History::Threads mThreads;
> bool mCanFetchMore;
> - HistoryQmlFilter *mFilter;
> - HistoryQmlSort *mSort;
> - EventType mType;
> - bool mMatchContacts;
> QHash<int, QByteArray> mRoles;
> mutable QMap<History::TextEvent, QList<QVariant> > mAttachmentCache;
> - int mUpdateTimer;
> };
>
> #endif // HISTORYTHREADMODEL_H
>
> === removed file 'Ubuntu/History/sortproxymodel.cpp'
> --- Ubuntu/History/sortproxymodel.cpp 2014-08-06 14:36:36 +0000
> +++ Ubuntu/History/sortproxymodel.cpp 1970-01-01 00:00:00 +0000
> @@ -1,68 +0,0 @@
> -/*
> - * Copyright (C) 2013 Canonical, Ltd.
> - *
> - * Authors:
> - * Tiago Salem Herrmann <tiago.herrmann at canonical.com>
> - *
> - * This file is part of history-service.
> - *
> - * history-service is free software; you can redistribute it and/or modify
> - * it under the terms of the GNU General Public License as published by
> - * the Free Software Foundation; version 3.
> - *
> - * history-service is distributed in the hope that it will be useful,
> - * but WITHOUT ANY WARRANTY; without even the implied warranty of
> - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> - * GNU General Public License for more details.
> - *
> - * You should have received a copy of the GNU General Public License
> - * along with this program. If not, see <http://www.gnu.org/licenses/>.
> - */
> -
> -#include "sortproxymodel.h"
> -#include <QDebug>
> -
> -SortProxyModel::SortProxyModel(QObject *parent) :
> - QSortFilterProxyModel(parent), mAscending(true)
> -{
> - setDynamicSortFilter(true);
> - updateSorting();
> -
> - connect(this, SIGNAL(rowsInserted(QModelIndex,int,int)), SIGNAL(countChanged()));
> - connect(this, SIGNAL(rowsRemoved(QModelIndex,int,int)), SIGNAL(countChanged()));
> - connect(this, SIGNAL(modelReset()), SIGNAL(countChanged()));
> -}
> -
> -bool SortProxyModel::ascending() const
> -{
> - return mAscending;
> -}
> -
> -void SortProxyModel::setAscending(bool value)
> -{
> - if (mAscending != value) {
> - mAscending = value;
> - updateSorting();
> - Q_EMIT ascendingChanged();
> - }
> -}
> -
> -QVariant SortProxyModel::get(int row) const
> -{
> - QVariantMap data;
> - QModelIndex sourceIndex = mapToSource(index(row, 0));
> - if (sourceIndex.isValid()) {
> - QAbstractItemModel *source = sourceModel();
> - QHash<int, QByteArray> roles = source->roleNames();
> - Q_FOREACH(int role, roles.keys()) {
> - data.insert(roles[role], source->data(sourceIndex, role));
> - }
> - }
> -
> - return data;
> -}
> -
> -void SortProxyModel::updateSorting()
> -{
> - sort(0, mAscending ? Qt::AscendingOrder : Qt::DescendingOrder);
> -}
>
> === removed file 'Ubuntu/History/sortproxymodel.h'
> --- Ubuntu/History/sortproxymodel.h 2014-08-06 14:36:36 +0000
> +++ Ubuntu/History/sortproxymodel.h 1970-01-01 00:00:00 +0000
> @@ -1,55 +0,0 @@
> -/*
> - * Copyright (C) 2013 Canonical, Ltd.
> - *
> - * Authors:
> - * Tiago Salem Herrmann <tiago.herrmann at canonical.com>
> - *
> - * This file is part of history-service.
> - *
> - * history-service is free software; you can redistribute it and/or modify
> - * it under the terms of the GNU General Public License as published by
> - * the Free Software Foundation; version 3.
> - *
> - * history-service is distributed in the hope that it will be useful,
> - * but WITHOUT ANY WARRANTY; without even the implied warranty of
> - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> - * GNU General Public License for more details.
> - *
> - * You should have received a copy of the GNU General Public License
> - * along with this program. If not, see <http://www.gnu.org/licenses/>.
> - */
> -
> -#ifndef SORTPROXYMODEL_H
> -#define SORTPROXYMODEL_H
> -
> -#include <QSortFilterProxyModel>
> -
> -class SortProxyModel : public QSortFilterProxyModel
> -{
> - Q_OBJECT
> - Q_PROPERTY(bool ascending
> - READ ascending
> - WRITE setAscending
> - NOTIFY ascendingChanged)
> - Q_PROPERTY(int count READ rowCount NOTIFY countChanged)
> -
> -public:
> - explicit SortProxyModel(QObject *parent = 0);
> -
> - bool ascending() const;
> - void setAscending(bool value);
> -
> - Q_INVOKABLE QVariant get(int row) const;
> -
> -private Q_SLOTS:
> - void updateSorting();
> -
> -Q_SIGNALS:
> - void ascendingChanged();
> - void countChanged();
> -
> -private:
> - bool mAscending;
> -};
> -
> -#endif // SORTPROXYMODEL_H
>
> === modified file 'src/thread.cpp'
> --- src/thread.cpp 2013-12-03 20:04:18 +0000
> +++ src/thread.cpp 2014-09-09 23:16:29 +0000
> @@ -164,6 +164,8 @@
> map[FieldParticipants] = d->participants;
> map[FieldCount] = d->count;
> map[FieldUnreadCount] = d->unreadCount;
> + map[FieldLastEventId] = lastEvent().eventId();
> + map[FieldLastEventTimestamp] = lastEvent().timestamp();
>
> return map;
> }
>
> === modified file 'src/types.h'
> --- src/types.h 2014-09-09 23:16:29 +0000
> +++ src/types.h 2014-09-09 23:16:29 +0000
> @@ -109,6 +109,10 @@
> static const char* FieldDate = "date";
> static const char* FieldNewEvent = "newEvent";
>
> +// thread fields
> +static const char* FieldLastEventId = "lastEventId";
> +static const char* FieldLastEventTimestamp = "lastEventTimestamp";
> +
> // text event fields
> static const char* FieldMessage = "message";
> static const char* FieldMessageType = "messageType";
>
--
https://code.launchpad.net/~boiko/history-service/keep_models_sorted/+merge/234024
Your team Ubuntu Phablet Team is subscribed to branch lp:history-service.
More information about the Ubuntu-reviews
mailing list