Merge lp:~bfiller/history-service/rtm-14.09-sim-presence into lp:history-service/rtm-14.09
- rtm-14.09-sim-presence
- Merge into rtm-14.09
Proposed by
Bill Filler
Status: | Merged |
---|---|
Approved by: | Bill Filler |
Approved revision: | 164 |
Merged at revision: | 164 |
Proposed branch: | lp:~bfiller/history-service/rtm-14.09-sim-presence |
Merge into: | lp:history-service/rtm-14.09 |
Diff against target: |
884 lines (+517/-56) 11 files modified
Ubuntu/History/CMakeLists.txt (+2/-0) Ubuntu/History/historyeventmodel.cpp (+34/-20) Ubuntu/History/historyeventmodel.h (+18/-14) Ubuntu/History/historygroupedeventsmodel.cpp (+346/-0) Ubuntu/History/historygroupedeventsmodel.h (+82/-0) Ubuntu/History/historyqmlplugin.cpp (+2/-0) Ubuntu/History/historythreadmodel.cpp (+23/-21) Ubuntu/History/historythreadmodel.h (+2/-1) src/event.cpp (+6/-0) src/event.h (+1/-0) src/types.h (+1/-0) |
To merge this branch: | bzr merge lp:~bfiller/history-service/rtm-14.09-sim-presence |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Ubuntu Phablet Team | Pending | ||
Review via email: mp+231916@code.launchpad.net |
Commit message
sim presence and call grouping merge from trunk
Description of the change
sim presence and call grouping merge from trunk
To post a comment you must log in.
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file 'Ubuntu/History/CMakeLists.txt' |
2 | --- Ubuntu/History/CMakeLists.txt 2014-07-14 23:05:23 +0000 |
3 | +++ Ubuntu/History/CMakeLists.txt 2014-08-22 15:31:10 +0000 |
4 | @@ -2,6 +2,7 @@ |
5 | |
6 | set(plugin_SRCS |
7 | historyeventmodel.cpp |
8 | + historygroupedeventsmodel.cpp |
9 | historythreadgroupingproxymodel.cpp |
10 | historyqmltexteventattachment.cpp |
11 | historyqmlfilter.cpp |
12 | @@ -15,6 +16,7 @@ |
13 | |
14 | set(plugin_HDRS |
15 | historyeventmodel.h |
16 | + historygroupedeventsmodel.h |
17 | historythreadgroupingproxymodel.h |
18 | historyqmltexteventattachment.h |
19 | historyqmlfilter.h |
20 | |
21 | === modified file 'Ubuntu/History/historyeventmodel.cpp' |
22 | --- Ubuntu/History/historyeventmodel.cpp 2014-08-07 19:22:31 +0000 |
23 | +++ Ubuntu/History/historyeventmodel.cpp 2014-08-22 15:31:10 +0000 |
24 | @@ -37,7 +37,8 @@ |
25 | |
26 | HistoryEventModel::HistoryEventModel(QObject *parent) : |
27 | QAbstractListModel(parent), mCanFetchMore(true), mFilter(0), |
28 | - mSort(0), mType(HistoryThreadModel::EventTypeText), mEventWritingTimer(0), mFetchTimer(0) |
29 | + mSort(new HistoryQmlSort(this)), mType(HistoryThreadModel::EventTypeText), |
30 | + mEventWritingTimer(0), mUpdateTimer(0) |
31 | { |
32 | connect(this, SIGNAL(rowsInserted(QModelIndex,int,int)), this, SIGNAL(countChanged())); |
33 | connect(this, SIGNAL(rowsRemoved(QModelIndex,int,int)), this, SIGNAL(countChanged())); |
34 | @@ -63,7 +64,7 @@ |
35 | mRoles[CallDurationRole] = "callDuration"; |
36 | |
37 | // create the view and get some objects |
38 | - updateQuery(); |
39 | + triggerQueryUpdate(); |
40 | } |
41 | |
42 | int HistoryEventModel::rowCount(const QModelIndex &parent) const |
43 | @@ -81,7 +82,11 @@ |
44 | return QVariant(); |
45 | } |
46 | |
47 | - History::Event event = mEvents[index.row()]; |
48 | + return eventData(mEvents[index.row()], role); |
49 | +} |
50 | + |
51 | +QVariant HistoryEventModel::eventData(const History::Event &event, int role) const |
52 | +{ |
53 | History::TextEvent textEvent; |
54 | History::VoiceEvent voiceEvent; |
55 | History::Thread thread; |
56 | @@ -194,7 +199,7 @@ |
57 | return; |
58 | } |
59 | |
60 | - History::Events events = mView->nextPage(); |
61 | + History::Events events = fetchNextPage(); |
62 | |
63 | qDebug() << "Got events:" << events.count(); |
64 | if (events.isEmpty()) { |
65 | @@ -227,11 +232,11 @@ |
66 | if (mFilter) { |
67 | connect(mFilter, |
68 | SIGNAL(filterChanged()), |
69 | - SLOT(updateQuery())); |
70 | + SLOT(triggerQueryUpdate())); |
71 | } |
72 | |
73 | Q_EMIT filterChanged(); |
74 | - updateQuery(); |
75 | + triggerQueryUpdate(); |
76 | } |
77 | |
78 | HistoryQmlSort *HistoryEventModel::sort() const |
79 | @@ -250,11 +255,11 @@ |
80 | if (mSort) { |
81 | connect(mSort, |
82 | SIGNAL(sortChanged()), |
83 | - SLOT(updateQuery())); |
84 | + SLOT(triggerQueryUpdate())); |
85 | } |
86 | |
87 | Q_EMIT sortChanged(); |
88 | - updateQuery(); |
89 | + triggerQueryUpdate(); |
90 | } |
91 | |
92 | HistoryThreadModel::EventType HistoryEventModel::type() const |
93 | @@ -266,7 +271,7 @@ |
94 | { |
95 | mType = value; |
96 | Q_EMIT typeChanged(); |
97 | - updateQuery(); |
98 | + triggerQueryUpdate(); |
99 | } |
100 | |
101 | QString HistoryEventModel::threadIdForParticipants(const QString &accountId, int eventType, const QStringList &participants, int matchFlags, bool create) |
102 | @@ -378,7 +383,7 @@ |
103 | SLOT(onEventsRemoved(History::Events))); |
104 | connect(mView.data(), |
105 | SIGNAL(invalidated()), |
106 | - SLOT(updateQuery())); |
107 | + SLOT(triggerQueryUpdate())); |
108 | |
109 | mCanFetchMore = true; |
110 | Q_EMIT canFetchMoreChanged(); |
111 | @@ -391,12 +396,7 @@ |
112 | } |
113 | mAttachmentCache.clear(); |
114 | |
115 | - if (mFetchTimer) { |
116 | - killTimer(mFetchTimer); |
117 | - } |
118 | - |
119 | - // delay the loading of the model data until the settings settle down |
120 | - mFetchTimer = startTimer(100); |
121 | + fetchMore(QModelIndex()); |
122 | } |
123 | |
124 | void HistoryEventModel::onEventsAdded(const History::Events &events) |
125 | @@ -474,13 +474,18 @@ |
126 | qDebug() << "... succeeded!"; |
127 | mEventWritingQueue.clear(); |
128 | } |
129 | - } else if (event->timerId() == mFetchTimer) { |
130 | - killTimer(mFetchTimer); |
131 | - mFetchTimer = 0; |
132 | - fetchMore(QModelIndex()); |
133 | + } else if (event->timerId() == mUpdateTimer) { |
134 | + killTimer(mUpdateTimer); |
135 | + mUpdateTimer = 0; |
136 | + updateQuery(); |
137 | } |
138 | } |
139 | |
140 | +History::Events HistoryEventModel::fetchNextPage() |
141 | +{ |
142 | + return mView->nextPage(); |
143 | +} |
144 | + |
145 | QVariant HistoryEventModel::get(int row) const |
146 | { |
147 | if (row >= this->rowCount() || row < 0) { |
148 | @@ -489,3 +494,12 @@ |
149 | |
150 | return QVariant(mEvents[row].properties()); |
151 | } |
152 | + |
153 | +void HistoryEventModel::triggerQueryUpdate() |
154 | +{ |
155 | + if (mUpdateTimer) { |
156 | + killTimer(mUpdateTimer); |
157 | + } |
158 | + // delay the loading of the model data until the settings settle down |
159 | + mUpdateTimer = startTimer(100); |
160 | +} |
161 | |
162 | === modified file 'Ubuntu/History/historyeventmodel.h' |
163 | --- Ubuntu/History/historyeventmodel.h 2014-08-07 19:22:31 +0000 |
164 | +++ Ubuntu/History/historyeventmodel.h 2014-08-22 15:31:10 +0000 |
165 | @@ -53,18 +53,20 @@ |
166 | TextReadSubjectRole, |
167 | TextMessageAttachmentsRole, |
168 | CallMissedRole, |
169 | - CallDurationRole |
170 | + CallDurationRole, |
171 | + LastRole |
172 | }; |
173 | |
174 | explicit HistoryEventModel(QObject *parent = 0); |
175 | |
176 | - int rowCount(const QModelIndex &parent = QModelIndex()) const; |
177 | - QVariant data(const QModelIndex &index, int role) const; |
178 | - |
179 | - Q_INVOKABLE bool canFetchMore(const QModelIndex &parent = QModelIndex()) const; |
180 | - Q_INVOKABLE void fetchMore(const QModelIndex &parent = QModelIndex()); |
181 | - |
182 | - QHash<int, QByteArray> roleNames() const; |
183 | + virtual int rowCount(const QModelIndex &parent = QModelIndex()) const; |
184 | + virtual QVariant data(const QModelIndex &index, int role) const; |
185 | + QVariant eventData(const History::Event &event, int role) const; |
186 | + |
187 | + Q_INVOKABLE virtual bool canFetchMore(const QModelIndex &parent = QModelIndex()) const; |
188 | + Q_INVOKABLE virtual void fetchMore(const QModelIndex &parent = QModelIndex()); |
189 | + |
190 | + virtual QHash<int, QByteArray> roleNames() const; |
191 | |
192 | HistoryQmlFilter *filter() const; |
193 | void setFilter(HistoryQmlFilter *value); |
194 | @@ -85,7 +87,7 @@ |
195 | Q_INVOKABLE bool markEventAsRead(const QString &accountId, const QString &threadId, const QString &eventId, int eventType); |
196 | |
197 | Q_INVOKABLE bool removeEventAttachment(const QString &accountId, const QString &threadId, const QString &eventId, int eventType, const QString &attachmentId); |
198 | - Q_INVOKABLE QVariant get(int row) const; |
199 | + Q_INVOKABLE virtual QVariant get(int row) const; |
200 | |
201 | Q_SIGNALS: |
202 | void countChanged(); |
203 | @@ -95,13 +97,15 @@ |
204 | void canFetchMoreChanged(); |
205 | |
206 | protected Q_SLOTS: |
207 | - void updateQuery(); |
208 | - void onEventsAdded(const History::Events &events); |
209 | - void onEventsModified(const History::Events &events); |
210 | - void onEventsRemoved(const History::Events &events); |
211 | + void triggerQueryUpdate(); |
212 | + virtual void updateQuery(); |
213 | + virtual void onEventsAdded(const History::Events &events); |
214 | + virtual void onEventsModified(const History::Events &events); |
215 | + virtual void onEventsRemoved(const History::Events &events); |
216 | |
217 | protected: |
218 | void timerEvent(QTimerEvent *event); |
219 | + History::Events fetchNextPage(); |
220 | |
221 | private: |
222 | History::EventViewPtr mView; |
223 | @@ -114,7 +118,7 @@ |
224 | mutable QMap<History::TextEvent, QList<QVariant> > mAttachmentCache; |
225 | History::Events mEventWritingQueue; |
226 | int mEventWritingTimer; |
227 | - int mFetchTimer; |
228 | + int mUpdateTimer; |
229 | }; |
230 | |
231 | #endif // HISTORYEVENTMODEL_H |
232 | |
233 | === added file 'Ubuntu/History/historygroupedeventsmodel.cpp' |
234 | --- Ubuntu/History/historygroupedeventsmodel.cpp 1970-01-01 00:00:00 +0000 |
235 | +++ Ubuntu/History/historygroupedeventsmodel.cpp 2014-08-22 15:31:10 +0000 |
236 | @@ -0,0 +1,346 @@ |
237 | +/* |
238 | + * Copyright (C) 2014 Canonical, Ltd. |
239 | + * |
240 | + * Authors: |
241 | + * Gustavo Pichorim Boiko <gustavo.boiko@canonical.com> |
242 | + * |
243 | + * This file is part of history-service. |
244 | + * |
245 | + * history-service is free software; you can redistribute it and/or modify |
246 | + * it under the terms of the GNU General Public License as published by |
247 | + * the Free Software Foundation; version 3. |
248 | + * |
249 | + * history-service is distributed in the hope that it will be useful, |
250 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
251 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
252 | + * GNU General Public License for more details. |
253 | + * |
254 | + * You should have received a copy of the GNU General Public License |
255 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
256 | + */ |
257 | + |
258 | +#include "historygroupedeventsmodel.h" |
259 | +#include "phoneutils_p.h" |
260 | +#include "sort.h" |
261 | +#include "historyqmlsort.h" |
262 | + |
263 | +HistoryGroupedEventsModel::HistoryGroupedEventsModel(QObject *parent) : |
264 | + HistoryEventModel(parent) |
265 | +{ |
266 | +} |
267 | + |
268 | +int HistoryGroupedEventsModel::rowCount(const QModelIndex &parent) const |
269 | +{ |
270 | + if (parent.isValid()) { |
271 | + return 0; |
272 | + } |
273 | + |
274 | + return mEventGroups.count(); |
275 | +} |
276 | + |
277 | +QVariant HistoryGroupedEventsModel::data(const QModelIndex &index, int role) const |
278 | +{ |
279 | + if (!index.isValid() || index.row() < 0 || index.row() >= mEventGroups.count()) { |
280 | + return QVariant(); |
281 | + } |
282 | + |
283 | + HistoryEventGroup group = mEventGroups[index.row()]; |
284 | + QVariant result; |
285 | + QVariantList events; |
286 | + |
287 | + switch (role) { |
288 | + case EventsRole: |
289 | + Q_FOREACH(const History::Event &event, group.events) { |
290 | + events << event.properties(); |
291 | + } |
292 | + result = events; |
293 | + break; |
294 | + case EventCountRole: |
295 | + result = group.events.count(); |
296 | + break; |
297 | + default: |
298 | + result = eventData(group.displayedEvent, role); |
299 | + break; |
300 | + } |
301 | + |
302 | + return result; |
303 | +} |
304 | + |
305 | +void HistoryGroupedEventsModel::fetchMore(const QModelIndex &parent) |
306 | +{ |
307 | + if (!canFetchMore(parent)) { |
308 | + return; |
309 | + } |
310 | + |
311 | + History::Events events = fetchNextPage(); |
312 | + |
313 | + // History already deliver us the events in the right order |
314 | + // but we might have added new entries in the added, removed, modified events. |
315 | + // still, it is less expensive to do a sequential search starting from the bottom |
316 | + // than to do a binary search for each event, as it is very likely that the entries |
317 | + // belong to the bottom part of the model. |
318 | + Q_FOREACH(const History::Event event, events) { |
319 | + bool found = false; |
320 | + int pos = mEventGroups.count() -1; |
321 | + for (; pos >= 0; pos--) { |
322 | + HistoryEventGroup &group = mEventGroups[pos]; |
323 | + if (areOfSameGroup(event, group.displayedEvent)) { |
324 | + found = true; |
325 | + addEventToGroup(event, group, pos); |
326 | + break; |
327 | + } else if (isAscending() ? lessThan(group.displayedEvent, event) : lessThan(event, group.displayedEvent)) { |
328 | + break; |
329 | + } |
330 | + } |
331 | + |
332 | + if (!found) { |
333 | + // the item goes into a new group right after the position found above |
334 | + pos++; |
335 | + HistoryEventGroup group; |
336 | + group.displayedEvent = event; |
337 | + group.events << event; |
338 | + beginInsertRows(QModelIndex(), pos, pos); |
339 | + mEventGroups.insert(pos, group); |
340 | + endInsertRows(); |
341 | + } |
342 | + } |
343 | +} |
344 | + |
345 | +QHash<int, QByteArray> HistoryGroupedEventsModel::roleNames() const |
346 | +{ |
347 | + QHash<int, QByteArray> roles = HistoryEventModel::roleNames(); |
348 | + roles[EventsRole] = "events"; |
349 | + roles[EventCountRole] = "eventCount"; |
350 | + return roles; |
351 | +} |
352 | + |
353 | +void HistoryGroupedEventsModel::updateQuery() |
354 | +{ |
355 | + // remove all event groups from the model |
356 | + if (!mEventGroups.isEmpty()) { |
357 | + beginRemoveRows(QModelIndex(), 0, mEventGroups.count() - 1); |
358 | + mEventGroups.clear(); |
359 | + endRemoveRows(); |
360 | + } |
361 | + |
362 | + // and ask HistoryEventModel to update the query and fetch items again |
363 | + HistoryEventModel::updateQuery(); |
364 | +} |
365 | + |
366 | +void HistoryGroupedEventsModel::onEventsAdded(const History::Events &events) |
367 | +{ |
368 | + if (!events.count()) { |
369 | + return; |
370 | + } |
371 | + |
372 | + Q_FOREACH(const History::Event &event, events) { |
373 | + int pos = positionForEvent(event); |
374 | + |
375 | + // check if the event belongs to the group at the position |
376 | + if (pos >= 0 && pos < mEventGroups.count()) { |
377 | + HistoryEventGroup &group = mEventGroups[pos]; |
378 | + if (areOfSameGroup(event, group.displayedEvent)) { |
379 | + addEventToGroup(event, group, pos); |
380 | + continue; |
381 | + } |
382 | + } |
383 | + |
384 | + // else, we just create a new group |
385 | + beginInsertRows(QModelIndex(), pos, pos); |
386 | + HistoryEventGroup group; |
387 | + group.displayedEvent = event; |
388 | + group.events << event; |
389 | + mEventGroups.insert(pos, group); |
390 | + endInsertRows(); |
391 | + |
392 | + } |
393 | +} |
394 | + |
395 | +void HistoryGroupedEventsModel::onEventsModified(const History::Events &events) |
396 | +{ |
397 | + // FIXME: we are not yet handling events changing the property used for sorting |
398 | + // so for now the behavior is to find the item and check if it needs inserting or |
399 | + // updating in the group, which is exactly what onEventsAdded() does, so: |
400 | + onEventsAdded(events); |
401 | +} |
402 | + |
403 | +void HistoryGroupedEventsModel::onEventsRemoved(const History::Events &events) |
404 | +{ |
405 | + Q_FOREACH(const History::Event &event, events) { |
406 | + int pos = positionForEvent(event); |
407 | + if (pos < 0 || pos >= rowCount()) { |
408 | + continue; |
409 | + } |
410 | + HistoryEventGroup &group = mEventGroups[pos]; |
411 | + if (!group.events.contains(event)) { |
412 | + continue; |
413 | + } |
414 | + removeEventFromGroup(event, group, pos); |
415 | + } |
416 | +} |
417 | + |
418 | +bool HistoryGroupedEventsModel::compareParticipants(const QStringList &list1, const QStringList &list2) |
419 | +{ |
420 | + if (list1.count() != list2.count()) { |
421 | + return false; |
422 | + } |
423 | + |
424 | + int found = 0; |
425 | + Q_FOREACH(const QString &participant, list1) { |
426 | + Q_FOREACH(const QString &item, list2) { |
427 | + if (PhoneUtils::comparePhoneNumbers(participant, item)) { |
428 | + found++; |
429 | + break; |
430 | + } |
431 | + } |
432 | + } |
433 | + |
434 | + return found == list1.count(); |
435 | +} |
436 | + |
437 | +bool HistoryGroupedEventsModel::areOfSameGroup(const History::Event &event1, const History::Event &event2) |
438 | +{ |
439 | + QVariantMap props1 = event1.properties(); |
440 | + QVariantMap props2 = event2.properties(); |
441 | + |
442 | + Q_FOREACH(const QString &property, mGroupingProperties) { |
443 | + // first check if the property exists in the maps |
444 | + if (!props1.contains(property) || !props2.contains(property)) { |
445 | + return false; |
446 | + } |
447 | + |
448 | + // now check if the values are the same |
449 | + if (property == History::FieldParticipants) { |
450 | + if (!compareParticipants(props1[property].toStringList(), |
451 | + props2[property].toStringList())) { |
452 | + return false; |
453 | + } |
454 | + } else if (props1[property] != props2[property]) { |
455 | + return false; |
456 | + } |
457 | + } |
458 | + |
459 | + // if it didn't fail before, the events are indeed of the same group |
460 | + return true; |
461 | +} |
462 | + |
463 | +void HistoryGroupedEventsModel::addEventToGroup(const History::Event &event, HistoryEventGroup &group, int row) |
464 | +{ |
465 | + if (!group.events.contains(event)) { |
466 | + // insert the event in the correct position according to the sort criteria |
467 | + bool append = true; |
468 | + for (int i = 0; i < group.events.count(); ++i) { |
469 | + History::Event &otherEvent = group.events[i]; |
470 | + if (isAscending() ? lessThan(event, otherEvent) : lessThan(otherEvent, event)) { |
471 | + group.events.insert(i, event); |
472 | + append = false; |
473 | + break; |
474 | + } |
475 | + } |
476 | + |
477 | + // if it is not above any item, just append it |
478 | + if (append) { |
479 | + group.events.append(event); |
480 | + } |
481 | + } |
482 | + |
483 | + // now check if the displayed event should be updated |
484 | + History::Event &firstEvent = group.events.first(); |
485 | + if (group.displayedEvent != firstEvent) { |
486 | + group.displayedEvent = firstEvent; |
487 | + QModelIndex idx(index(row)); |
488 | + Q_EMIT dataChanged(idx, idx); |
489 | + } |
490 | +} |
491 | + |
492 | +void HistoryGroupedEventsModel::removeEventFromGroup(const History::Event &event, HistoryEventGroup &group, int row) |
493 | +{ |
494 | + if (group.events.contains(event)) { |
495 | + group.events.removeOne(event); |
496 | + } |
497 | + |
498 | + if (group.events.isEmpty()) { |
499 | + beginRemoveRows(QModelIndex(), row, row); |
500 | + mEventGroups.removeAt(row); |
501 | + endRemoveRows(); |
502 | + return; |
503 | + } |
504 | + |
505 | + if (group.displayedEvent == event) { |
506 | + // check what is the event that should be displayed |
507 | + group.displayedEvent = group.events.first(); |
508 | + Q_FOREACH(const History::Event &other, group.events) { |
509 | + if (isAscending() ? lessThan(other, group.displayedEvent) : lessThan(group.displayedEvent, other)) { |
510 | + group.displayedEvent = other; |
511 | + } |
512 | + } |
513 | + } |
514 | + QModelIndex idx = index(row); |
515 | + Q_EMIT dataChanged(idx, idx); |
516 | +} |
517 | + |
518 | +bool HistoryGroupedEventsModel::lessThan(const History::Event &left, const History::Event &right) const |
519 | +{ |
520 | + |
521 | + QVariant leftValue = left.properties()[sort()->sortField()]; |
522 | + QVariant rightValue = right.properties()[sort()->sortField()]; |
523 | + return leftValue < rightValue; |
524 | +} |
525 | + |
526 | +int HistoryGroupedEventsModel::positionForEvent(const History::Event &event) const |
527 | +{ |
528 | + // do a binary search for the item position on the list |
529 | + int lowerBound = 0; |
530 | + int upperBound = mEventGroups.count() - 1; |
531 | + if (upperBound < 0) { |
532 | + return 0; |
533 | + } |
534 | + |
535 | + while (true) { |
536 | + int pos = (upperBound + lowerBound) / 2; |
537 | + const History::Event &posEvent = mEventGroups[pos].displayedEvent; |
538 | + if (lowerBound == pos) { |
539 | + if (isAscending() ? lessThan(event, posEvent) : lessThan(posEvent, event)) { |
540 | + return pos; |
541 | + } |
542 | + } |
543 | + if (isAscending() ? lessThan(posEvent, event) : lessThan(event, posEvent)) { |
544 | + lowerBound = pos + 1; // its in the upper |
545 | + if (lowerBound > upperBound) { |
546 | + return pos += 1; |
547 | + } |
548 | + } else if (lowerBound > upperBound) { |
549 | + return pos; |
550 | + } else { |
551 | + upperBound = pos - 1; // its in the lower |
552 | + } |
553 | + } |
554 | + |
555 | +} |
556 | + |
557 | +QVariant HistoryGroupedEventsModel::get(int row) const |
558 | +{ |
559 | + if (row >= rowCount() || row < 0) { |
560 | + return QVariant(); |
561 | + } |
562 | + |
563 | + return data(index(row), EventsRole); |
564 | +} |
565 | + |
566 | + |
567 | +QStringList HistoryGroupedEventsModel::groupingProperties() const |
568 | +{ |
569 | + return mGroupingProperties; |
570 | +} |
571 | + |
572 | +void HistoryGroupedEventsModel::setGroupingProperties(const QStringList &properties) |
573 | +{ |
574 | + mGroupingProperties = properties; |
575 | + Q_EMIT groupingPropertiesChanged(); |
576 | + triggerQueryUpdate(); |
577 | +} |
578 | + |
579 | +bool HistoryGroupedEventsModel::isAscending() const |
580 | +{ |
581 | + return sort() && sort()->sort().sortOrder() == Qt::AscendingOrder; |
582 | +} |
583 | |
584 | === added file 'Ubuntu/History/historygroupedeventsmodel.h' |
585 | --- Ubuntu/History/historygroupedeventsmodel.h 1970-01-01 00:00:00 +0000 |
586 | +++ Ubuntu/History/historygroupedeventsmodel.h 2014-08-22 15:31:10 +0000 |
587 | @@ -0,0 +1,82 @@ |
588 | +/* |
589 | + * Copyright (C) 2014 Canonical, Ltd. |
590 | + * |
591 | + * Authors: |
592 | + * Gustavo Pichorim Boiko <gustavo.boiko@canonical.com> |
593 | + * |
594 | + * This file is part of history-service. |
595 | + * |
596 | + * history-service is free software; you can redistribute it and/or modify |
597 | + * it under the terms of the GNU General Public License as published by |
598 | + * the Free Software Foundation; version 3. |
599 | + * |
600 | + * history-service is distributed in the hope that it will be useful, |
601 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
602 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
603 | + * GNU General Public License for more details. |
604 | + * |
605 | + * You should have received a copy of the GNU General Public License |
606 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
607 | + */ |
608 | + |
609 | +#ifndef HISTORYGROUPEDEVENTSMODEL_H |
610 | +#define HISTORYGROUPEDEVENTSMODEL_H |
611 | + |
612 | +#include "historyeventmodel.h" |
613 | + |
614 | +typedef struct { |
615 | + History::Events events; |
616 | + History::Event displayedEvent; |
617 | +} HistoryEventGroup; |
618 | + |
619 | +class HistoryGroupedEventsModel : public HistoryEventModel |
620 | +{ |
621 | + Q_OBJECT |
622 | + Q_PROPERTY(QStringList groupingProperties |
623 | + READ groupingProperties |
624 | + WRITE setGroupingProperties |
625 | + NOTIFY groupingPropertiesChanged) |
626 | + Q_ENUMS(GroupedRole) |
627 | +public: |
628 | + enum GroupedRole { |
629 | + EventsRole = HistoryEventModel::LastRole, |
630 | + EventCountRole |
631 | + }; |
632 | + |
633 | + explicit HistoryGroupedEventsModel(QObject *parent = 0); |
634 | + |
635 | + // reimplemented from HistoryEventModel |
636 | + int rowCount(const QModelIndex &parent = QModelIndex()) const; |
637 | + QVariant data(const QModelIndex &index, int role) const; |
638 | + Q_INVOKABLE void fetchMore(const QModelIndex &parent = QModelIndex()); |
639 | + QHash<int, QByteArray> roleNames() const; |
640 | + Q_INVOKABLE QVariant get(int row) const; |
641 | + |
642 | + QStringList groupingProperties() const; |
643 | + void setGroupingProperties(const QStringList &properties); |
644 | + |
645 | + bool isAscending() const; |
646 | + |
647 | +Q_SIGNALS: |
648 | + void groupingPropertiesChanged(); |
649 | + |
650 | +protected Q_SLOTS: |
651 | + void updateQuery(); |
652 | + void onEventsAdded(const History::Events &events); |
653 | + void onEventsModified(const History::Events &events); |
654 | + void onEventsRemoved(const History::Events &events); |
655 | + |
656 | +protected: |
657 | + bool compareParticipants(const QStringList &list1, const QStringList &list2); |
658 | + bool areOfSameGroup(const History::Event &event1, const History::Event &event2); |
659 | + void addEventToGroup(const History::Event &event, HistoryEventGroup &group, int row); |
660 | + void removeEventFromGroup(const History::Event &event, HistoryEventGroup &group, int row); |
661 | + bool lessThan(const History::Event &left, const History::Event &right) const; |
662 | + int positionForEvent(const History::Event &event) const; |
663 | + |
664 | +private: |
665 | + QStringList mGroupingProperties; |
666 | + QList<HistoryEventGroup> mEventGroups; |
667 | +}; |
668 | + |
669 | +#endif // HISTORYGROUPEDEVENTSMODEL_H |
670 | |
671 | === modified file 'Ubuntu/History/historyqmlplugin.cpp' |
672 | --- Ubuntu/History/historyqmlplugin.cpp 2014-07-14 23:05:23 +0000 |
673 | +++ Ubuntu/History/historyqmlplugin.cpp 2014-08-22 15:31:10 +0000 |
674 | @@ -27,6 +27,7 @@ |
675 | #include "historythreadmodel.h" |
676 | #include "historythreadgroupingproxymodel.h" |
677 | #include "historyeventmodel.h" |
678 | +#include "historygroupedeventsmodel.h" |
679 | #include "historyqmltexteventattachment.h" |
680 | #include "sortproxymodel.h" |
681 | #include <QQmlEngine> |
682 | @@ -43,6 +44,7 @@ |
683 | { |
684 | // @uri History |
685 | qmlRegisterType<HistoryEventModel>(uri, 0, 1, "HistoryEventModel"); |
686 | + qmlRegisterType<HistoryGroupedEventsModel>(uri, 0, 1, "HistoryGroupedEventsModel"); |
687 | qmlRegisterType<HistoryThreadModel>(uri, 0, 1, "HistoryThreadModel"); |
688 | qmlRegisterType<HistoryQmlFilter>(uri, 0, 1, "HistoryFilter"); |
689 | qmlRegisterType<HistoryQmlIntersectionFilter>(uri, 0, 1, "HistoryIntersectionFilter"); |
690 | |
691 | === modified file 'Ubuntu/History/historythreadmodel.cpp' |
692 | --- Ubuntu/History/historythreadmodel.cpp 2014-08-07 19:22:31 +0000 |
693 | +++ Ubuntu/History/historythreadmodel.cpp 2014-08-22 15:31:10 +0000 |
694 | @@ -35,7 +35,8 @@ |
695 | Q_DECLARE_METATYPE(History::TextEventAttachments) |
696 | |
697 | HistoryThreadModel::HistoryThreadModel(QObject *parent) : |
698 | - QAbstractListModel(parent), mCanFetchMore(true), mFilter(0), mSort(0), mType(EventTypeText), mFetchTimer(0) |
699 | + QAbstractListModel(parent), mCanFetchMore(true), mFilter(0), mSort(0), |
700 | + mType(EventTypeText), mUpdateTimer(0) |
701 | { |
702 | // configure the roles |
703 | mRoles[AccountIdRole] = "accountId"; |
704 | @@ -66,7 +67,7 @@ |
705 | connect(this, SIGNAL(modelReset()), SIGNAL(countChanged())); |
706 | |
707 | // create the results view |
708 | - updateQuery(); |
709 | + triggerQueryUpdate(); |
710 | } |
711 | |
712 | int HistoryThreadModel::rowCount(const QModelIndex &parent) const |
713 | @@ -213,7 +214,7 @@ |
714 | |
715 | void HistoryThreadModel::fetchMore(const QModelIndex &parent) |
716 | { |
717 | - if (parent.isValid()) { |
718 | + if (parent.isValid() || mThreadView.isNull()) { |
719 | return; |
720 | } |
721 | |
722 | @@ -249,11 +250,11 @@ |
723 | if (mFilter) { |
724 | connect(mFilter, |
725 | SIGNAL(filterChanged()), |
726 | - SLOT(updateQuery())); |
727 | + SLOT(triggerQueryUpdate())); |
728 | } |
729 | |
730 | Q_EMIT filterChanged(); |
731 | - updateQuery(); |
732 | + triggerQueryUpdate(); |
733 | } |
734 | |
735 | HistoryQmlSort *HistoryThreadModel::sort() const |
736 | @@ -272,11 +273,11 @@ |
737 | if (mSort) { |
738 | connect(mSort, |
739 | SIGNAL(sortChanged()), |
740 | - SLOT(updateQuery())); |
741 | + SLOT(triggerQueryUpdate())); |
742 | } |
743 | |
744 | Q_EMIT sortChanged(); |
745 | - updateQuery(); |
746 | + triggerQueryUpdate(); |
747 | } |
748 | |
749 | HistoryThreadModel::EventType HistoryThreadModel::type() const |
750 | @@ -288,7 +289,7 @@ |
751 | { |
752 | mType = value; |
753 | Q_EMIT typeChanged(); |
754 | - updateQuery(); |
755 | + triggerQueryUpdate(); |
756 | } |
757 | |
758 | QString HistoryThreadModel::threadIdForParticipants(const QString &accountId, int eventType, const QStringList &participants, int matchFlags, bool create) |
759 | @@ -324,6 +325,14 @@ |
760 | return mThreads[row].properties(); |
761 | } |
762 | |
763 | +void HistoryThreadModel::triggerQueryUpdate() |
764 | +{ |
765 | + if (mUpdateTimer) { |
766 | + killTimer(mUpdateTimer); |
767 | + } |
768 | + mUpdateTimer = startTimer(100); |
769 | +} |
770 | + |
771 | void HistoryThreadModel::updateQuery() |
772 | { |
773 | // remove all events from the model |
774 | @@ -363,7 +372,7 @@ |
775 | SLOT(onThreadsRemoved(History::Threads))); |
776 | connect(mThreadView.data(), |
777 | SIGNAL(invalidated()), |
778 | - SLOT(updateQuery())); |
779 | + SLOT(triggerQueryUpdate())); |
780 | |
781 | Q_FOREACH(const QVariant &attachment, mAttachmentCache) { |
782 | HistoryQmlTextEventAttachment *qmlAttachment = attachment.value<HistoryQmlTextEventAttachment *>(); |
783 | @@ -373,16 +382,10 @@ |
784 | } |
785 | mAttachmentCache.clear(); |
786 | |
787 | - if (mFetchTimer) { |
788 | - killTimer(mFetchTimer); |
789 | - } |
790 | - |
791 | // and fetch again |
792 | mCanFetchMore = true; |
793 | Q_EMIT canFetchMoreChanged(); |
794 | - |
795 | - // delay the loading just to give the settings some time to settle down |
796 | - mFetchTimer = startTimer(100); |
797 | + fetchMore(QModelIndex()); |
798 | } |
799 | |
800 | void HistoryThreadModel::onThreadsAdded(const History::Threads &threads) |
801 | @@ -430,10 +433,9 @@ |
802 | |
803 | void HistoryThreadModel::timerEvent(QTimerEvent *event) |
804 | { |
805 | - if (event->timerId() == mFetchTimer) { |
806 | - killTimer(mFetchTimer); |
807 | - mFetchTimer = 0; |
808 | - |
809 | - fetchMore(QModelIndex()); |
810 | + if (event->timerId() == mUpdateTimer) { |
811 | + killTimer(mUpdateTimer); |
812 | + mUpdateTimer = 0; |
813 | + updateQuery(); |
814 | } |
815 | } |
816 | |
817 | === modified file 'Ubuntu/History/historythreadmodel.h' |
818 | --- Ubuntu/History/historythreadmodel.h 2014-08-07 19:22:31 +0000 |
819 | +++ Ubuntu/History/historythreadmodel.h 2014-08-22 15:31:10 +0000 |
820 | @@ -125,6 +125,7 @@ |
821 | void canFetchMoreChanged(); |
822 | |
823 | protected Q_SLOTS: |
824 | + void triggerQueryUpdate(); |
825 | void updateQuery(); |
826 | void onThreadsAdded(const History::Threads &threads); |
827 | void onThreadsModified(const History::Threads &threads); |
828 | @@ -142,7 +143,7 @@ |
829 | EventType mType; |
830 | QHash<int, QByteArray> mRoles; |
831 | mutable QMap<History::TextEvent, QList<QVariant> > mAttachmentCache; |
832 | - int mFetchTimer; |
833 | + int mUpdateTimer; |
834 | }; |
835 | |
836 | #endif // HISTORYTHREADMODEL_H |
837 | |
838 | === modified file 'src/event.cpp' |
839 | --- src/event.cpp 2013-09-26 21:06:50 +0000 |
840 | +++ src/event.cpp 2014-08-22 15:31:10 +0000 |
841 | @@ -55,6 +55,7 @@ |
842 | map[FieldEventId] = eventId; |
843 | map[FieldSenderId] = senderId; |
844 | map[FieldTimestamp] = timestamp.toString(Qt::ISODate); |
845 | + map[FieldDate] = timestamp.date().toString(Qt::ISODate); |
846 | map[FieldNewEvent] = newEvent; |
847 | map[FieldType] = type(); |
848 | map[FieldParticipants] = participants; |
849 | @@ -243,6 +244,11 @@ |
850 | return true; |
851 | } |
852 | |
853 | +bool Event::operator!=(const Event &other) const |
854 | +{ |
855 | + return !(*this == other); |
856 | +} |
857 | + |
858 | bool Event::operator<(const Event &other) const |
859 | { |
860 | QString selfData = accountId() + threadId() + eventId(); |
861 | |
862 | === modified file 'src/event.h' |
863 | --- src/event.h 2013-09-26 21:06:50 +0000 |
864 | +++ src/event.h 2014-08-22 15:31:10 +0000 |
865 | @@ -56,6 +56,7 @@ |
866 | QVariantMap properties() const; |
867 | bool isNull() const; |
868 | bool operator==(const Event &other) const; |
869 | + bool operator!=(const Event &other) const; |
870 | bool operator<(const Event &other) const; |
871 | |
872 | protected: |
873 | |
874 | === modified file 'src/types.h' |
875 | --- src/types.h 2014-06-25 11:45:09 +0000 |
876 | +++ src/types.h 2014-08-22 15:31:10 +0000 |
877 | @@ -106,6 +106,7 @@ |
878 | static const char* FieldUnreadCount = "unreadCount"; |
879 | static const char* FieldSenderId = "senderId"; |
880 | static const char* FieldTimestamp = "timestamp"; |
881 | +static const char* FieldDate = "date"; |
882 | static const char* FieldNewEvent = "newEvent"; |
883 | |
884 | // text event fields |