1 /****************************************************************************
2 **
3 ** Copyright (C) 2019 The Qt Company Ltd.
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of Qt Creator.
7 **
8 ** Commercial License Usage
9 ** Licensees holding valid commercial Qt licenses may use this file in
10 ** accordance with the commercial license agreement provided with the
11 ** Software or, alternatively, in accordance with the terms contained in
12 ** a written agreement between you and The Qt Company. For licensing terms
13 ** and conditions see https://www.qt.io/terms-conditions. For further
14 ** information use the contact form at https://www.qt.io/contact-us.
15 **
16 ** GNU General Public License Usage
17 ** Alternatively, this file may be used under the terms of the GNU
18 ** General Public License version 3 as published by the Free Software
19 ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
20 ** included in the packaging of this file. Please review the following
21 ** information to ensure the GNU General Public License requirements will
22 ** be met: https://www.gnu.org/licenses/gpl-3.0.html.
23 **
24 ****************************************************************************/
25 
26 #include "mcusupportconstants.h"
27 #include "mcusupportoptionspage.h"
28 #include "mcusupportoptions.h"
29 #include "mcusupportsdk.h"
30 
31 #include <cmakeprojectmanager/cmakeprojectconstants.h>
32 #include <cmakeprojectmanager/cmaketoolmanager.h>
33 #include <coreplugin/icore.h>
34 #include <projectexplorer/kitmanager.h>
35 #include <projectexplorer/projectexplorerconstants.h>
36 #include <utils/algorithm.h>
37 #include <utils/infolabel.h>
38 #include <utils/qtcassert.h>
39 #include <utils/utilsicons.h>
40 
41 #include <QCheckBox>
42 #include <QComboBox>
43 #include <QDir>
44 #include <QHBoxLayout>
45 #include <QFormLayout>
46 #include <QGroupBox>
47 #include <QLabel>
48 #include <QPushButton>
49 #include <QVBoxLayout>
50 
51 namespace McuSupport {
52 namespace Internal {
53 
54 class McuSupportOptionsWidget : public Core::IOptionsPageWidget
55 {
56     Q_DECLARE_TR_FUNCTIONS(McuSupport::Internal::McuSupportOptionsWidget)
57 
58 public:
59     McuSupportOptionsWidget();
60 
61     void updateStatus();
62     void showMcuTargetPackages();
63     McuTarget *currentMcuTarget() const;
64 
65 private:
66     void apply() final;
67 
68     void populateMcuTargetsComboBox();
69     void showEvent(QShowEvent *event) final;
70 
71     QString m_armGccPath;
72     McuSupportOptions m_options;
73     QMap <McuPackage*, QWidget*> m_packageWidgets;
74     QMap <McuTarget*, QWidget*> m_mcuTargetPacketWidgets;
75     QFormLayout *m_packagesLayout = nullptr;
76     QGroupBox *m_qtForMCUsSdkGroupBox = nullptr;
77     QGroupBox *m_packagesGroupBox = nullptr;
78     QGroupBox *m_mcuTargetsGroupBox = nullptr;
79     QComboBox *m_mcuTargetsComboBox = nullptr;
80     QGroupBox *m_kitCreationGroupBox = nullptr;
81     QCheckBox *m_kitAutomaticCreationCheckBox = nullptr;
82     Utils::InfoLabel *m_kitCreationInfoLabel = nullptr;
83     Utils::InfoLabel *m_statusInfoLabel = nullptr;
84     Utils::InfoLabel *m_mcuTargetsInfoLabel = nullptr;
85     QPushButton *m_kitCreationPushButton = nullptr;
86     QPushButton *m_kitUpdatePushButton = nullptr;
87 };
88 
McuSupportOptionsWidget()89 McuSupportOptionsWidget::McuSupportOptionsWidget()
90 {
91     auto mainLayout = new QVBoxLayout(this);
92 
93     {
94         m_statusInfoLabel = new Utils::InfoLabel;
95         m_statusInfoLabel->setElideMode(Qt::ElideNone);
96         m_statusInfoLabel->setOpenExternalLinks(false);
97         mainLayout->addWidget(m_statusInfoLabel);
98         connect(m_statusInfoLabel, &QLabel::linkActivated, this, [] {
99             Core::ICore::showOptionsDialog(CMakeProjectManager::Constants::CMAKE_SETTINGS_PAGE_ID);
100         });
101     }
102 
103     {
104         m_qtForMCUsSdkGroupBox = new QGroupBox(m_options.qtForMCUsSdkPackage->label());
105         m_qtForMCUsSdkGroupBox->setFlat(true);
106         auto layout = new QVBoxLayout(m_qtForMCUsSdkGroupBox);
107         layout->addWidget(m_options.qtForMCUsSdkPackage->widget());
108         mainLayout->addWidget(m_qtForMCUsSdkGroupBox);
109     }
110 
111     {
112         m_mcuTargetsGroupBox = new QGroupBox(tr("Targets supported by the %1")
113                                              .arg(m_qtForMCUsSdkGroupBox->title()));
114         m_mcuTargetsGroupBox->setFlat(true);
115         mainLayout->addWidget(m_mcuTargetsGroupBox);
116         m_mcuTargetsComboBox = new QComboBox;
117         auto layout = new QVBoxLayout(m_mcuTargetsGroupBox);
118         layout->addWidget(m_mcuTargetsComboBox);
119         connect(m_mcuTargetsComboBox, &QComboBox::currentTextChanged,
120                 this, &McuSupportOptionsWidget::showMcuTargetPackages);
121         connect(m_options.qtForMCUsSdkPackage, &McuPackage::changed,
122                 this, &McuSupportOptionsWidget::populateMcuTargetsComboBox);
123     }
124 
125     {
126         m_packagesGroupBox = new QGroupBox(tr("Requirements"));
127         m_packagesGroupBox->setFlat(true);
128         mainLayout->addWidget(m_packagesGroupBox);
129         m_packagesLayout = new QFormLayout;
130         m_packagesGroupBox->setLayout(m_packagesLayout);
131     }
132 
133     {
134         m_mcuTargetsInfoLabel = new Utils::InfoLabel;
135         mainLayout->addWidget(m_mcuTargetsInfoLabel);
136     }
137 
138     {
139         m_kitAutomaticCreationCheckBox = new QCheckBox(tr("Automatically create kits for all available targets on start"));
140         connect(m_kitAutomaticCreationCheckBox, &QCheckBox::stateChanged, this, [this] (int state) {
141             m_options.qtForMCUsSdkPackage->setAutomaticKitCreationEnabled(state == Qt::CheckState::Checked);
142         });
143         mainLayout->addWidget(m_kitAutomaticCreationCheckBox);
144     }
145 
146     {
147         m_kitCreationGroupBox = new QGroupBox(tr("Create a Kit"));
148         m_kitCreationGroupBox->setFlat(true);
149         mainLayout->addWidget(m_kitCreationGroupBox);
150         m_kitCreationInfoLabel = new Utils::InfoLabel;
151         auto vLayout = new QHBoxLayout(m_kitCreationGroupBox);
152         vLayout->addWidget(m_kitCreationInfoLabel);
153         m_kitCreationPushButton = new QPushButton(tr("Create Kit"));
154         m_kitCreationPushButton->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Preferred);
155         connect(m_kitCreationPushButton, &QPushButton::clicked, this, [this] {
156             McuSupportOptions::newKit(currentMcuTarget(), m_options.qtForMCUsSdkPackage);
157             McuSupportOptions::registerQchFiles();
158             updateStatus();
159         });
160         m_kitUpdatePushButton = new QPushButton(tr("Update Kit"));
161         m_kitUpdatePushButton->setSizePolicy(m_kitCreationPushButton->sizePolicy());
162         connect(m_kitUpdatePushButton, &QPushButton::clicked, this, [this] {
163             for (auto kit: McuSupportOptions::upgradeableKits(currentMcuTarget(), m_options.qtForMCUsSdkPackage))
164                 m_options.upgradeKitInPlace(kit, currentMcuTarget(), m_options.qtForMCUsSdkPackage);
165             updateStatus();
166         });
167         vLayout->addWidget(m_kitCreationPushButton);
168         vLayout->addWidget(m_kitUpdatePushButton);
169     }
170 
171     mainLayout->addStretch();
172 
173     connect(&m_options, &McuSupportOptions::changed, this, &McuSupportOptionsWidget::updateStatus);
174 
175     showMcuTargetPackages();
176 }
177 
updateStatus()178 void McuSupportOptionsWidget::updateStatus()
179 {
180     const McuTarget *mcuTarget = currentMcuTarget();
181 
182     const bool cMakeAvailable = !CMakeProjectManager::CMakeToolManager::cmakeTools().isEmpty();
183 
184     // Page elements
185     {
186         m_qtForMCUsSdkGroupBox->setVisible(cMakeAvailable);
187         const bool valid = cMakeAvailable &&
188                 m_options.qtForMCUsSdkPackage->validStatus();
189         const bool ready = valid && mcuTarget;
190         m_mcuTargetsGroupBox->setVisible(ready);
191         m_packagesGroupBox->setVisible(ready && !mcuTarget->packages().isEmpty());
192         m_kitCreationGroupBox->setVisible(ready);
193         m_mcuTargetsInfoLabel->setVisible(valid && m_options.mcuTargets.isEmpty());
194         if (m_mcuTargetsInfoLabel->isVisible()) {
195             m_mcuTargetsInfoLabel->setType(Utils::InfoLabel::NotOk);
196             const auto sdkPath = Utils::FilePath::fromString(m_options.qtForMCUsSdkPackage->basePath());
197             QString deprecationMessage;
198             if (Sdk::checkDeprecatedSdkError(sdkPath, deprecationMessage))
199                 m_mcuTargetsInfoLabel->setText(deprecationMessage);
200             else
201                 m_mcuTargetsInfoLabel->setText(tr("No valid kit descriptions found at %1.")
202                                                .arg(Sdk::kitsPath(sdkPath).toUserOutput()));
203         }
204     }
205 
206     // Kit creation status
207     if (mcuTarget) {
208         const bool mcuTargetValid = mcuTarget->isValid();
209         m_kitCreationPushButton->setVisible(mcuTargetValid);
210         m_kitUpdatePushButton->setVisible(mcuTargetValid);
211         if (mcuTargetValid) {
212             const bool hasMatchingKits = !McuSupportOptions::matchingKits(
213                         mcuTarget, m_options.qtForMCUsSdkPackage).isEmpty();
214             const bool hasUpgradeableKits = !hasMatchingKits &&
215                     !McuSupportOptions::upgradeableKits(
216                         mcuTarget, m_options.qtForMCUsSdkPackage).isEmpty();
217 
218             m_kitCreationPushButton->setEnabled(!hasMatchingKits);
219             m_kitUpdatePushButton->setEnabled(hasUpgradeableKits);
220 
221             m_kitCreationInfoLabel->setType(!hasMatchingKits
222                                             ? Utils::InfoLabel::Information
223                                             : Utils::InfoLabel::Ok);
224 
225             m_kitCreationInfoLabel->setText(
226                         hasMatchingKits ?
227                             tr("A kit for the selected target and SDK version already exists.")
228                           : hasUpgradeableKits ?
229                                 tr("Kits for a different SDK version exist.")
230                               : tr("A kit for the selected target can be created."));
231         } else {
232             m_kitCreationInfoLabel->setType(Utils::InfoLabel::NotOk);
233             m_kitCreationInfoLabel->setText("Provide the package paths in order to create a kit "
234                                             "for your target.");
235         }
236     }
237 
238     // Automatic Kit creation
239     m_kitAutomaticCreationCheckBox->setChecked(m_options.qtForMCUsSdkPackage->automaticKitCreationEnabled());
240 
241     // Status label in the bottom
242     {
243         m_statusInfoLabel->setVisible(!cMakeAvailable);
244         if (m_statusInfoLabel->isVisible()) {
245             m_statusInfoLabel->setType(Utils::InfoLabel::NotOk);
246             m_statusInfoLabel->setText("No CMake tool was detected. Add a CMake tool in the "
247                                        "<a href=\"cmake\">CMake options</a> and press Apply.");
248         }
249     }
250 }
251 
showMcuTargetPackages()252 void McuSupportOptionsWidget::showMcuTargetPackages()
253 {
254     const McuTarget *mcuTarget = currentMcuTarget();
255     if (!mcuTarget)
256         return;
257 
258     while (m_packagesLayout->rowCount() > 0) {
259         QFormLayout::TakeRowResult row = m_packagesLayout->takeRow(0);
260         row.labelItem->widget()->hide();
261         row.fieldItem->widget()->hide();
262     }
263 
264     for (auto package : qAsConst(m_options.packages)) {
265         QWidget *packageWidget = package->widget();
266         if (!mcuTarget->packages().contains(package))
267             continue;
268         m_packagesLayout->addRow(package->label(), packageWidget);
269         packageWidget->show();
270     }
271 
272     updateStatus();
273 }
274 
currentMcuTarget() const275 McuTarget *McuSupportOptionsWidget::currentMcuTarget() const
276 {
277     const int mcuTargetIndex = m_mcuTargetsComboBox->currentIndex();
278     return (mcuTargetIndex == -1 || m_options.mcuTargets.isEmpty())
279             ? nullptr
280             : m_options.mcuTargets.at(mcuTargetIndex);
281 }
282 
showEvent(QShowEvent * event)283 void McuSupportOptionsWidget::showEvent(QShowEvent *event)
284 {
285     Q_UNUSED(event)
286     populateMcuTargetsComboBox();
287 }
288 
apply()289 void McuSupportOptionsWidget::apply()
290 {
291     bool pathsChanged = false;
292 
293     m_options.qtForMCUsSdkPackage->writeGeneralSettings();
294     pathsChanged |= m_options.qtForMCUsSdkPackage->writeToSettings();
295     for (auto package : qAsConst(m_options.packages))
296         pathsChanged |= package->writeToSettings();
297 
298     if (pathsChanged) {
299         m_options.checkUpgradeableKits();
300         m_options.fixKitsDependencies();
301     }
302 }
303 
populateMcuTargetsComboBox()304 void McuSupportOptionsWidget::populateMcuTargetsComboBox()
305 {
306     m_options.populatePackagesAndTargets();
307     m_mcuTargetsComboBox->clear();
308     m_mcuTargetsComboBox->addItems(
309                 Utils::transform<QStringList>(m_options.mcuTargets, [](McuTarget *t) {
310                     return McuSupportOptions::kitName(t);
311                 }));
312     updateStatus();
313 }
314 
McuSupportOptionsPage()315 McuSupportOptionsPage::McuSupportOptionsPage()
316 {
317     setId(Utils::Id(Constants::SETTINGS_ID));
318     setDisplayName(McuSupportOptionsWidget::tr("MCU"));
319     setCategory(ProjectExplorer::Constants::DEVICE_SETTINGS_CATEGORY);
320     setWidgetCreator([] { return new McuSupportOptionsWidget; });
321 }
322 
323 } // Internal
324 } // McuSupport
325