summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
Diffstat (limited to 'Swift/Controllers/Roster/TableRoster.cpp')
-rw-r--r--Swift/Controllers/Roster/TableRoster.cpp127
1 files changed, 117 insertions, 10 deletions
diff --git a/Swift/Controllers/Roster/TableRoster.cpp b/Swift/Controllers/Roster/TableRoster.cpp
index fda7de8..66e3a85 100644
--- a/Swift/Controllers/Roster/TableRoster.cpp
+++ b/Swift/Controllers/Roster/TableRoster.cpp
@@ -7,30 +7,137 @@
#include <Swift/Controllers/Roster/TableRoster.h>
#include <boost/cast.hpp>
+#include <cassert>
+#include <algorithm>
+#include <Swiften/Base/foreach.h>
+#include <Swiften/Network/TimerFactory.h>
+#include <Swiften/Network/Timer.h>
#include <Swift/Controllers/Roster/Roster.h>
#include <Swift/Controllers/Roster/GroupRosterItem.h>
+#include <Swift/Controllers/Roster/LeastCommonSubsequence.h>
+
+namespace Swift {
+ struct SectionNameEquals {
+ bool operator()(const TableRoster::Section& s1, const TableRoster::Section& s2) const {
+ return s1.name == s2.name;
+ }
+ };
+
+ template<typename T>
+ struct True {
+ bool operator()(const T&, const T&) const {
+ return true;
+ }
+ };
+
+ struct ItemEquals {
+ bool operator()(const TableRoster::Item& i1, const TableRoster::Item& i2) const {
+ return i1.jid == i2.jid;
+ }
+ };
+
+
+ struct ItemNeedsUpdate {
+ bool operator()(const TableRoster::Item& i1, const TableRoster::Item& i2) const {
+ return i1.description != i2.description || i1.name != i2.name;
+ }
+ };
+
+ struct CreateIndexForSection {
+ CreateIndexForSection(size_t section) : section(section) {
+ }
+
+ TableRoster::Index operator()(size_t row) const {
+ return TableRoster::Index(section, row);
+ }
+
+ size_t section;
+ };
+}
using namespace Swift;
-TableRoster::TableRoster(Roster* model) : model(model) {
+TableRoster::TableRoster(Roster* model, TimerFactory* timerFactory, int updateDelay) : model(model) {
+ updateTimer = timerFactory->createTimer(updateDelay);
+ model->onChildrenChanged.connect(boost::bind(&TableRoster::scheduleUpdate, this));
+ model->onGroupAdded.connect(boost::bind(&TableRoster::scheduleUpdate, this));
+ model->onDataChanged.connect(boost::bind(&TableRoster::scheduleUpdate, this));
+}
+
+TableRoster::~TableRoster() {
+ model->onDataChanged.disconnect(boost::bind(&TableRoster::scheduleUpdate, this));
+ model->onGroupAdded.disconnect(boost::bind(&TableRoster::scheduleUpdate, this));
+ model->onChildrenChanged.disconnect(boost::bind(&TableRoster::scheduleUpdate, this));
+ updateTimer->stop();
}
size_t TableRoster::getNumberOfSections() const {
- return model ? model->getRoot()->getDisplayedChildren().size() : 0;
+ return sections.size();
}
-size_t TableRoster::getNumberOfRowsInSection(size_t section) const {
- return boost::polymorphic_downcast<Swift::GroupRosterItem*>(model->getRoot()->getDisplayedChildren()[section])->getDisplayedChildren().size();
+const std::string& TableRoster::getSectionTitle(size_t section) {
+ return sections[section].name;
}
-const std::string& TableRoster::getSectionTitle(size_t section) {
- return model->getRoot()->getDisplayedChildren()[section]->getDisplayName();
+size_t TableRoster::getNumberOfRowsInSection(size_t section) const {
+ return sections[section].items.size();
}
TableRoster::Item TableRoster::getItem(const Index& index) const {
- Item item;
- item.name = boost::polymorphic_downcast<Swift::GroupRosterItem*>(model->getRoot()->getDisplayedChildren().at(index.section))->getDisplayedChildren().at(index.row)->getDisplayName();
- item.description = "My Status";
- return item;
+ return sections[index.section].items[index.row];
+}
+
+void TableRoster::handleUpdateTimerTick() {
+ updateTimer->stop();
+ updatePending = false;
+
+ // Get a model for the new roster
+ std::vector<Section> newSections;
+ foreach(RosterItem* item, model->getRoot()->getDisplayedChildren()) {
+ if (GroupRosterItem* groupItem = boost::polymorphic_downcast<GroupRosterItem*>(item)) {
+ Section section(groupItem->getDisplayName());
+ foreach(RosterItem* groupChildItem, groupItem->getDisplayedChildren()) {
+ if (ContactRosterItem* contact = boost::polymorphic_downcast<ContactRosterItem*>(groupChildItem)) {
+ section.items.push_back(Item(contact->getDisplayName(), contact->getStatusText(), contact->getDisplayJID()));
+ }
+ }
+ newSections.push_back(section);
+ }
+ }
+
+ // Do a diff with the previous roster
+ Update update;
+ std::vector<size_t> sectionUpdates;
+ std::vector<size_t> sectionPostUpdates;
+ computeIndexDiff<Section,SectionNameEquals,True<Section> >(sections, newSections, sectionUpdates, sectionPostUpdates, update.deletedSections, update.insertedSections);
+ assert(sectionUpdates.size() == sectionPostUpdates.size());
+ for (size_t i = 0; i < sectionUpdates.size(); ++i) {
+ assert(sectionUpdates[i] < sections.size());
+ assert(sectionPostUpdates[i] < newSections.size());
+ std::vector<size_t> itemUpdates;
+ std::vector<size_t> itemPostUpdates;
+ std::vector<size_t> itemRemoves;
+ std::vector<size_t> itemInserts;
+ computeIndexDiff<Item, ItemEquals, ItemNeedsUpdate >(sections[sectionUpdates[i]].items, newSections[sectionPostUpdates[i]].items, itemUpdates, itemPostUpdates, itemRemoves, itemInserts);
+ update.insertedRows.resize(itemInserts.size());
+ std::transform(itemInserts.begin(), itemInserts.end(), update.insertedRows.begin(), CreateIndexForSection(sectionPostUpdates[i]));
+ update.deletedRows.resize(itemRemoves.size());
+ std::transform(itemRemoves.begin(), itemRemoves.end(), update.deletedRows.begin(), CreateIndexForSection(sectionPostUpdates[i]));
+ update.updatedRows.resize(itemUpdates.size());
+ std::transform(itemUpdates.begin(), itemUpdates.end(), update.updatedRows.begin(), CreateIndexForSection(sectionPostUpdates[i]));
+ }
+
+ // Switch the old model with the new
+ sections.swap(newSections);
+
+ // Emit the update
+ onUpdate(update);
+}
+
+void TableRoster::scheduleUpdate() {
+ if (!updatePending) {
+ updatePending = true;
+ updateTimer->start();
+ }
}