From c0e50601ac8d0bd3bf087dfc01e92d5b83a709f7 Mon Sep 17 00:00:00 2001
From: Christian Kandeler <christian.kandeler@nokia.com>
Date: Tue, 13 Mar 2012 14:31:14 +0100
Subject: [PATCH] DeviceManager: Remember auto-detected devices.

The current implementation "forgets" auto-detected devices when
they've been disconnected or Creator has been closed.
This has the drawback that potential changes by users to the connection
parameters cannot become persistent. This is overcome by introducing a
device fingerprint and keeping removed auto-connected devices in a
special list from which they can later be retrieved, typically when the
device gets connected again.

Change-Id: I98cfd25c677e6a2a46891a0facf3a28f0a0c3465
Reviewed-by: Nikolai Kosjar <nikolai.kosjar@nokia.com>
---
 .../devicesupport/devicemanager.cpp           | 50 ++++++++++++++++---
 .../devicesupport/devicemanager.h             |  3 ++
 .../devicesupport/devicesettingswidget.cpp    |  4 +-
 .../projectexplorer/devicesupport/idevice.cpp | 30 ++++++++++-
 .../projectexplorer/devicesupport/idevice.h   |  3 +-
 .../remotelinux/linuxdeviceconfiguration.cpp  |  8 +--
 .../remotelinux/linuxdeviceconfiguration.h    |  4 +-
 7 files changed, 85 insertions(+), 17 deletions(-)

diff --git a/src/plugins/projectexplorer/devicesupport/devicemanager.cpp b/src/plugins/projectexplorer/devicesupport/devicemanager.cpp
index 0338d282e51..d3ec14e8a70 100644
--- a/src/plugins/projectexplorer/devicesupport/devicemanager.cpp
+++ b/src/plugins/projectexplorer/devicesupport/devicemanager.cpp
@@ -52,6 +52,18 @@
 namespace ProjectExplorer {
 namespace Internal {
 
+static IDevice::Ptr findAutoDetectedDevice(const QList<IDevice::Ptr> &deviceList,
+        const QString &type, const QString &fingerprint)
+{
+    foreach (const IDevice::Ptr &device, deviceList) {
+        if (device->isAutoDetected() && device->type() == type
+                && device->fingerprint() == fingerprint) {
+            return device;
+        }
+    }
+    return IDevice::Ptr();
+}
+
 const char DeviceManagerKey[] = "DeviceManager";
 const char DeviceListKey[] = "DeviceList";
 const char DefaultDevicesKey[] = "DefaultDevices";
@@ -62,6 +74,7 @@ public:
     static DeviceManager *instance;
     static DeviceManager *clonedInstance;
     QList<IDevice::Ptr> devices;
+    QList<IDevice::Ptr> inactiveAutoDetectedDevices;
     QHash<QString, IDevice::Id> defaultDevices;
 };
 DeviceManager *DeviceManagerPrivate::instance = 0;
@@ -192,7 +205,10 @@ void DeviceManager::fromMap(const QVariantMap &map)
         QTC_ASSERT(device, continue);
         if (device->internalId() == IDevice::invalidId())
             device->setInternalId(unusedId());
-        d->devices << device;
+        if (device->isAutoDetected())
+            d->inactiveAutoDetectedDevices << device;
+        else
+            d->devices << device;
     }
 }
 
@@ -207,10 +223,10 @@ QVariantMap DeviceManager::toMap() const
     }
     map.insert(QLatin1String(DefaultDevicesKey), defaultDeviceMap);
     QVariantList deviceList;
-    foreach (const IDevice::ConstPtr &device, d->devices) {
-        if (!device->isAutoDetected())
-            deviceList << device->toMap();
-    }
+    foreach (const IDevice::ConstPtr &device, d->devices)
+        deviceList << device->toMap();
+    foreach (const IDevice::ConstPtr &device, d->inactiveAutoDetectedDevices)
+        deviceList << device->toMap();
     map.insert(QLatin1String(DeviceListKey), deviceList);
     return map;
 }
@@ -224,6 +240,8 @@ QString DeviceManager::settingsFilePath()
 void DeviceManager::addDevice(const IDevice::Ptr &device)
 {
     QTC_ASSERT(this != DeviceManagerPrivate::instance || (device->isAutoDetected()), return);
+    QTC_ASSERT(!device->isAutoDetected() || !findAutoDetectedDevice(d->devices, device->type(),
+            device->fingerprint()), return);
 
     // Ensure uniqueness of name.
     QString name = device->displayName();
@@ -241,13 +259,25 @@ void DeviceManager::addDevice(const IDevice::Ptr &device)
     d->devices << device;
     if (this == d->instance && d->clonedInstance)
         d->clonedInstance->addDevice(device->clone());
+    if (this == d->instance) {
+        QList<IDevice::Ptr>::Iterator it = d->inactiveAutoDetectedDevices.begin();
+        while (it != d->inactiveAutoDetectedDevices.end()) {
+            if (it->data()->type() == device->type()
+                    && it->data()->fingerprint() == device->fingerprint()) {
+                d->inactiveAutoDetectedDevices.erase(it);
+                break;
+            }
+            ++it;
+        }
+    }
+
     emit deviceAdded(device);
     emit updated();
 }
 
 void DeviceManager::removeDevice(int idx)
 {
-    const IDevice::ConstPtr device = deviceAt(idx);
+    const IDevice::Ptr device = mutableDeviceAt(idx);
     QTC_ASSERT(device, return);
     QTC_ASSERT(this != DeviceManagerPrivate::instance || device->isAutoDetected(), return);
 
@@ -269,6 +299,8 @@ void DeviceManager::removeDevice(int idx)
         d->clonedInstance->removeDevice(d->clonedInstance->
             indexForInternalId(device->internalId()));
     }
+    if (this == d->instance && device->isAutoDetected())
+        d->inactiveAutoDetectedDevices << device;
 
     emit updated();
 }
@@ -357,6 +389,12 @@ IDevice::ConstPtr DeviceManager::find(IDevice::Id id) const
     return index == -1 ? IDevice::ConstPtr() : deviceAt(index);
 }
 
+IDevice::ConstPtr DeviceManager::findInactiveAutoDetectedDevice(const QString &type,
+        const QString &fingerprint)
+{
+    return findAutoDetectedDevice(d->inactiveAutoDetectedDevices, type, fingerprint);
+}
+
 IDevice::ConstPtr DeviceManager::defaultDevice(const QString &deviceType) const
 {
     const IDevice::Id id = d->defaultDevices.value(deviceType, IDevice::invalidId());
diff --git a/src/plugins/projectexplorer/devicesupport/devicemanager.h b/src/plugins/projectexplorer/devicesupport/devicemanager.h
index 81a4b2e723a..cdb4a567f04 100644
--- a/src/plugins/projectexplorer/devicesupport/devicemanager.h
+++ b/src/plugins/projectexplorer/devicesupport/devicemanager.h
@@ -40,6 +40,7 @@
 
 namespace ProjectExplorer {
 class IDeviceFactory;
+
 namespace Internal {
 class DeviceManagerPrivate;
 class DeviceSettingsWidget;
@@ -58,6 +59,8 @@ public:
     int deviceCount() const;
     IDevice::ConstPtr deviceAt(int index) const;
     IDevice::ConstPtr find(IDevice::Id id) const;
+    IDevice::ConstPtr findInactiveAutoDetectedDevice(const QString &type,
+        const QString &fingerprint);
     IDevice::ConstPtr defaultDevice(const QString &deviceType) const;
     bool hasDevice(const QString &name) const;
     IDevice::Id internalId(const IDevice::ConstPtr &device) const;
diff --git a/src/plugins/projectexplorer/devicesupport/devicesettingswidget.cpp b/src/plugins/projectexplorer/devicesupport/devicesettingswidget.cpp
index 37d709ebe1c..161ab40b235 100644
--- a/src/plugins/projectexplorer/devicesupport/devicesettingswidget.cpp
+++ b/src/plugins/projectexplorer/devicesupport/devicesettingswidget.cpp
@@ -183,8 +183,8 @@ void DeviceSettingsWidget::displayCurrent()
     m_ui->defaultDeviceButton->setEnabled(
         m_deviceManager->defaultDevice(current->type()) != current);
     m_ui->osTypeValueLabel->setText(DeviceManager::displayNameForDeviceType(current->type()));
-    m_ui->autoDetectionValueLabel->setText(current->isAutoDetected() ? tr("Yes") : tr("No"));
-
+    m_ui->autoDetectionValueLabel->setText(current->isAutoDetected()
+        ? tr("Yes (fingerprint is '%1')").arg(current->fingerprint()) : tr("No"));
     m_nameValidator->setDisplayName(current->displayName());
     m_ui->removeConfigButton->setEnabled(!current->isAutoDetected());
     fillInValues();
diff --git a/src/plugins/projectexplorer/devicesupport/idevice.cpp b/src/plugins/projectexplorer/devicesupport/idevice.cpp
index 516e9182d88..14c2c0e2e11 100644
--- a/src/plugins/projectexplorer/devicesupport/idevice.cpp
+++ b/src/plugins/projectexplorer/devicesupport/idevice.cpp
@@ -31,6 +31,8 @@
 **************************************************************************/
 #include "idevice.h"
 
+#include <utils/qtcassert.h>
+
 #include <QString>
 
 /*!
@@ -74,6 +76,15 @@
  * \brief Identify the device internally.
  */
 
+/*!
+ * \fn QString ProjectExplorer::IDevice::fingerprint() const
+ * \brief Uniquely identifies an auto-detected device.
+ * The fingerprint can later be used to retrieve changes the user has done to the settings
+ * of an auto-detected device so they are not lost when the device goes away, e.g. because
+ * it has been disconnected.
+ * \sa ProjectExplorer::DeviceManager::findInactiveAutoDetectedDevice()
+ */
+
 /*!
  * \fn ProjectExplorer::IDevice::Id ProjectExplorer::IDevice::invalidId()
  * \brief A value that no device can ever have as its internal id.
@@ -103,6 +114,8 @@ namespace ProjectExplorer {
 const char DisplayNameKey[] = "Name";
 const char TypeKey[] = "OsType";
 const char InternalIdKey[] = "InternalId";
+const char OriginKey[] = "Origin";
+const char FingerprintKey[] = "FingerPrint";
 
 namespace Internal {
 class IDevicePrivate
@@ -112,6 +125,7 @@ public:
     QString type;
     IDevice::Origin origin;
     IDevice::Id internalId;
+    QString fingerprint;
 };
 } // namespace Internal
 
@@ -119,10 +133,13 @@ IDevice::IDevice() : d(new Internal::IDevicePrivate)
 {
 }
 
-IDevice::IDevice(const QString &type, Origin origin) : d(new Internal::IDevicePrivate)
+IDevice::IDevice(const QString &type, Origin origin, const QString fingerprint)
+    : d(new Internal::IDevicePrivate)
 {
     d->type = type;
     d->origin = origin;
+    d->fingerprint = fingerprint;
+    QTC_CHECK(d->origin == ManuallyAdded || !d->fingerprint.isEmpty());
 }
 
 IDevice::IDevice(const IDevice &other) : d(new Internal::IDevicePrivate)
@@ -155,6 +172,11 @@ bool IDevice::isAutoDetected() const
     return d->origin == AutoDetected;
 }
 
+QString IDevice::fingerprint() const
+{
+    return d->fingerprint;
+}
+
 IDevice::Id IDevice::internalId() const
 {
     return d->internalId;
@@ -178,9 +200,11 @@ QString IDevice::typeFromMap(const QVariantMap &map)
 void IDevice::fromMap(const QVariantMap &map)
 {
     d->type = typeFromMap(map);
-    d->origin = ManuallyAdded;
     d->displayName = map.value(QLatin1String(DisplayNameKey)).toString();
     d->internalId = map.value(QLatin1String(InternalIdKey), invalidId()).toULongLong();
+    d->origin = static_cast<Origin>(map.value(QLatin1String(OriginKey), ManuallyAdded).toInt());
+    d->fingerprint = map.value(QLatin1String(FingerprintKey)).toString();
+    QTC_CHECK(d->origin == ManuallyAdded || !d->fingerprint.isEmpty());
 }
 
 QVariantMap IDevice::toMap() const
@@ -189,6 +213,8 @@ QVariantMap IDevice::toMap() const
     map.insert(QLatin1String(DisplayNameKey), d->displayName);
     map.insert(QLatin1String(TypeKey), d->type);
     map.insert(QLatin1String(InternalIdKey), d->internalId);
+    map.insert(QLatin1String(OriginKey), d->origin);
+    map.insert(QLatin1String(FingerprintKey), d->fingerprint);
     return map;
 }
 
diff --git a/src/plugins/projectexplorer/devicesupport/idevice.h b/src/plugins/projectexplorer/devicesupport/idevice.h
index 1d917c0bcaa..047d7698d6a 100644
--- a/src/plugins/projectexplorer/devicesupport/idevice.h
+++ b/src/plugins/projectexplorer/devicesupport/idevice.h
@@ -59,6 +59,7 @@ public:
 
     QString type() const;
     bool isAutoDetected() const;
+    QString fingerprint() const;
     Id internalId() const;
 
     virtual void fromMap(const QVariantMap &map);
@@ -70,7 +71,7 @@ public:
 
 protected:
     IDevice();
-    IDevice(const QString &type, Origin origin);
+    IDevice(const QString &type, Origin origin, const QString fingerprint = QString());
     IDevice(const IDevice &other);
 
     virtual QVariantMap toMap() const;
diff --git a/src/plugins/remotelinux/linuxdeviceconfiguration.cpp b/src/plugins/remotelinux/linuxdeviceconfiguration.cpp
index ad5f95fb99f..7ffc5e492f6 100644
--- a/src/plugins/remotelinux/linuxdeviceconfiguration.cpp
+++ b/src/plugins/remotelinux/linuxdeviceconfiguration.cpp
@@ -79,9 +79,9 @@ LinuxDeviceConfiguration::~LinuxDeviceConfiguration()
 }
 
 LinuxDeviceConfiguration::Ptr LinuxDeviceConfiguration::create(const QString &name,
-   const QString &type, MachineType machineType, Origin origin)
+   const QString &type, MachineType machineType, Origin origin, const QString &fingerprint)
 {
-    return Ptr(new LinuxDeviceConfiguration(name, type, machineType, origin));
+    return Ptr(new LinuxDeviceConfiguration(name, type, machineType, origin, fingerprint));
 }
 
 LinuxDeviceConfiguration::LinuxDeviceConfiguration() : d(new LinuxDeviceConfigurationPrivate)
@@ -89,8 +89,8 @@ LinuxDeviceConfiguration::LinuxDeviceConfiguration() : d(new LinuxDeviceConfigur
 }
 
 LinuxDeviceConfiguration::LinuxDeviceConfiguration(const QString &name, const QString &type,
-        MachineType machineType, Origin origin)
-    : IDevice(type, origin), d(new LinuxDeviceConfigurationPrivate)
+        MachineType machineType, Origin origin, const QString &fingerprint)
+    : IDevice(type, origin, fingerprint), d(new LinuxDeviceConfigurationPrivate)
 {
     setDisplayName(name);
     d->machineType = machineType;
diff --git a/src/plugins/remotelinux/linuxdeviceconfiguration.h b/src/plugins/remotelinux/linuxdeviceconfiguration.h
index c5385a168c9..58c9d11e8d2 100644
--- a/src/plugins/remotelinux/linuxdeviceconfiguration.h
+++ b/src/plugins/remotelinux/linuxdeviceconfiguration.h
@@ -68,14 +68,14 @@ public:
 
     static Ptr create();
     static Ptr create(const QString &name, const QString &type, MachineType machineType,
-        Origin origin = ManuallyAdded);
+        Origin origin = ManuallyAdded, const QString &fingerprint = QString());
 
     void fromMap(const QVariantMap &map);
     ProjectExplorer::IDevice::Ptr clone() const;
 private:
     LinuxDeviceConfiguration();
     LinuxDeviceConfiguration(const QString &name, const QString &type, MachineType machineType,
-        Origin origin);
+        Origin origin, const QString &fingerprint);
 
     LinuxDeviceConfiguration(const LinuxDeviceConfiguration &other);
     LinuxDeviceConfiguration &operator=(const LinuxDeviceConfiguration &);
-- 
GitLab