From 39fa663cd08bd2b7d46ed170d49ac794c531c42e Mon Sep 17 00:00:00 2001
From: "Javier S. Pedro" <maemo@javispedro.com>
Date: Fri, 10 Aug 2012 16:06:14 +0200
Subject: watchlet edit UI

---
 libsowatch/notificationplugininterface.h           |   2 -
 libsowatch/watchletplugininterface.h               |   7 +
 .../notificationswatchletplugin.cpp                |   9 ++
 .../notificationswatchletplugin.h                  |   3 +-
 sowatchui/main.cpp                                 |   2 +
 sowatchui/providersmodel.cc                        | 105 -------------
 sowatchui/providersmodel.cpp                       | 128 ++++++++++++++++
 sowatchui/providersmodel.h                         |   1 +
 sowatchui/qml/AddWatchletSheet.qml                 |  48 ++++++
 sowatchui/qml/WatchPage.qml                        |  62 +++++++-
 sowatchui/sowatchui.pro                            |   6 +-
 sowatchui/watchesmodel.cpp                         |  40 +++--
 sowatchui/watchesmodel.h                           |   2 +-
 sowatchui/watchletsmodel.cpp                       | 166 +++++++++++++++++++++
 sowatchui/watchletsmodel.h                         |  52 +++++++
 sysinfowatchlet/sysinfoplugin.cpp                  |  13 +-
 sysinfowatchlet/sysinfoplugin.h                    |   3 +-
 17 files changed, 518 insertions(+), 131 deletions(-)
 delete mode 100644 sowatchui/providersmodel.cc
 create mode 100644 sowatchui/providersmodel.cpp
 create mode 100644 sowatchui/qml/AddWatchletSheet.qml
 create mode 100644 sowatchui/watchletsmodel.cpp
 create mode 100644 sowatchui/watchletsmodel.h

diff --git a/libsowatch/notificationplugininterface.h b/libsowatch/notificationplugininterface.h
index fa75ddd..144e388 100644
--- a/libsowatch/notificationplugininterface.h
+++ b/libsowatch/notificationplugininterface.h
@@ -4,7 +4,6 @@
 #include <QtPlugin>
 #include <QtCore/QSettings>
 #include <QtCore/QStringList>
-#include <QtGui/QIcon>
 #include "sowatch_global.h"
 
 namespace sowatch
@@ -21,7 +20,6 @@ public:
 
 	struct NotificationProviderInfo {
 		QString name;
-		QIcon icon;
 	};
 
 	virtual QStringList providers() = 0;
diff --git a/libsowatch/watchletplugininterface.h b/libsowatch/watchletplugininterface.h
index ef03d81..31accb9 100644
--- a/libsowatch/watchletplugininterface.h
+++ b/libsowatch/watchletplugininterface.h
@@ -4,6 +4,7 @@
 #include <QtPlugin>
 #include <QtCore/QSettings>
 #include <QtCore/QStringList>
+#include <QtCore/QUrl>
 #include "sowatch_global.h"
 
 namespace sowatch
@@ -18,7 +19,13 @@ class SOWATCH_EXPORT WatchletPluginInterface
 public:
 	virtual ~WatchletPluginInterface();
 
+	struct WatchletInfo {
+		QString name;
+		QUrl icon;
+	};
+
 	virtual QStringList watchlets() = 0;
+	virtual WatchletInfo describeWatchlet(const QString& id) = 0;
 	virtual Watchlet* getWatchlet(const QString& id, ConfigKey *settings, WatchServer *server) = 0;
 };
 
diff --git a/notificationswatchlet/notificationswatchletplugin.cpp b/notificationswatchlet/notificationswatchletplugin.cpp
index 5f4875f..d5ba173 100644
--- a/notificationswatchlet/notificationswatchletplugin.cpp
+++ b/notificationswatchlet/notificationswatchletplugin.cpp
@@ -19,6 +19,15 @@ QStringList NotificationsWatchletPlugin::watchlets()
 	return l;
 }
 
+WatchletPluginInterface::WatchletInfo NotificationsWatchletPlugin::describeWatchlet(const QString &id)
+{
+	WatchletInfo info;
+	if (id != "com.javispedro.sowatch.notifications") return info;
+	info.name = "Pending notifications";
+	info.icon = QUrl::fromLocalFile(SOWATCH_QML_DIR "/notificationswatchlet/icon.png");
+	return info;
+}
+
 Watchlet* NotificationsWatchletPlugin::getWatchlet(const QString& driver, ConfigKey *settings, WatchServer *server)
 {
 	Q_UNUSED(driver);
diff --git a/notificationswatchlet/notificationswatchletplugin.h b/notificationswatchlet/notificationswatchletplugin.h
index bc305fc..7efbc1c 100644
--- a/notificationswatchlet/notificationswatchletplugin.h
+++ b/notificationswatchlet/notificationswatchletplugin.h
@@ -16,7 +16,8 @@ public:
 	~NotificationsWatchletPlugin();
 
 	QStringList watchlets();
-	Watchlet* getWatchlet(const QString& driver, ConfigKey *settings, WatchServer* server);
+	WatchletInfo describeWatchlet(const QString &id);
+	Watchlet* getWatchlet(const QString& id, ConfigKey *settings, WatchServer* server);
 };
 
 }
diff --git a/sowatchui/main.cpp b/sowatchui/main.cpp
index 5e116ac..80a0f6b 100644
--- a/sowatchui/main.cpp
+++ b/sowatchui/main.cpp
@@ -7,6 +7,7 @@
 #include "watchesmodel.h"
 #include "watchscannermodel.h"
 #include "providersmodel.h"
+#include "watchletsmodel.h"
 
 static sowatch::Registry *registry;
 static WatchesModel *watches;
@@ -26,6 +27,7 @@ Q_DECL_EXPORT int main(int argc, char *argv[])
 	qmlRegisterType<sowatch::ConfigKey>();
 	qmlRegisterType<sowatch::GConfKey>("com.javispedro.sowatch", 1, 0, "GConfKey");
 	qmlRegisterType<ProvidersModel>("com.javispedro.sowatch", 1, 0, "ProvidersModel");
+	qmlRegisterType<WatchletsModel>("com.javispedro.sowatch", 1, 0, "WatchletsModel");
 
 	viewer->rootContext()->setContextProperty("watches", watches);
 	viewer->rootContext()->setContextProperty("watchScanner", watchScanner);
diff --git a/sowatchui/providersmodel.cc b/sowatchui/providersmodel.cc
deleted file mode 100644
index 68ce21b..0000000
--- a/sowatchui/providersmodel.cc
+++ /dev/null
@@ -1,105 +0,0 @@
-#include <QtDebug>
-
-#include "providersmodel.h"
-
-using namespace sowatch;
-
-ProvidersModel::ProvidersModel(QObject *parent) :
-    QAbstractListModel(parent),
-    _config(0)
-{
-	QHash<int, QByteArray> roles = roleNames();
-	roles[Qt::DisplayRole] = QByteArray("title");
-	roles[NameRole] = QByteArray("name");
-	roles[EnabledRole] = QByteArray("enabled");
-	setRoleNames(roles);
-}
-
-QString ProvidersModel::configKey() const
-{
-	if (_config) {
-		return _config->key();
-	} else {
-		return QString();
-	}
-}
-
-void ProvidersModel::setConfigKey(const QString &configKey)
-{
-	QString oldConfigKey = this->configKey();
-	if (_config) {
-		delete _config;
-		_config = 0;
-	}
-	if (!configKey.isEmpty()) {
-		_config = new GConfKey(configKey, this);
-		connect(_config, SIGNAL(changed()), SLOT(reload()));
-	}
-	if (this->configKey() != oldConfigKey) {
-		reload();
-		emit configKeyChanged();
-	}
-}
-
-int ProvidersModel::rowCount(const QModelIndex &parent) const
-{
-	return _all_list.count();
-}
-
-QVariant ProvidersModel::data(const QModelIndex &index, int role) const
-{
-	switch (role) {
-	case Qt::DisplayRole:
-		return QVariant::fromValue(_info_list[index.row()].name);
-	case NameRole:
-		return QVariant::fromValue(_all_list[index.row()]);
-	case EnabledRole:
-		return QVariant::fromValue(_enabled.contains(_all_list[index.row()]));
-	}
-	return QVariant();
-}
-
-bool ProvidersModel::setData(const QModelIndex &index, const QVariant &value, int role)
-{
-	switch (role) {
-	case EnabledRole:
-		setProviderEnabled(_all_list[index.row()], value.toBool());
-		return true;
-	}
-	return false;
-}
-
-void ProvidersModel::setProviderEnabled(const QString &id, bool enabled)
-{
-	if (enabled) {
-		_enabled.insert(id);
-	} else {
-		_enabled.remove(id);
-	}
-	_config->set(QVariant::fromValue(QStringList(_enabled.toList())));
-}
-
-void ProvidersModel::reload()
-{
-	Registry *registry = Registry::registry();
-	beginResetModel();
-	_all_list.clear();
-	_info_list.clear();
-	_enabled.clear();
-
-	_all_list = registry->allNotificationProviders();
-	_info_list.reserve(_all_list.size());
-	foreach (const QString& s, _all_list) {
-		NotificationPluginInterface *plugin = registry->getNotificationPlugin(s);
-		if (plugin) {
-			_info_list.append(plugin->describeProvider(s));
-		} else {
-			NotificationPluginInterface::NotificationProviderInfo info;
-			info.name = s;
-			_info_list.append(info);
-		}
-	}
-
-	_enabled = _config->value().toStringList().toSet();
-	endResetModel();
-}
diff --git a/sowatchui/providersmodel.cpp b/sowatchui/providersmodel.cpp
new file mode 100644
index 0000000..8e93e5c
--- /dev/null
+++ b/sowatchui/providersmodel.cpp
@@ -0,0 +1,128 @@
+#include <QtDebug>
+
+#include "providersmodel.h"
+
+using namespace sowatch;
+
+static const QString providersSubKey("/providers");
+
+ProvidersModel::ProvidersModel(QObject *parent) :
+    QAbstractListModel(parent),
+    _config(0)
+{
+	QHash<int, QByteArray> roles = roleNames();
+	roles[Qt::DisplayRole] = QByteArray("title");
+	roles[NameRole] = QByteArray("name");
+	roles[EnabledRole] = QByteArray("enabled");
+	setRoleNames(roles);
+}
+
+QString ProvidersModel::configKey() const
+{
+	if (_config) {
+		QString key = _config->key();
+		return key.left(key.length() - providersSubKey.length());
+	} else {
+		return QString();
+	}
+}
+
+void ProvidersModel::setConfigKey(const QString &configKey)
+{
+	QString oldConfigKey = this->configKey();
+	if (_config) {
+		delete _config;
+		_config = 0;
+	}
+	if (!configKey.isEmpty()) {
+		_config = new GConfKey(configKey + providersSubKey, this);
+		connect(_config, SIGNAL(changed()), SLOT(handleConfigChanged()));
+	}
+	if (this->configKey() != oldConfigKey) {
+		reload();
+		emit configKeyChanged();
+	}
+}
+
+int ProvidersModel::rowCount(const QModelIndex &parent) const
+{
+	Q_UNUSED(parent);
+	return _all_list.count();
+}
+
+QVariant ProvidersModel::data(const QModelIndex &index, int role) const
+{
+	switch (role) {
+	case Qt::DisplayRole:
+		return QVariant::fromValue(_info_list[index.row()].name);
+	case NameRole:
+		return QVariant::fromValue(_all_list[index.row()]);
+	case EnabledRole:
+		return QVariant::fromValue(_enabled.contains(_all_list[index.row()]));
+	}
+	return QVariant();
+}
+
+bool ProvidersModel::setData(const QModelIndex &index, const QVariant &value, int role)
+{
+	switch (role) {
+	case EnabledRole:
+		setProviderEnabled(_all_list[index.row()], value.toBool());
+		return true;
+	}
+	return false;
+}
+
+void ProvidersModel::setProviderEnabled(const QString &id, bool enabled)
+{
+	if (enabled) {
+		_enabled.insert(id);
+	} else {
+		_enabled.remove(id);
+	}
+
+	qDebug() << "Storing providers" << _enabled;
+
+	_config->set(QVariant::fromValue(QStringList(_enabled.toList())));
+}
+
+void ProvidersModel::reload()
+{
+	Registry *registry = Registry::registry();
+	beginResetModel();
+	_all_list.clear();
+	_info_list.clear();
+	_enabled.clear();
+
+	qDebug() << "Reloading providers";
+
+	_all_list = registry->allNotificationProviders();
+	_info_list.reserve(_all_list.size());
+	_all_list.sort();
+	foreach (const QString& s, _all_list) {
+		NotificationPluginInterface *plugin = registry->getNotificationPlugin(s);
+		if (plugin) {
+			_info_list.append(plugin->describeProvider(s));
+		} else {
+			NotificationPluginInterface::NotificationProviderInfo info;
+			info.name = s;
+			_info_list.append(info);
+		}
+	}
+
+	_enabled = _config->value().toStringList().toSet();
+	endResetModel();
+}
+
+void ProvidersModel::handleConfigChanged()
+{
+	QSet<QString> prevEnabled = _enabled;
+	_enabled = _config->value().toStringList().toSet();
+
+	for (int i = 0; i < _all_list.count(); i++) {
+		const QString& id = _all_list[i];
+		if (_enabled.contains(id) != prevEnabled.contains(id)) {
+			emit dataChanged(createIndex(i, 0), createIndex(i, 0));
+		}
+	}
+}
diff --git a/sowatchui/providersmodel.h b/sowatchui/providersmodel.h
index 9cf6ec6..bcde2d5 100644
--- a/sowatchui/providersmodel.h
+++ b/sowatchui/providersmodel.h
@@ -33,6 +33,7 @@ signals:
 	
 private slots:
 	void reload();
+	void handleConfigChanged();
 
 private:
 	sowatch::ConfigKey *_config;
diff --git a/sowatchui/qml/AddWatchletSheet.qml b/sowatchui/qml/AddWatchletSheet.qml
new file mode 100644
index 0000000..e052350
--- /dev/null
+++ b/sowatchui/qml/AddWatchletSheet.qml
@@ -0,0 +1,48 @@
+import QtQuick 1.1
+import com.nokia.meego 1.1
+import com.nokia.extras 1.1
+import com.javispedro.sowatch 1.0
+
+Sheet {
+	id: sheet
+	anchors.margins: UiConstants.DefaultMargin
+
+	property string configKey;
+
+	rejectButtonText: qsTr("Cancel")
+
+	content: ListView {
+		id: listView
+		anchors.fill: parent
+		anchors.margins: UiConstants.DefaultMargin
+
+		flickableDirection: Flickable.VerticalFlick
+
+		model: WatchletsModel {
+			id: watchletsModel
+			configKey: sheet.configKey
+			displayUnadded: true
+		}
+
+		header: Column {
+			width: parent.width
+
+			GroupHeader {
+				width: parent.width
+				text: qsTr("Available watchlets")
+			}
+		}
+
+		delegate: ListDelegate {
+			onClicked: {
+				watchletsModel.addWatchlet(model.name);
+				close();
+				accepted();
+			}
+		}
+
+		ScrollDecorator {
+			flickableItem: listView
+		}
+	}
+}
diff --git a/sowatchui/qml/WatchPage.qml b/sowatchui/qml/WatchPage.qml
index 21c4543..4799085 100644
--- a/sowatchui/qml/WatchPage.qml
+++ b/sowatchui/qml/WatchPage.qml
@@ -14,10 +14,55 @@ Page {
 
 	tools: ToolBarLayout {
 		ToolIcon {
+			id: backToolIcon
 			platformIconId: "toolbar-back"
 			anchors.left: parent.left
 			onClicked: pageStack.pop()
 		}
+		ToolIcon {
+			id: menuToolIcon
+			platformIconId: "toolbar-view-menu"
+			anchors.right: parent.right
+			onClicked: (watchMenu.status === DialogStatus.Closed) ? watchMenu.open() : watchMenu.close();
+		}
+	}
+
+	Menu {
+		id: watchMenu
+		MenuLayout {
+			MenuItem {
+				text: qsTr("Remove watch")
+				onClicked: {
+					watchMenu.close();
+					watches.removeWatch(watchPage.configKey);
+					pageStack.pop();
+				}
+			}
+		}
+	}
+
+	Menu {
+		id: watchletMenu
+		property string watchlet;
+		MenuLayout {
+			MenuItem {
+				text: qsTr("Move up")
+				onClicked: watchletsModel.moveWatchletUp(watchletMenu.watchlet)
+			}
+			MenuItem {
+				text: qsTr("Move down")
+				onClicked: watchletsModel.moveWatchletDown(watchletMenu.watchlet)
+			}
+			MenuItem {
+				text: qsTr("Remove watchlet")
+				onClicked: watchletsModel.removeWatchlet(watchletMenu.watchlet)
+			}
+		}
+	}
+
+	AddWatchletSheet {
+		id: addWatchletSheet
+		configKey: watchPage.configKey
 	}
 
 	GConfKey {
@@ -84,7 +129,7 @@ Page {
 				height: UiConstants.ListItemHeightDefault * count
 				model: ProvidersModel {
 					id: providersModel
-					configKey: watchPage.configKey + "/providers"
+					configKey: watchPage.configKey
 				}
 				delegate: ListDelegate {
 					CheckBox {
@@ -107,19 +152,24 @@ Page {
 				interactive: false
 				width: parent.width
 				height: UiConstants.ListItemHeightDefault * count
-				model: ListModel {
-					ListElement {
-						title: "Test"
-					}
+				model: WatchletsModel {
+					id: watchletsModel
+					configKey: watchPage.configKey
+					displayUnadded: false
 				}
-				delegate: ListDelegate {
 
+				delegate: ListDelegate {
+					onClicked: {
+						watchletMenu.watchlet = model.name;
+						watchletMenu.open();
+					}
 				}
 			}
 
 			Button {
 				anchors.horizontalCenter: parent.horizontalCenter
 				text: qsTr("Add new watchlet")
+				onClicked: addWatchletSheet.open()
 			}
 		}
 	}
diff --git a/sowatchui/sowatchui.pro b/sowatchui/sowatchui.pro
index fa965d3..4098228 100644
--- a/sowatchui/sowatchui.pro
+++ b/sowatchui/sowatchui.pro
@@ -38,12 +38,14 @@ DEPENDPATH += $$PWD/../libsowatch
 SOURCES += main.cpp \
     watchesmodel.cpp daemonproxy.cpp \
     watchscannermodel.cpp \
-    providersmodel.cc
+    providersmodel.cpp \
+    watchletsmodel.cpp
 
 HEADERS += \
     watchesmodel.h daemonproxy.h \
     watchscannermodel.h \
-    providersmodel.h
+    providersmodel.h \
+    watchletsmodel.h
 
 OTHER_FILES += qml/main.qml \
 	qml/MainPage.qml \
diff --git a/sowatchui/watchesmodel.cpp b/sowatchui/watchesmodel.cpp
index 302e484..f8afcd4 100644
--- a/sowatchui/watchesmodel.cpp
+++ b/sowatchui/watchesmodel.cpp
@@ -81,14 +81,6 @@ QVariant WatchesModel::data(const QModelIndex &index, int role) const
 	return QVariant();
 }
 
-bool WatchesModel::removeRows(int row, int count, const QModelIndex &parent)
-{
-	Q_UNUSED(row);
-	Q_UNUSED(count);
-	Q_UNUSED(parent);
-	return false; // TODO
-}
-
 void WatchesModel::addFoundWatch(const QVariantMap &info)
 {
 	QStringList existing = _config->dirs();
@@ -109,9 +101,8 @@ void WatchesModel::addFoundWatch(const QVariantMap &info)
 
 	// Set some defaults
 	Registry *registry = Registry::registry();
-	foreach (const QString& providerId, registry->allNotificationProviders()) {
-		qDebug() << "Would add" << providerId;
-	}
+	newkey->set("providers", registry->allNotificationProviders());
+	newkey->set("watchlets", registry->allWatchlets());
 
 	// Now add to the watches list
 	QStringList active = _watches_list->value().toStringList();
@@ -120,6 +111,33 @@ void WatchesModel::addFoundWatch(const QVariantMap &info)
 
 	// Now enable
 	newkey->set("enable", true);
+
+	delete newkey;
+}
+
+void WatchesModel::removeWatch(const QString &id)
+{
+	QString name;
+
+	int i = id.lastIndexOf('/');
+	if (i != -1) {
+		// Hack: QML UI might pass /apps/sowatch/id instead of id alone
+		name = id.mid(i + 1);
+	} else {
+		name = id;
+	}
+
+	ConfigKey *subkey = _config->getSubkey(name);
+
+	qDebug() << "Deleting watch" << name;
+
+	// Remove from watches list
+	QStringList active = _watches_list->value().toStringList();
+	active.removeAll(name);
+	_watches_list->set(active);
+
+	subkey->recursiveUnset();
+	delete subkey;
 }
 
 void WatchesModel::reload()
diff --git a/sowatchui/watchesmodel.h b/sowatchui/watchesmodel.h
index 6e9b65e..46114e5 100644
--- a/sowatchui/watchesmodel.h
+++ b/sowatchui/watchesmodel.h
@@ -22,10 +22,10 @@ public:
 
 	int rowCount(const QModelIndex &parent) const;
 	QVariant data(const QModelIndex &index, int role) const;
-	bool removeRows(int row, int count, const QModelIndex &parent);
 
 public slots:
 	void addFoundWatch(const QVariantMap& info);
+	void removeWatch(const QString& id);
 
 private slots:
 	void reload();
diff --git a/sowatchui/watchletsmodel.cpp b/sowatchui/watchletsmodel.cpp
new file mode 100644
index 0000000..d9c8f86
--- /dev/null
+++ b/sowatchui/watchletsmodel.cpp
@@ -0,0 +1,166 @@
+#include "watchletsmodel.h"
+
+using namespace sowatch;
+
+static const QString watchletsSubKey("/watchlets");
+
+WatchletsModel::WatchletsModel(QObject *parent) :
+    QAbstractListModel(parent),
+    _config(0),
+    _unadded(false)
+{
+	QHash<int, QByteArray> roles = roleNames();
+	roles[Qt::DisplayRole] = QByteArray("title");
+	roles[Qt::DecorationRole] = QByteArray("iconSource");
+	roles[NameRole] = QByteArray("name");
+	setRoleNames(roles);
+}
+
+QString WatchletsModel::configKey() const
+{
+	if (_config) {
+		QString key = _config->key();
+		return key.left(key.length() - watchletsSubKey.length());
+	} else {
+		return QString();
+	}
+}
+
+void WatchletsModel::setConfigKey(const QString &configKey)
+{
+	qDebug() << "Set confgKey" << configKey;
+	QString oldConfigKey = this->configKey();
+	if (_config) {
+		delete _config;
+		_config = 0;
+	}
+	if (!configKey.isEmpty()) {
+		_config = new GConfKey(configKey + watchletsSubKey, this);
+		connect(_config, SIGNAL(changed()), SLOT(handleConfigChanged()));
+	}
+	if (this->configKey() != oldConfigKey) {
+		reload();
+		emit configKeyChanged();
+	}
+}
+
+bool WatchletsModel::displayUnadded() const
+{
+	return _unadded;
+}
+
+void WatchletsModel::setDisplayUnadded(bool displayUnadded)
+{
+	qDebug() << "Set dunadded" << displayUnadded;
+	_unadded = displayUnadded;
+	if (_config) reload();
+	emit displayUnaddedChanged();
+}
+
+int WatchletsModel::rowCount(const QModelIndex &parent) const
+{
+	Q_UNUSED(parent);
+	return _list.count();
+}
+
+QVariant WatchletsModel::data(const QModelIndex &index, int role) const
+{
+	const QString id = _list[index.row()];
+	switch (role) {
+	case Qt::DisplayRole:
+		return QVariant::fromValue(_info[id].name);
+	case Qt::DecorationRole:
+		return QVariant::fromValue(_info[id].icon);
+	case NameRole:
+		return QVariant::fromValue(id);
+	}
+	return QVariant();
+}
+
+void WatchletsModel::addWatchlet(const QString &name)
+{
+	if (!_config) return;
+	QStringList enabled = _config->value().toStringList();
+	if (_enabled.contains(name)) return;
+	enabled << name;
+	_config->set(enabled);
+}
+
+void WatchletsModel::removeWatchlet(const QString &name)
+{
+	if (!_config) return;
+	QStringList enabled = _config->value().toStringList();
+	enabled.removeAll(name);
+	_config->set(enabled);
+}
+
+void WatchletsModel::moveWatchletUp(const QString &name)
+{
+	if (!_config) return;
+	QStringList enabled = _config->value().toStringList();
+	int index = enabled.indexOf(name);
+	qDebug() << "move up" << enabled << index << enabled[index];
+	if (index > 0 && index < enabled.size()) {
+		enabled.swap(index - 1, index);
+	}
+	_config->set(enabled);
+}
+
+void WatchletsModel::moveWatchletDown(const QString &name)
+{
+	if (!_config) return;
+	QStringList enabled = _config->value().toStringList();
+	int index = enabled.indexOf(name);
+	qDebug() << "move down" << enabled << index << enabled[index];
+	if (index >= 0 && index < enabled.size() - 1) {
+		enabled.swap(index, index + 1);
+	}
+	_config->set(enabled);
+}
+
+void WatchletsModel::reload()
+{
+	Registry *registry = Registry::registry();
+	Q_ASSERT(_config);
+	beginResetModel();
+	_list.clear();
+	_info.clear();
+	_enabled.clear();
+
+	qDebug() << "Reloading watchlets";
+
+	QStringList all = registry->allWatchlets();
+	foreach (const QString& s, all) {
+		WatchletPluginInterface *plugin = registry->getWatchletPlugin(s);
+		if (plugin) {
+			_info[s] = plugin->describeWatchlet(s);
+		} else {
+			WatchletPluginInterface::WatchletInfo info;
+			info.name = s;
+			_info[s] = info;
+		}
+	}
+
+	QStringList enabled = _config->value().toStringList();
+	_enabled = enabled.toSet();
+
+	if (_unadded) {
+		qDebug() << "Listing unadded watchlets from" << all;
+		foreach (const QString& s, all) {
+			if (!_enabled.contains(s)) {
+				_list.append(s);
+			}
+		}
+		_list.sort();
+	} else {
+		qDebug() << "Listing added watchlets from" << enabled;
+		_list = enabled;
+	}
+	endResetModel();
+}
+
+void WatchletsModel::handleConfigChanged()
+{
+	// TODO
+	reload();
+}
diff --git a/sowatchui/watchletsmodel.h b/sowatchui/watchletsmodel.h
new file mode 100644
index 0000000..e0c2c62
--- /dev/null
+++ b/sowatchui/watchletsmodel.h
@@ -0,0 +1,52 @@
+#ifndef WATCHLETSMODEL_H
+#define WATCHLETSMODEL_H
+
+#include <QAbstractListModel>
+
+#include <sowatch.h>
+
+class WatchletsModel : public QAbstractListModel
+{
+	Q_OBJECT
+	Q_PROPERTY(QString configKey READ configKey WRITE setConfigKey NOTIFY configKeyChanged)
+	Q_PROPERTY(bool displayUnadded READ displayUnadded WRITE setDisplayUnadded NOTIFY displayUnaddedChanged)
+
+public:
+	explicit WatchletsModel(QObject *parent = 0);
+
+	enum DataRoles {
+		NameRole = Qt::UserRole
+	};
+
+	QString configKey() const;
+	void setConfigKey(const QString& configKey);
+
+	bool displayUnadded() const;
+	void setDisplayUnadded(bool displayUnadded);
+
+	int rowCount(const QModelIndex &parent) const;
+	QVariant data(const QModelIndex &index, int role) const;
+
+public slots:
+	void addWatchlet(const QString& name);
+	void removeWatchlet(const QString& name);
+	void moveWatchletUp(const QString& name);
+	void moveWatchletDown(const QString& name);
+
+signals:
+	void configKeyChanged();
+	void displayUnaddedChanged();
+
+private slots:
+	void reload();
+	void handleConfigChanged();
+
+private:
+	sowatch::ConfigKey *_config;
+	bool _unadded;
+	QStringList _list;
+	QMap<QString, sowatch::WatchletPluginInterface::WatchletInfo> _info;
+	QSet<QString> _enabled;
+};
+
+#endif // WATCHLETSMODEL_H
diff --git a/sysinfowatchlet/sysinfoplugin.cpp b/sysinfowatchlet/sysinfoplugin.cpp
index f24b23e..3e56307 100644
--- a/sysinfowatchlet/sysinfoplugin.cpp
+++ b/sysinfowatchlet/sysinfoplugin.cpp
@@ -19,9 +19,18 @@ QStringList SysInfoPlugin::watchlets()
 	return l;
 }
 
-Watchlet* SysInfoPlugin::getWatchlet(const QString& driver, ConfigKey *settings, WatchServer *server)
+SysInfoPlugin::WatchletInfo SysInfoPlugin::describeWatchlet(const QString &id)
 {
-	Q_UNUSED(driver);
+	WatchletInfo info;
+	if (id != "com.javispedro.sowatch.sysinfo") return info;
+	info.name = "Phone info";
+	info.icon = QUrl::fromLocalFile(SOWATCH_QML_DIR "/sysinfowatchlet/icon.png");
+	return info;
+}
+
+Watchlet* SysInfoPlugin::getWatchlet(const QString& id, ConfigKey *settings, WatchServer *server)
+{
+	Q_UNUSED(id);
 	Q_UNUSED(settings);
 	return new SysInfoWatchlet(server);
 }
diff --git a/sysinfowatchlet/sysinfoplugin.h b/sysinfowatchlet/sysinfoplugin.h
index b341614..8b37a1c 100644
--- a/sysinfowatchlet/sysinfoplugin.h
+++ b/sysinfowatchlet/sysinfoplugin.h
@@ -16,7 +16,8 @@ public:
 	~SysInfoPlugin();
 
 	QStringList watchlets();
-	Watchlet* getWatchlet(const QString& driver, ConfigKey *settings, WatchServer* server);
+	WatchletInfo describeWatchlet(const QString &id);
+	Watchlet* getWatchlet(const QString &id, ConfigKey *settings, WatchServer *server);
 };
 
 }
-- 
cgit v1.2.3