From c752ccafd8d826b3c0c9140e89ece09e90b34cb1 Mon Sep 17 00:00:00 2001
From: "Javier S. Pedro" <maemo@javispedro.com>
Date: Wed, 15 Aug 2012 15:48:47 +0200
Subject: avoid reloading watchlets when changing order only

---
 libsowatch/watchserver.cpp | 12 +++++++
 libsowatch/watchserver.h   |  1 +
 sowatchd/watchhandler.cpp  | 88 +++++++++++++++++++++++++++++++++-------------
 sowatchd/watchhandler.h    |  4 +++
 4 files changed, 80 insertions(+), 25 deletions(-)

diff --git a/libsowatch/watchserver.cpp b/libsowatch/watchserver.cpp
index 01af063..511c2c5 100644
--- a/libsowatch/watchserver.cpp
+++ b/libsowatch/watchserver.cpp
@@ -71,6 +71,18 @@ void WatchServer::insertWatchlet(int position, Watchlet *watchlet)
 	_watchletIds[id] = watchlet;
 }
 
+void WatchServer::moveWatchlet(const Watchlet *watchlet, int to)
+{
+	const QString id = watchlet->id();
+	int index = _watchlets.indexOf(const_cast<Watchlet*>(watchlet));
+
+	Q_ASSERT(watchlet->_server == this);
+	Q_ASSERT(_watchletIds.contains(id));
+	Q_ASSERT(index >= 0);
+
+	_watchlets.move(index, to);
+}
+
 void WatchServer::removeWatchlet(const Watchlet *watchlet)
 {
 	const QString id = watchlet->id();
diff --git a/libsowatch/watchserver.h b/libsowatch/watchserver.h
index 91f9b4e..67fcb81 100644
--- a/libsowatch/watchserver.h
+++ b/libsowatch/watchserver.h
@@ -35,6 +35,7 @@ public:
 
 	void addWatchlet(Watchlet *watchlet);
 	void insertWatchlet(int position, Watchlet *watchlet);
+	void moveWatchlet(const Watchlet *watchlet, int to);
 	void removeWatchlet(const Watchlet *watchlet);
 
 	void addProvider(NotificationProvider *provider);
diff --git a/sowatchd/watchhandler.cpp b/sowatchd/watchhandler.cpp
index b04c90b..4cd86d3 100644
--- a/sowatchd/watchhandler.cpp
+++ b/sowatchd/watchhandler.cpp
@@ -73,42 +73,80 @@ QString WatchHandler::status() const
 	}
 }
 
-void WatchHandler::updateWatchlets()
+Watchlet* WatchHandler::createWatchlet(const QString &id)
 {
 	Registry *registry = Registry::registry();
+	WatchletPluginInterface *plugin = registry->getWatchletPlugin(id);
+	if (!plugin) {
+		qWarning() << "Unknown watchlet" << id;
+		return 0;
+	}
 
-	if (!_server) return;
+	ConfigKey *subconfig = _config->getSubkey(id);
+	Watchlet* watchlet = plugin->getWatchlet(id, subconfig, _server);
+	delete subconfig;
 
-	QStringList newWatchlets = _config->value("watchlets").toStringList();
-	QStringList curWatchlets = _watchlet_order;
+	return watchlet;
+}
 
-	if (newWatchlets == curWatchlets) return; // Nothing to do
+void WatchHandler::deleteWatchletAt(int index)
+{
+	const QString id = _watchlet_order[index];
+	Watchlet *watchlet = _watchlets[id];
 
-	// TODO: Something better than removing all and readding
-	foreach (const QString& s, curWatchlets) {
-		Watchlet* watchlet = _watchlets[s];
-		_server->removeWatchlet(watchlet);
-		delete watchlet;
-	}
+	_server->removeWatchlet(watchlet);
+	_watchlet_order.removeAt(index);
+	_watchlets.remove(id);
 
-	_watchlet_order.clear();
-	_watchlets.clear();
+	delete watchlet;
+}
 
-	foreach (const QString& s, newWatchlets) {
-		WatchletPluginInterface *plugin = registry->getWatchletPlugin(s);
-		if (!plugin) {
-			qWarning() << "Unknown watchlet" << s;
-			continue;
+void WatchHandler::updateWatchlets()
+{
+	if (!_server) return;
+
+	QStringList newWatchlets = _config->value("watchlets").toStringList();
+
+	// Try to do one operation at the time
+	// (e.g. move a watchlet or add/remove it).
+	// Find the first difference
+	int i;
+	for (i = 0; i < newWatchlets.size(); i++) {
+		// Precondition: newWatchlets and curWatchlets are equal in range 0..i-1
+		if (i >= _watchlet_order.size()) {
+			// We need to add this watchlet
+			const QString id = newWatchlets[i];
+			Watchlet *watchlet = createWatchlet(id);
+			_watchlet_order << id;
+			_watchlets[id] = watchlet;
+			_server->addWatchlet(watchlet);
+		} else if (newWatchlets[i] != _watchlet_order[i]) {
+			// Let's find out if this watchlet has been moved, or removed.
+			const QString id = _watchlet_order[i];
+			int j;
+			for (j = i; j < newWatchlets.size(); j++) {
+				if (id == newWatchlets[j]) {
+					break; // Found
+				}
+			}
+			if (j == _watchlet_order.size()) {
+				// It was not found, so it has been removed
+				deleteWatchletAt(i);
+			} else {
+				// It has been found at index j, it needs to moved.
+				_watchlet_order.move(i, j);
+				_server->moveWatchlet(_watchlets[id], j);
+			}
 		}
-		ConfigKey *subconfig = _config->getSubkey(s);
-		Watchlet* watchlet = plugin->getWatchlet(s, subconfig, _server);
-		_watchlet_order << s;
-		_watchlets[s] = watchlet;
-		_server->addWatchlet(watchlet);
-		delete subconfig;
 	}
+	while (i < _watchlet_order.size()) {
+		// These watchlets are to be unloaded
+		deleteWatchletAt(i);
+	}
+
+	Q_ASSERT(newWatchlets == _watchlet_order);
 
-	qDebug() << "Watchlets reloaded: " << _watchlets.keys();
+	qDebug() << "New watchlet order: " << _watchlet_order;
 }
 
 void WatchHandler::updateProviders()
diff --git a/sowatchd/watchhandler.h b/sowatchd/watchhandler.h
index 39c1d17..83f0a67 100644
--- a/sowatchd/watchhandler.h
+++ b/sowatchd/watchhandler.h
@@ -22,6 +22,10 @@ public:
 signals:
 	void statusChanged();
 
+private:
+	Watchlet* createWatchlet(const QString& id);
+	void deleteWatchletAt(int index);
+
 private slots:
 	void updateWatchlets();
 	void updateProviders();
-- 
cgit v1.2.3