devicemanager.cpp 17.3 KB
Newer Older
hjk's avatar
hjk committed
1
/****************************************************************************
2
**
3
** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
hjk's avatar
hjk committed
4
** Contact: http://www.qt-project.org/legal
5
**
hjk's avatar
hjk committed
6
** This file is part of Qt Creator.
7
**
hjk's avatar
hjk committed
8
9
10
11
12
13
14
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Digia.  For licensing terms and
** conditions see http://qt.digia.com/licensing.  For further information
** use the contact form at http://qt.digia.com/contact-us.
15
16
**
** GNU Lesser General Public License Usage
hjk's avatar
hjk committed
17
18
19
20
21
22
23
24
25
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file.  Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Digia gives you certain additional
** rights.  These rights are described in the Digia Qt LGPL Exception
26
27
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
hjk's avatar
hjk committed
28
****************************************************************************/
29
30
31
32
33
34
#include "devicemanager.h"

#include "idevicefactory.h"

#include <coreplugin/icore.h>
#include <extensionsystem/pluginmanager.h>
35
#include <projectexplorer/project.h>
36
#include <projectexplorer/projectexplorerconstants.h>
37
#include <utils/fileutils.h>
38
39
#include <utils/persistentsettings.h>
#include <utils/qtcassert.h>
40
#include <utils/portlist.h>
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59

#include <QFileInfo>
#include <QHash>
#include <QList>
#include <QString>
#include <QVariantList>

#include <limits>

namespace ProjectExplorer {
namespace Internal {

const char DeviceManagerKey[] = "DeviceManager";
const char DeviceListKey[] = "DeviceList";
const char DefaultDevicesKey[] = "DefaultDevices";

class DeviceManagerPrivate
{
public:
60
61
62
    DeviceManagerPrivate() : writer(0)
    { }

63
64
65
66
67
68
69
70
71
    int indexForId(Core::Id id) const
    {
        for (int i = 0; i < devices.count(); ++i) {
            if (devices.at(i)->id() == id)
                return i;
        }
        return -1;
    }

72
    static DeviceManager *instance;
73
74
    static DeviceManager *clonedInstance;
    QList<IDevice::Ptr> devices;
75
    QHash<Core::Id, Core::Id> defaultDevices;
Tobias Hunger's avatar
Tobias Hunger committed
76
77

    Utils::PersistentSettingsWriter *writer;
78
79
};
DeviceManager *DeviceManagerPrivate::clonedInstance = 0;
80
DeviceManager *DeviceManagerPrivate::instance = 0;
81
82
83
84
85
86

} // namespace Internal

using namespace Internal;


87
DeviceManager *DeviceManager::instance()
88
{
89
    return DeviceManagerPrivate::instance;
90
91
92
93
94
95
96
97
98
}

int DeviceManager::deviceCount() const
{
    return d->devices.count();
}

void DeviceManager::replaceInstance()
{
99
100
101
    copy(DeviceManagerPrivate::clonedInstance, instance(), false);
    emit instance()->deviceListChanged();
    emit instance()->updated();
102
103
104
105
106
107
108
109
110
111
112
113
}

void DeviceManager::removeClonedInstance()
{
    delete DeviceManagerPrivate::clonedInstance;
    DeviceManagerPrivate::clonedInstance = 0;
}

DeviceManager *DeviceManager::cloneInstance()
{
    QTC_ASSERT(!DeviceManagerPrivate::clonedInstance, return 0);

114
    DeviceManagerPrivate::clonedInstance = new DeviceManager(false);
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
    copy(instance(), DeviceManagerPrivate::clonedInstance, true);
    return DeviceManagerPrivate::clonedInstance;
}

void DeviceManager::copy(const DeviceManager *source, DeviceManager *target, bool deep)
{
    if (deep) {
        foreach (const IDevice::ConstPtr &device, source->d->devices)
            target->d->devices << device->clone();
    } else {
        target->d->devices = source->d->devices;
    }
    target->d->defaultDevices = source->d->defaultDevices;
}

void DeviceManager::save()
{
132
    if (d->clonedInstance == this || !d->writer)
133
        return;
134
135
136
    QVariantMap data;
    data.insert(QLatin1String(DeviceManagerKey), toMap());
    d->writer->save(data, Core::ICore::mainWindow());
137
138
139
140
}

void DeviceManager::load()
{
141
142
143
144
145
146
147
    QTC_ASSERT(!d->writer, return);

    // Only create writer now: We do not want to save before the settings were read!
    d->writer = new Utils::PersistentSettingsWriter(
                settingsFilePath(QLatin1String("/qtcreator/devices.xml")),
                QLatin1String("QtCreatorDevices"));

148
    Utils::PersistentSettingsReader reader;
149
150
151
152
153
154
    // read devices file from global settings path
    QList<IDevice::Ptr> sdkDevices;
    if (reader.load(systemSettingsFilePath(QLatin1String("/qtcreator/devices.xml"))))
        sdkDevices = fromMap(reader.restoreValues().value(QLatin1String(DeviceManagerKey)).toMap());
    // read devices file from user settings path
    QList<IDevice::Ptr> userDevices;
Tobias Hunger's avatar
Tobias Hunger committed
155
    if (reader.load(settingsFilePath(QLatin1String("/qtcreator/devices.xml"))))
156
157
158
159
160
161
162
163
164
165
166
167
        userDevices = fromMap(reader.restoreValues().value(QLatin1String(DeviceManagerKey)).toMap());
    // Insert devices into the model. Prefer the higher device version when there are multiple
    // devices with the same id.
    foreach (IDevice::Ptr device, userDevices) {
        foreach (const IDevice::Ptr &sdkDevice, sdkDevices) {
            if (device->id() == sdkDevice->id()) {
                if (device->version() < sdkDevice->version())
                    device = sdkDevice;
                sdkDevices.removeOne(sdkDevice);
                break;
            }
        }
168
        addDevice(device);
169
170
    }
    // Append the new SDK devices to the model.
171
172
    foreach (const IDevice::Ptr &sdkDevice, sdkDevices)
        addDevice(sdkDevice);
173

174
    ensureOneDefaultDevicePerType();
175
176

    emit devicesLoaded();
177
178
}

179
QList<IDevice::Ptr> DeviceManager::fromMap(const QVariantMap &map)
180
{
181
    QList<IDevice::Ptr> devices;
182
183
184
    const QVariantMap defaultDevsMap = map.value(QLatin1String(DefaultDevicesKey)).toMap();
    for (QVariantMap::ConstIterator it = defaultDevsMap.constBegin();
         it != defaultDevsMap.constEnd(); ++it) {
185
        d->defaultDevices.insert(Core::Id::fromString(it.key()), Core::Id::fromSetting(it.value()));
186
187
188
189
    }
    const QVariantList deviceList = map.value(QLatin1String(DeviceListKey)).toList();
    foreach (const QVariant &v, deviceList) {
        const QVariantMap map = v.toMap();
190
        const IDeviceFactory * const factory = restoreFactory(map);
191
192
        if (!factory)
            continue;
193
        const IDevice::Ptr device = factory->restore(map);
194
        QTC_ASSERT(device, continue);
195
        devices << device;
196
    }
197
    return devices;
198
199
200
201
202
203
}

QVariantMap DeviceManager::toMap() const
{
    QVariantMap map;
    QVariantMap defaultDeviceMap;
204
    typedef QHash<Core::Id, Core::Id> TypeIdHash;
205
206
    for (TypeIdHash::ConstIterator it = d->defaultDevices.constBegin();
             it != d->defaultDevices.constEnd(); ++it) {
207
        defaultDeviceMap.insert(it.key().toString(), it.value().toSetting());
208
209
210
    }
    map.insert(QLatin1String(DefaultDevicesKey), defaultDeviceMap);
    QVariantList deviceList;
211
212
    foreach (const IDevice::ConstPtr &device, d->devices)
        deviceList << device->toMap();
213
214
215
216
    map.insert(QLatin1String(DeviceListKey), deviceList);
    return map;
}

217
Utils::FileName DeviceManager::settingsFilePath(const QString &extension)
218
{
219
    return Utils::FileName::fromString(QFileInfo(Core::ICore::settings()->fileName()).absolutePath() + extension);
220
221
}

222
223
224
Utils::FileName DeviceManager::systemSettingsFilePath(const QString &deviceFileRelativePath)
{
    return Utils::FileName::fromString(
225
              QFileInfo(Core::ICore::settings(QSettings::SystemScope)->fileName()).absolutePath()
226
227
228
              + deviceFileRelativePath);
}

229
void DeviceManager::addDevice(const IDevice::ConstPtr &_device)
230
{
231
    const IDevice::Ptr device = _device->clone();
232

233
234
235
236
    QStringList names;
    foreach (const IDevice::ConstPtr &tmp, d->devices) {
        if (tmp->id() != device->id())
            names << tmp->displayName();
237
    }
238

239
240
241
    device->setDisplayName(Project::makeUnique(device->displayName(), names));

    const int pos = d->indexForId(device->id());
242

243
    if (!defaultDevice(device->type()))
244
        d->defaultDevices.insert(device->type(), device->id());
245
    if (this == DeviceManagerPrivate::instance && d->clonedInstance)
246
        d->clonedInstance->addDevice(device->clone());
247
248

    if (pos >= 0) {
249
        d->devices[pos] = device;
250
251
252
253
        emit deviceUpdated(device->id());
    } else {
        d->devices << device;
        emit deviceAdded(device->id());
254
255
    }

256
257
258
    emit updated();
}

259
void DeviceManager::removeDevice(Core::Id id)
260
{
261
    const IDevice::Ptr device = mutableDevice(id);
262
    QTC_ASSERT(device, return);
263
    QTC_ASSERT(this != instance() || device->isAutoDetected(), return);
264

265
    const bool wasDefault = d->defaultDevices.value(device->type()) == device->id();
266
    const Core::Id deviceType = device->type();
267
    d->devices.removeAt(d->indexForId(id));
268
    emit deviceRemoved(device->id());
269
270
271
272

    if (wasDefault) {
        for (int i = 0; i < d->devices.count(); ++i) {
            if (deviceAt(i)->type() == deviceType) {
273
                d->defaultDevices.insert(deviceAt(i)->type(), deviceAt(i)->id());
274
                emit deviceUpdated(deviceAt(i)->id());
275
276
277
278
                break;
            }
        }
    }
279
280
    if (this == instance() && d->clonedInstance)
        d->clonedInstance->removeDevice(id);
281
282
283
284

    emit updated();
}

285
286
void DeviceManager::setDeviceState(Core::Id deviceId, IDevice::DeviceState deviceState)
{
287
288
289
290
291
    // To see the state change in the DeviceSettingsWidget. This has to happen before
    // the pos check below, in case the device is only present in the cloned instance.
    if (this == instance() && d->clonedInstance)
        d->clonedInstance->setDeviceState(deviceId, deviceState);

292
    const int pos = d->indexForId(deviceId);
293
294
    if (pos < 0)
        return;
295
296
297
    IDevice::Ptr &device = d->devices[pos];
    if (device->deviceState() == deviceState)
        return;
298

299
300
301
302
303
    device->setDeviceState(deviceState);
    emit deviceUpdated(deviceId);
    emit updated();
}

304
305
306
307
308
bool DeviceManager::isLoaded() const
{
    return d->writer;
}

309
void DeviceManager::setDefaultDevice(Core::Id id)
310
{
311
    QTC_ASSERT(this != instance(), return);
312

313
314
    const IDevice::ConstPtr &device = find(id);
    QTC_ASSERT(device, return);
315
316
317
    const IDevice::ConstPtr &oldDefaultDevice = defaultDevice(device->type());
    if (device == oldDefaultDevice)
        return;
318
    d->defaultDevices.insert(device->type(), device->id());
319
320
    emit deviceUpdated(device->id());
    emit deviceUpdated(oldDefaultDevice->id());
321
322
323
324

    emit updated();
}

325
const IDeviceFactory *DeviceManager::restoreFactory(const QVariantMap &map)
326
327
{
    const QList<IDeviceFactory *> &factories
328
        = ExtensionSystem::PluginManager::getObjects<IDeviceFactory>();
329
    foreach (const IDeviceFactory * const factory, factories) {
330
        if (factory->canRestore(map))
331
332
            return factory;
    }
333
334
335
    qWarning("Warning: No factory found for device '%s' of type '%s'.",
             qPrintable(IDevice::idFromMap(map).toString()),
             qPrintable(IDevice::typeFromMap(map).toString()));
336
337
338
    return 0;
}

339
DeviceManager::DeviceManager(bool isInstance) : d(new DeviceManagerPrivate)
340
{
341
342
    if (isInstance) {
        connect(Core::ICore::instance(), SIGNAL(saveSettingsRequested()), SLOT(save()));
343
344
        QTC_CHECK(!DeviceManagerPrivate::instance);
        DeviceManagerPrivate::instance = this;
345
    }
346
347
348
349
}

DeviceManager::~DeviceManager()
{
350
351
    if (d->clonedInstance != this)
        delete d->writer;
352
353
354
355
356
357
358
359
360
    delete d;
}

IDevice::ConstPtr DeviceManager::deviceAt(int idx) const
{
    QTC_ASSERT(idx >= 0 && idx < deviceCount(), return IDevice::ConstPtr());
    return d->devices.at(idx);
}

361
IDevice::Ptr DeviceManager::mutableDevice(Core::Id id) const
362
{
363
    const int index = d->indexForId(id);
364
    return index == -1 ? IDevice::Ptr() : d->devices.at(index);
365
366
367
368
369
370
371
372
373
374
375
}

bool DeviceManager::hasDevice(const QString &name) const
{
    foreach (const IDevice::Ptr &device, d->devices) {
        if (device->displayName() == name)
            return true;
    }
    return false;
}

376
IDevice::ConstPtr DeviceManager::find(Core::Id id) const
377
{
378
    const int index = d->indexForId(id);
379
380
381
    return index == -1 ? IDevice::ConstPtr() : deviceAt(index);
}

382
IDevice::ConstPtr DeviceManager::defaultDevice(Core::Id deviceType) const
383
{
384
385
    const Core::Id id = d->defaultDevices.value(deviceType);
    return id.isValid() ? find(id) : IDevice::ConstPtr();
386
387
388
389
390
391
}

void DeviceManager::ensureOneDefaultDevicePerType()
{
    foreach (const IDevice::Ptr &device, d->devices) {
        if (!defaultDevice(device->type()))
392
            d->defaultDevices.insert(device->type(), device->id());
393
394
395
    }
}

396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
IDevice::Ptr DeviceManager::fromRawPointer(IDevice *device) const
{
    foreach (const IDevice::Ptr &devPtr, d->devices) {
        if (devPtr == device)
            return devPtr;
    }

    if (this == instance() && d->clonedInstance)
        return d->clonedInstance->fromRawPointer(device);

    qWarning("%s: Device not found.", Q_FUNC_INFO);
    return IDevice::Ptr();
}

IDevice::ConstPtr DeviceManager::fromRawPointer(const IDevice *device) const
{
    // The const_cast is safe, because we convert the Ptr back to a ConstPtr before returning it.
    return fromRawPointer(const_cast<IDevice *>(device));
}

416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
} // namespace ProjectExplorer


#ifdef WITH_TESTS
#include "projectexplorer.h"
#include <QSignalSpy>
#include <QTest>
#include <QUuid>

namespace ProjectExplorer {

class TestDevice : public IDevice
{
public:
    TestDevice()
431
        : IDevice(testTypeId(), AutoDetected, Hardware, Core::Id::fromString(QUuid::createUuid().toString())) {}
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448

    static Core::Id testTypeId() { return Core::Id("TestType"); }
private:
    TestDevice(const TestDevice &other) : IDevice(other) {}
    QString displayType() const { return QLatin1String("blubb"); }
    IDeviceWidget *createWidget() { return 0; }
    QList<Core::Id> actionIds() const { return QList<Core::Id>(); }
    QString displayNameForActionId(Core::Id) const { return QString(); }
    void executeAction(Core::Id, QWidget *) const { }
    Ptr clone() const { return Ptr(new TestDevice(*this)); }
};

void ProjectExplorerPlugin::testDeviceManager()
{
    TestDevice::Ptr dev = IDevice::Ptr(new TestDevice);
    dev->setDisplayName(QLatin1String("blubbdiblubbfurz!"));
    QVERIFY(dev->isAutoDetected());
449
    QCOMPARE(dev->deviceState(), IDevice::DeviceStateUnknown);
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
    QCOMPARE(dev->type(), TestDevice::testTypeId());

    TestDevice::Ptr dev2 = dev->clone();
    QCOMPARE(dev->id(), dev2->id());

    DeviceManager * const mgr = DeviceManager::instance();
    QVERIFY(!mgr->find(dev->id()));
    const int oldDeviceCount = mgr->deviceCount();

    QSignalSpy deviceAddedSpy(mgr, SIGNAL(deviceAdded(Core::Id)));
    QSignalSpy deviceRemovedSpy(mgr, SIGNAL(deviceRemoved(Core::Id)));
    QSignalSpy deviceUpdatedSpy(mgr, SIGNAL(deviceUpdated(Core::Id)));
    QSignalSpy deviceListChangedSpy(mgr, SIGNAL(deviceListChanged()));
    QSignalSpy updatedSpy(mgr, SIGNAL(updated()));

    mgr->addDevice(dev);
    QCOMPARE(mgr->deviceCount(), oldDeviceCount + 1);
    QVERIFY(mgr->find(dev->id()));
    QVERIFY(mgr->hasDevice(dev->displayName()));
    QCOMPARE(deviceAddedSpy.count(), 1);
    QCOMPARE(deviceRemovedSpy.count(), 0);
    QCOMPARE(deviceUpdatedSpy.count(), 0);
    QCOMPARE(deviceListChangedSpy.count(), 0);
    QCOMPARE(updatedSpy.count(), 1);
    deviceAddedSpy.clear();
    updatedSpy.clear();

477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
    mgr->setDeviceState(dev->id(), IDevice::DeviceStateUnknown);
    QCOMPARE(deviceAddedSpy.count(), 0);
    QCOMPARE(deviceRemovedSpy.count(), 0);
    QCOMPARE(deviceUpdatedSpy.count(), 0);
    QCOMPARE(deviceListChangedSpy.count(), 0);
    QCOMPARE(updatedSpy.count(), 0);

    mgr->setDeviceState(dev->id(), IDevice::DeviceReadyToUse);
    QCOMPARE(mgr->find(dev->id())->deviceState(), IDevice::DeviceReadyToUse);
    QCOMPARE(deviceAddedSpy.count(), 0);
    QCOMPARE(deviceRemovedSpy.count(), 0);
    QCOMPARE(deviceUpdatedSpy.count(), 1);
    QCOMPARE(deviceListChangedSpy.count(), 0);
    QCOMPARE(updatedSpy.count(), 1);
    deviceUpdatedSpy.clear();
    updatedSpy.clear();

494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
    mgr->addDevice(dev2);
    QCOMPARE(mgr->deviceCount(), oldDeviceCount + 1);
    QVERIFY(mgr->find(dev->id()));
    QCOMPARE(deviceAddedSpy.count(), 0);
    QCOMPARE(deviceRemovedSpy.count(), 0);
    QCOMPARE(deviceUpdatedSpy.count(), 1);
    QCOMPARE(deviceListChangedSpy.count(), 0);
    QCOMPARE(updatedSpy.count(), 1);
    deviceUpdatedSpy.clear();
    updatedSpy.clear();

    TestDevice::Ptr dev3 = IDevice::Ptr(new TestDevice);
    QVERIFY(dev->id() != dev3->id());

    dev3->setDisplayName(dev->displayName());
    mgr->addDevice(dev3);
    QCOMPARE(mgr->deviceAt(mgr->deviceCount() - 1)->displayName(),
511
             QString(dev3->displayName() + QLatin1String("2")));
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
    QCOMPARE(deviceAddedSpy.count(), 1);
    QCOMPARE(deviceRemovedSpy.count(), 0);
    QCOMPARE(deviceUpdatedSpy.count(), 0);
    QCOMPARE(deviceListChangedSpy.count(), 0);
    QCOMPARE(updatedSpy.count(), 1);
    deviceAddedSpy.clear();
    updatedSpy.clear();

    mgr->removeDevice(dev->id());
    mgr->removeDevice(dev3->id());
    QCOMPARE(mgr->deviceCount(), oldDeviceCount);
    QVERIFY(!mgr->find(dev->id()));
    QVERIFY(!mgr->find(dev3->id()));
    QCOMPARE(deviceAddedSpy.count(), 0);
    QCOMPARE(deviceRemovedSpy.count(), 2);
//    QCOMPARE(deviceUpdatedSpy.count(), 0); Uncomment once the "default" stuff is gone.
    QCOMPARE(deviceListChangedSpy.count(), 0);
    QCOMPARE(updatedSpy.count(), 2);
}
531

532
} // namespace ProjectExplorer
533
534

#endif // WITH_TESTS