gdbserverproviderssettingspage.cpp 14.7 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
/****************************************************************************
**
** Copyright (C) 2014 Denis Shienkov <denis.shienkov@gmail.com>
** Contact: http://www.qt-project.org/legal
**
** This file is part of Qt Creator.
**
** 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://www.qt.io/licensing.  For further information
** use the contact form at http://www.qt.io/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 or version 3 as published by the Free
** Software Foundation and appearing in the file LICENSE.LGPLv21 and
** LICENSE.LGPLv3 included in the packaging of this file.  Please review the
** following information to ensure the GNU Lesser General Public License
** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
** 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
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
****************************************************************************/

#include "gdbserverproviderssettingspage.h"
#include "gdbserverprovider.h"
#include "baremetalconstants.h"
#include "gdbserverprovidermanager.h"

#include <coreplugin/icore.h>
#include <extensionsystem/pluginmanager.h>

#include <utils/detailswidget.h>
#include <utils/qtcassert.h>
#include <utils/algorithm.h>

#include <QApplication>
#include <QAction>
#include <QHBoxLayout>
#include <QHeaderView>
#include <QItemSelectionModel>
#include <QMenu>
#include <QMessageBox>
#include <QPushButton>
#include <QSpacerItem>
#include <QTextStream>
#include <QTreeView>
#include <QVBoxLayout>

hjk's avatar
hjk committed
56
57
using namespace Utils;

58
59
60
namespace BareMetal {
namespace Internal {

hjk's avatar
hjk committed
61
class GdbServerProviderNode : public TreeItem
62
63
{
public:
hjk's avatar
hjk committed
64
65
66
67
68
    GdbServerProviderNode(GdbServerProvider *provider, bool changed = false)
        : provider(provider), changed(changed)
    {
        widget = provider ? provider->configurationWidget() : 0;
    }
69

70
    Qt::ItemFlags flags(int) const override
hjk's avatar
hjk committed
71
72
73
    {
        return provider ? Qt::ItemIsEnabled|Qt::ItemIsSelectable : Qt::ItemIsEnabled;
    }
74

75
    QVariant data(int column, int role) const override
hjk's avatar
hjk committed
76
77
78
    {
        if (!provider)
            return QVariant();
79

hjk's avatar
hjk committed
80
81
82
83
84
85
        if (role == Qt::FontRole) {
            QFont f = QApplication::font();
            if (changed)
                f.setBold(true);
            return f;
        }
86

hjk's avatar
hjk committed
87
88
89
        if (role == Qt::DisplayRole) {
            return column == 0 ? provider->displayName() : provider->typeDisplayName();
        }
90

hjk's avatar
hjk committed
91
92
        // FIXME: Need to handle ToolTipRole role?
        return QVariant();
93
94
    }

hjk's avatar
hjk committed
95
96
97
98
99
    GdbServerProvider *provider;
    GdbServerProviderConfigWidget *widget;
    bool changed;
};

100
101

GdbServerProviderModel::GdbServerProviderModel(QObject *parent)
hjk's avatar
hjk committed
102
    : TreeModel(parent)
103
{
hjk's avatar
hjk committed
104
105
    setHeader({tr("Name"), tr("Type")});

106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
    const GdbServerProviderManager *manager = GdbServerProviderManager::instance();

    connect(manager, &GdbServerProviderManager::providerAdded,
            this, &GdbServerProviderModel::addProvider);
    connect(manager, &GdbServerProviderManager::providerRemoved,
            this, &GdbServerProviderModel::removeProvider);

    foreach (GdbServerProvider *p, manager->providers())
        addProvider(p);
}

GdbServerProvider *GdbServerProviderModel::provider(
        const QModelIndex &index) const
{
    if (!index.isValid())
        return 0;

hjk's avatar
hjk committed
123
    return static_cast<GdbServerProviderNode *>(itemForIndex(index))->provider;
124
125
126
127
128
129
130
131
}

GdbServerProviderConfigWidget *GdbServerProviderModel::widget(
        const QModelIndex &index) const
{
    if (!index.isValid())
        return 0;

hjk's avatar
hjk committed
132
    return static_cast<GdbServerProviderNode *>(itemForIndex(index))->widget;
133
134
135
136
137
}

void GdbServerProviderModel::apply()
{
    // Remove unused providers
hjk's avatar
hjk committed
138
139
    foreach (TreeItem *item, rootItem()->children()) {
        auto n = static_cast<GdbServerProviderNode *>(item);
140
141
        GdbServerProviderManager::instance()->deregisterProvider(n->provider);
    }
hjk's avatar
hjk committed
142
    QTC_CHECK(m_toRemoveNodes.isEmpty());
143
144

    // Update providers
hjk's avatar
hjk committed
145
146
    foreach (TreeItem *item, rootItem()->children()) {
        auto n = static_cast<GdbServerProviderNode *>(item);
147
148
149
        if (!n->changed)
            continue;

hjk's avatar
hjk committed
150
        QTC_CHECK(n->provider);
151
152
153
154
        if (n->widget)
            n->widget->apply();

        n->changed = false;
hjk's avatar
hjk committed
155
        n->update();
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
    }

    // Add new (and already updated) providers
    QStringList removedProviders;
    foreach (const GdbServerProviderNode *n, m_toAddNodes) {
        if (!GdbServerProviderManager::instance()->registerProvider(n->provider))
            removedProviders << n->provider->displayName();
    }

    qDeleteAll(m_toAddNodes);

    if (removedProviders.count() == 1) {
        QMessageBox::warning(Core::ICore::dialogParent(),
                             tr("Duplicate Providers Detected"),
                             tr("The following provider was already configured:<br>"
                                "&nbsp;%1<br>"
                                "It was not configured again.")
                             .arg(removedProviders.at(0)));

    } else if (!removedProviders.isEmpty()) {
        QMessageBox::warning(Core::ICore::dialogParent(),
                             tr("Duplicate Providers Detected"),
                             tr("The following providers were already configured:<br>"
                                "&nbsp;%1<br>"
                                "They were not configured again.")
                             .arg(removedProviders.join(QLatin1String(",<br>&nbsp;"))));
    }
}

hjk's avatar
hjk committed
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
template <class Container>
GdbServerProviderNode *findNode(const Container &container, const GdbServerProvider *provider)
{
    auto test = [provider](TreeItem *item) {
        return static_cast<GdbServerProviderNode *>(item)->provider == provider;
    };

    return static_cast<GdbServerProviderNode *>(Utils::findOrDefault(container, test));
}

QModelIndex GdbServerProviderModel::indexForProvider(GdbServerProvider *provider) const
{
    GdbServerProviderNode *n = findNode(rootItem()->children(), provider);
    return n ? indexForItem(n) : QModelIndex();
}

201
202
void GdbServerProviderModel::markForRemoval(GdbServerProvider *provider)
{
hjk's avatar
hjk committed
203
    GdbServerProviderNode *n = findNode(rootItem()->children(), provider);
204
    QTC_ASSERT(n, return);
hjk's avatar
hjk committed
205
    takeItem(n);
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220

    if (m_toAddNodes.contains(n)) {
        delete n->provider;
        n->provider = 0;
        m_toAddNodes.removeOne(n);
        delete n;
    } else {
        m_toRemoveNodes.append(n);
    }

    emit endRemoveRows();
}

void GdbServerProviderModel::markForAddition(GdbServerProvider *provider)
{
hjk's avatar
hjk committed
221
222
    GdbServerProviderNode *n = createNode(provider, true);
    rootItem()->appendChild(n);
223
224
225
226
227
228
    m_toAddNodes.append(n);
}

GdbServerProviderNode *GdbServerProviderModel::createNode(
        GdbServerProvider *provider, bool changed)
{
hjk's avatar
hjk committed
229
    auto n = new GdbServerProviderNode(provider, changed);
230
    if (n->widget) {
hjk's avatar
hjk committed
231
232
233
234
235
236
237
238
239
        connect(n->widget, &GdbServerProviderConfigWidget::dirty, this, [this, n] {
            foreach (TreeItem *item, rootItem()->children()) {
                auto nn = static_cast<GdbServerProviderNode *>(item);
                if (nn->widget == n->widget) {
                    nn->changed = true;
                    nn->update();
                }
            }
        });
240
241
242
243
244
245
    }
    return n;
}

void GdbServerProviderModel::addProvider(GdbServerProvider *provider)
{
hjk's avatar
hjk committed
246
247
248
249
250
251
252
    foreach (TreeItem *item, rootItem()->children()) {
        auto n = static_cast<GdbServerProviderNode *>(item);
        if (n->provider == provider) {
            m_toAddNodes.removeOne(n);
            // do not delete n: Still used elsewhere!
            return;
        }
253
    }
hjk's avatar
hjk committed
254
    rootItem()->appendChild(createNode(provider, false));
255
256
257
258
259
260
261
262
263
264
265
266
    emit providerStateChanged();
}

void GdbServerProviderModel::removeProvider(GdbServerProvider *provider)
{
    GdbServerProviderNode *n = findNode(m_toRemoveNodes, provider);
    if (n) {
        m_toRemoveNodes.removeOne(n);
        delete n;
        return;
    }

hjk's avatar
hjk committed
267
268
    n = findNode(rootItem()->children(), provider);
    takeItem(n);
269
270
271
272
273
    delete n;

    emit providerStateChanged();
}

hjk's avatar
hjk committed
274
class GdbServerProvidersSettingsWidget : public QWidget
275
{
hjk's avatar
hjk committed
276
    Q_DECLARE_TR_FUNCTIONS(BareMetal::Internal::GdbServerProvidersSettingsPage)
277

hjk's avatar
hjk committed
278
279
public:
    GdbServerProvidersSettingsWidget(GdbServerProvidersSettingsPage *page);
280

hjk's avatar
hjk committed
281
282
283
    void providerSelectionChanged();
    void removeProvider();
    void updateState();
284

hjk's avatar
hjk committed
285
286
    void createProvider(GdbServerProviderFactory *f);
    QModelIndex currentIndex() const;
287

hjk's avatar
hjk committed
288
289
290
291
292
293
294
295
296
297
298
299
300
301
public:
    GdbServerProvidersSettingsPage *m_page;
    GdbServerProviderModel m_model;
    QItemSelectionModel *m_selectionModel;
    QTreeView *m_providerView;
    Utils::DetailsWidget *m_container;
    QPushButton *m_addButton;
    QPushButton *m_cloneButton;
    QPushButton *m_delButton;
};

GdbServerProvidersSettingsWidget::GdbServerProvidersSettingsWidget
        (GdbServerProvidersSettingsPage *page)
    : m_page(page)
302
{
hjk's avatar
hjk committed
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
    m_providerView = new QTreeView(this);
    m_providerView->setUniformRowHeights(true);
    m_providerView->header()->setStretchLastSection(false);

    m_addButton = new QPushButton(tr("Add"), this);
    m_cloneButton = new QPushButton(tr("Clone"), this);
    m_delButton = new QPushButton(tr("Remove"), this);

    m_container = new Utils::DetailsWidget(this);
    m_container->setState(Utils::DetailsWidget::NoSummary);
    m_container->setMinimumWidth(500);
    m_container->setVisible(false);

    auto buttonLayout = new QHBoxLayout();
    buttonLayout->setSpacing(6);
    buttonLayout->setContentsMargins(0, 0, 0, 0);
    buttonLayout->addWidget(m_addButton);
    buttonLayout->addWidget(m_cloneButton);
    buttonLayout->addWidget(m_delButton);
    auto spacerItem = new QSpacerItem(40, 10, QSizePolicy::Expanding, QSizePolicy::Minimum);
    buttonLayout->addItem(spacerItem);

    auto verticalLayout = new QVBoxLayout();
    verticalLayout->addWidget(m_providerView);
    verticalLayout->addLayout(buttonLayout);
328

hjk's avatar
hjk committed
329
330
331
    auto horizontalLayout = new QHBoxLayout(this);
    horizontalLayout->addLayout(verticalLayout);
    horizontalLayout->addWidget(m_container);
332

hjk's avatar
hjk committed
333
334
    connect(&m_model, &GdbServerProviderModel::providerStateChanged,
            this, &GdbServerProvidersSettingsWidget::updateState);
335

hjk's avatar
hjk committed
336
    m_providerView->setModel(&m_model);
337

hjk's avatar
hjk committed
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
    auto headerView = m_providerView->header();
    headerView->setSectionResizeMode(0, QHeaderView::ResizeToContents);
    headerView->setSectionResizeMode(1, QHeaderView::Stretch);
    m_providerView->expandAll();

    m_selectionModel = m_providerView->selectionModel();

    connect(m_selectionModel, &QItemSelectionModel::selectionChanged,
            this, &GdbServerProvidersSettingsWidget::providerSelectionChanged);

    connect(GdbServerProviderManager::instance(), &GdbServerProviderManager::providersChanged,
            this, &GdbServerProvidersSettingsWidget::providerSelectionChanged);

    // Set up add menu:
    auto addMenu = new QMenu(m_addButton);

    foreach (const auto f, GdbServerProviderManager::instance()->factories()) {
        auto action = new QAction(addMenu);
        action->setText(f->displayName());
        connect(action, &QAction::triggered, this, [this, f] { createProvider(f); });
        addMenu->addAction(action);
359
360
    }

hjk's avatar
hjk committed
361
    connect(m_cloneButton, &QAbstractButton::clicked, this, [this] { createProvider(0); });
362

hjk's avatar
hjk committed
363
    m_addButton->setMenu(addMenu);
364

hjk's avatar
hjk committed
365
366
    connect(m_delButton, &QPushButton::clicked,
            this, &GdbServerProvidersSettingsWidget::removeProvider);
367

hjk's avatar
hjk committed
368
    updateState();
369
370
}

hjk's avatar
hjk committed
371
void GdbServerProvidersSettingsWidget::providerSelectionChanged()
372
373
374
375
376
377
378
{
    if (!m_container)
        return;
    const QModelIndex current = currentIndex();
    QWidget *w = m_container->takeWidget(); // Prevent deletion.
    if (w)
        w->setVisible(false);
hjk's avatar
hjk committed
379
    w = current.isValid() ? m_model.widget(current) : 0;
380
381
382
383
384
    m_container->setWidget(w);
    m_container->setVisible(w != 0);
    updateState();
}

hjk's avatar
hjk committed
385
void GdbServerProvidersSettingsWidget::createProvider(GdbServerProviderFactory *f)
386
387
388
{
    GdbServerProvider *provider = 0;
    if (!f) {
hjk's avatar
hjk committed
389
        GdbServerProvider *old = m_model.provider(currentIndex());
390
391
392
393
394
395
396
        if (!old)
            return;
        provider = old->clone();
    } else {
        provider = f->create();
    }

hjk's avatar
hjk committed
397
398
399
400
    if (!provider)
        return;

    m_model.markForAddition(provider);
401

hjk's avatar
hjk committed
402
    m_selectionModel->select(m_model.indexForProvider(provider),
403
404
405
406
407
                             QItemSelectionModel::Clear
                             | QItemSelectionModel::SelectCurrent
                             | QItemSelectionModel::Rows);
}

hjk's avatar
hjk committed
408
void GdbServerProvidersSettingsWidget::removeProvider()
409
{
hjk's avatar
hjk committed
410
411
    if (GdbServerProvider *p = m_model.provider(currentIndex()))
        m_model.markForRemoval(p);
412
413
}

hjk's avatar
hjk committed
414
void GdbServerProvidersSettingsWidget::updateState()
415
416
417
418
419
420
{
    if (!m_cloneButton)
        return;

    bool canCopy = false;
    bool canDelete = false;
hjk's avatar
hjk committed
421
    if (const GdbServerProvider *p = m_model.provider(currentIndex())) {
422
423
424
425
426
427
428
429
        canCopy = p->isValid();
        canDelete = true;
    }

    m_cloneButton->setEnabled(canCopy);
    m_delButton->setEnabled(canDelete);
}

hjk's avatar
hjk committed
430
QModelIndex GdbServerProvidersSettingsWidget::currentIndex() const
431
432
433
434
435
436
437
438
439
440
{
    if (!m_selectionModel)
        return QModelIndex();

    const QModelIndexList rows = m_selectionModel->selectedRows();
    if (rows.count() != 1)
        return QModelIndex();
    return rows.at(0);
}

hjk's avatar
hjk committed
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467

GdbServerProvidersSettingsPage::GdbServerProvidersSettingsPage(QObject *parent)
    : Core::IOptionsPage(parent)
{
    setCategory(Constants::BAREMETAL_SETTINGS_CATEGORY);
    setDisplayCategory(QCoreApplication::translate(
                       "BareMetal", Constants::BAREMETAL_SETTINGS_TR_CATEGORY));
    setCategoryIcon(QLatin1String(Constants::BAREMETAL_SETTINGS_CATEGORY_ICON));
    setId(Constants::GDB_PROVIDERS_SETTINGS_ID);
    setDisplayName(tr("GDB Server Providers"));
}

QWidget *GdbServerProvidersSettingsPage::widget()
{
    if (!m_configWidget)
        m_configWidget = new GdbServerProvidersSettingsWidget(this);
     return m_configWidget;
}

void GdbServerProvidersSettingsPage::apply()
{
    if (m_configWidget)
        m_configWidget->m_model.apply();
}

void GdbServerProvidersSettingsPage::finish()
{
Nikita Baryshnikov's avatar
Nikita Baryshnikov committed
468
469
470
    if (m_configWidget)
        disconnect(GdbServerProviderManager::instance(), &GdbServerProviderManager::providersChanged,
                   m_configWidget.data(), &GdbServerProvidersSettingsWidget::providerSelectionChanged);
hjk's avatar
hjk committed
471

Nikita Baryshnikov's avatar
Nikita Baryshnikov committed
472
    delete m_configWidget.data();
hjk's avatar
hjk committed
473
474
}

475
476
} // namespace Internal
} // namespace BareMetal