1 /****************************************************************************
2 **
3 ** Copyright (C) 2016 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 "debuggerdialogs.h"
27 
28 #include "debuggerkitinformation.h"
29 #include "debuggerruncontrol.h"
30 #include "cdb/cdbengine.h"
31 
32 #include <coreplugin/icore.h>
33 #include <projectexplorer/projectexplorerconstants.h>
34 #include <projectexplorer/toolchain.h>
35 
36 #include <app/app_version.h>
37 #include <utils/pathchooser.h>
38 #include <utils/fancylineedit.h>
39 #include <utils/qtcassert.h>
40 
41 #include <ssh/sshconnection.h>
42 
43 #include <QButtonGroup>
44 #include <QCheckBox>
45 #include <QComboBox>
46 #include <QDebug>
47 #include <QDialogButtonBox>
48 #include <QDir>
49 #include <QFormLayout>
50 #include <QGroupBox>
51 #include <QLabel>
52 #include <QPlainTextEdit>
53 #include <QPushButton>
54 #include <QRadioButton>
55 #include <QRegularExpression>
56 #include <QScrollArea>
57 #include <QSpinBox>
58 
59 using namespace Core;
60 using namespace ProjectExplorer;
61 using namespace Utils;
62 
63 namespace Debugger {
64 namespace Internal {
65 
66 ///////////////////////////////////////////////////////////////////////
67 //
68 // StartApplicationDialogPrivate
69 //
70 ///////////////////////////////////////////////////////////////////////
71 
72 class StartApplicationDialogPrivate
73 {
74 public:
75     KitChooser *kitChooser;
76     QLabel *serverPortLabel;
77     QLabel *channelOverrideHintLabel;
78     QLabel *channelOverrideLabel;
79     QLineEdit *channelOverrideEdit;
80     QSpinBox *serverPortSpinBox;
81     PathChooser *localExecutablePathChooser;
82     FancyLineEdit *arguments;
83     PathChooser *workingDirectory;
84     QCheckBox *breakAtMainCheckBox;
85     QCheckBox *runInTerminalCheckBox;
86     QCheckBox *useTargetExtendedRemoteCheckBox;
87     PathChooser *debuginfoPathChooser;
88     QLabel *sysRootLabel;
89     PathChooser *sysRootPathChooser;
90     QLabel *serverInitCommandsLabel;
91     QPlainTextEdit *serverInitCommandsTextEdit;
92     QLabel *serverResetCommandsLabel;
93     QPlainTextEdit *serverResetCommandsTextEdit;
94     QComboBox *historyComboBox;
95     QDialogButtonBox *buttonBox;
96 };
97 
98 } // namespace Internal
99 } // namespace Debugger
100 
101 Q_DECLARE_METATYPE(Debugger::Internal::StartApplicationParameters)
102 
103 namespace Debugger {
104 namespace Internal {
105 
106 ///////////////////////////////////////////////////////////////////////
107 //
108 // StartApplicationParameters
109 //
110 ///////////////////////////////////////////////////////////////////////
111 
112 class StartApplicationParameters
113 {
114 public:
115     QString displayName() const;
116     bool equals(const StartApplicationParameters &rhs) const;
117     void toSettings(QSettings *) const;
118     void fromSettings(const QSettings *settings);
119 
operator ==(const StartApplicationParameters & p) const120     bool operator==(const StartApplicationParameters &p) const { return equals(p); }
operator !=(const StartApplicationParameters & p) const121     bool operator!=(const StartApplicationParameters &p) const { return !equals(p); }
122 
123     Id kitId;
124     uint serverPort;
125     QString serverAddress;
126     Runnable runnable;
127     bool breakAtMain = false;
128     bool runInTerminal = false;
129     bool useTargetExtendedRemote = false;
130     FilePath sysRoot;
131     QString serverInitCommands;
132     QString serverResetCommands;
133     QString debugInfoLocation;
134 };
135 
equals(const StartApplicationParameters & rhs) const136 bool StartApplicationParameters::equals(const StartApplicationParameters &rhs) const
137 {
138     return runnable.executable == rhs.runnable.executable
139         && serverPort == rhs.serverPort
140         && runnable.commandLineArguments == rhs.runnable.commandLineArguments
141         && runnable.workingDirectory == rhs.runnable.workingDirectory
142         && breakAtMain == rhs.breakAtMain
143         && runInTerminal == rhs.runInTerminal
144         && sysRoot == rhs.sysRoot
145         && serverInitCommands == rhs.serverInitCommands
146         && serverResetCommands == rhs.serverResetCommands
147         && kitId == rhs.kitId
148         && debugInfoLocation == rhs.debugInfoLocation
149         && serverAddress == rhs.serverAddress;
150 }
151 
displayName() const152 QString StartApplicationParameters::displayName() const
153 {
154     const int maxLength = 60;
155 
156     QString name = runnable.executable.fileName()
157             + ' ' + runnable.commandLineArguments;
158     if (name.size() > 60) {
159         int index = name.lastIndexOf(' ', maxLength);
160         if (index == -1)
161             index = maxLength;
162         name.truncate(index);
163         name += "...";
164     }
165 
166     if (Kit *kit = KitManager::kit(kitId))
167         name += QString::fromLatin1(" (%1)").arg(kit->displayName());
168 
169     return name;
170 }
171 
toSettings(QSettings * settings) const172 void StartApplicationParameters::toSettings(QSettings *settings) const
173 {
174     settings->setValue("LastKitId", kitId.toSetting());
175     settings->setValue("LastServerPort", serverPort);
176     settings->setValue("LastServerAddress", serverAddress);
177     settings->setValue("LastExternalExecutable", runnable.executable.toVariant());
178     settings->setValue("LastExternalExecutableArguments", runnable.commandLineArguments);
179     settings->setValue("LastExternalWorkingDirectory", runnable.workingDirectory);
180     settings->setValue("LastExternalBreakAtMain", breakAtMain);
181     settings->setValue("LastExternalRunInTerminal", runInTerminal);
182     settings->setValue("LastExternalUseTargetExtended", useTargetExtendedRemote);
183     settings->setValue("LastServerInitCommands", serverInitCommands);
184     settings->setValue("LastServerResetCommands", serverResetCommands);
185     settings->setValue("LastDebugInfoLocation", debugInfoLocation);
186     settings->setValue("LastSysRoot", sysRoot.toVariant());
187 }
188 
fromSettings(const QSettings * settings)189 void StartApplicationParameters::fromSettings(const QSettings *settings)
190 {
191     kitId = Id::fromSetting(settings->value("LastKitId"));
192     serverPort = settings->value("LastServerPort").toUInt();
193     serverAddress = settings->value("LastServerAddress").toString();
194     runnable.executable = FilePath::fromVariant(settings->value("LastExternalExecutable"));
195     runnable.commandLineArguments = settings->value("LastExternalExecutableArguments").toString();
196     runnable.workingDirectory = settings->value("LastExternalWorkingDirectory").toString();
197     breakAtMain = settings->value("LastExternalBreakAtMain").toBool();
198     runInTerminal = settings->value("LastExternalRunInTerminal").toBool();
199     useTargetExtendedRemote = settings->value("LastExternalUseTargetExtended").toBool();
200     serverInitCommands = settings->value("LastServerInitCommands").toString();
201     serverResetCommands = settings->value("LastServerResetCommands").toString();
202     debugInfoLocation = settings->value("LastDebugInfoLocation").toString();
203     sysRoot = FilePath::fromVariant(settings->value("LastSysRoot"));
204 }
205 
206 ///////////////////////////////////////////////////////////////////////
207 //
208 // StartApplicationDialog
209 //
210 ///////////////////////////////////////////////////////////////////////
211 
StartApplicationDialog(QWidget * parent)212 StartApplicationDialog::StartApplicationDialog(QWidget *parent)
213   : QDialog(parent), d(new StartApplicationDialogPrivate)
214 {
215     setWindowTitle(tr("Start Debugger"));
216 
217     d->kitChooser = new KitChooser(this);
218     d->kitChooser->setShowIcons(true);
219     d->kitChooser->populate();
220 
221     d->serverPortLabel = new QLabel(tr("Server port:"), this);
222     d->serverPortSpinBox = new QSpinBox(this);
223     d->serverPortSpinBox->setRange(1, 65535);
224 
225     d->channelOverrideHintLabel =
226             new QLabel(tr("Normally, the running server is identified by the IP of the "
227                           "device in the kit and the server port selected above.\n"
228                           "You can choose another communication channel here, such as "
229                           "a serial line or custom ip:port."));
230 
231     d->channelOverrideLabel = new QLabel(tr("Override server channel:"), this);
232     d->channelOverrideEdit = new QLineEdit(this);
233     //: "For example, /dev/ttyS0, COM1, 127.0.0.1:1234"
234     d->channelOverrideEdit->setPlaceholderText(
235         tr("For example, %1").arg("/dev/ttyS0, COM1, 127.0.0.1:1234"));
236 
237     d->localExecutablePathChooser = new PathChooser(this);
238     d->localExecutablePathChooser->setExpectedKind(PathChooser::File);
239     d->localExecutablePathChooser->setPromptDialogTitle(tr("Select Executable"));
240     d->localExecutablePathChooser->setHistoryCompleter("LocalExecutable");
241 
242     d->arguments = new FancyLineEdit(this);
243     d->arguments->setHistoryCompleter("CommandlineArguments");
244 
245     d->workingDirectory = new PathChooser(this);
246     d->workingDirectory->setExpectedKind(PathChooser::ExistingDirectory);
247     d->workingDirectory->setPromptDialogTitle(tr("Select Working Directory"));
248     d->workingDirectory->setHistoryCompleter("WorkingDirectory");
249 
250     d->runInTerminalCheckBox = new QCheckBox(this);
251 
252     d->breakAtMainCheckBox = new QCheckBox(this);
253     d->breakAtMainCheckBox->setText(QString());
254 
255     d->useTargetExtendedRemoteCheckBox = new QCheckBox(this);
256 
257     d->sysRootPathChooser = new PathChooser(this);
258     d->sysRootPathChooser->setExpectedKind(PathChooser::Directory);
259     d->sysRootPathChooser->setHistoryCompleter("Debugger.SysRoot.History");
260     d->sysRootPathChooser->setPromptDialogTitle(tr("Select SysRoot Directory"));
261     d->sysRootPathChooser->setToolTip(tr(
262         "This option can be used to override the kit's SysRoot setting."));
263     d->sysRootLabel = new QLabel(tr("Override S&ysRoot:"), this);
264     d->sysRootLabel->setBuddy(d->sysRootPathChooser);
265     d->sysRootLabel->setToolTip(d->sysRootPathChooser->toolTip());
266 
267     d->serverInitCommandsTextEdit = new QPlainTextEdit(this);
268     d->serverInitCommandsTextEdit->setToolTip(tr(
269         "This option can be used to send the target init commands."));
270 
271     d->serverInitCommandsLabel = new QLabel(tr("&Init commands:"), this);
272     d->serverInitCommandsLabel->setBuddy(d->serverInitCommandsTextEdit);
273     d->serverInitCommandsLabel->setToolTip(d->serverInitCommandsTextEdit->toolTip());
274 
275     d->serverResetCommandsTextEdit = new QPlainTextEdit(this);
276     d->serverResetCommandsTextEdit->setToolTip(tr(
277         "This option can be used to send the target reset commands."));
278 
279     d->serverResetCommandsLabel = new QLabel(tr("&Reset commands:"), this);
280     d->serverResetCommandsLabel->setBuddy(d->serverResetCommandsTextEdit);
281     d->serverResetCommandsLabel->setToolTip(d->serverResetCommandsTextEdit->toolTip());
282 
283     d->debuginfoPathChooser = new PathChooser(this);
284     d->debuginfoPathChooser->setPromptDialogTitle(tr("Select Location of Debugging Information"));
285     d->debuginfoPathChooser->setToolTip(tr(
286         "Base path for external debug information and debug sources. "
287         "If empty, $SYSROOT/usr/lib/debug will be chosen."));
288     d->debuginfoPathChooser->setHistoryCompleter("Debugger.DebugLocation.History");
289 
290     auto line = new QFrame(this);
291     line->setFrameShape(QFrame::HLine);
292     line->setFrameShadow(QFrame::Sunken);
293 
294     auto line2 = new QFrame(this);
295     line2->setFrameShape(QFrame::HLine);
296     line2->setFrameShadow(QFrame::Sunken);
297 
298     d->historyComboBox = new QComboBox(this);
299 
300     d->buttonBox = new QDialogButtonBox(this);
301     d->buttonBox->setStandardButtons(QDialogButtonBox::Cancel|QDialogButtonBox::Ok);
302     d->buttonBox->button(QDialogButtonBox::Ok)->setDefault(true);
303 
304     auto formLayout = new QFormLayout();
305     formLayout->setFieldGrowthPolicy(QFormLayout::AllNonFixedFieldsGrow);
306     formLayout->addRow(tr("&Kit:"), d->kitChooser);
307     formLayout->addRow(d->serverPortLabel, d->serverPortSpinBox);
308     formLayout->addRow(tr("Local &executable:"), d->localExecutablePathChooser);
309     formLayout->addRow(tr("Command line &arguments:"), d->arguments);
310     formLayout->addRow(tr("&Working directory:"), d->workingDirectory);
311     formLayout->addRow(tr("Run in &terminal:"), d->runInTerminalCheckBox);
312     formLayout->addRow(tr("Break at \"&main\":"), d->breakAtMainCheckBox);
313     formLayout->addRow(tr("Use target extended-remote to connect:"), d->useTargetExtendedRemoteCheckBox);
314     formLayout->addRow(d->sysRootLabel, d->sysRootPathChooser);
315     formLayout->addRow(d->serverInitCommandsLabel, d->serverInitCommandsTextEdit);
316     formLayout->addRow(d->serverResetCommandsLabel, d->serverResetCommandsTextEdit);
317     formLayout->addRow(tr("Debug &information:"), d->debuginfoPathChooser);
318     formLayout->addRow(d->channelOverrideHintLabel);
319     formLayout->addRow(d->channelOverrideLabel, d->channelOverrideEdit);
320     formLayout->addRow(line2);
321     formLayout->addRow(tr("&Recent:"), d->historyComboBox);
322 
323     auto verticalLayout = new QVBoxLayout(this);
324     verticalLayout->addLayout(formLayout);
325     verticalLayout->addStretch();
326     verticalLayout->addWidget(line);
327     verticalLayout->addWidget(d->buttonBox);
328 
329     connect(d->localExecutablePathChooser, &PathChooser::rawPathChanged,
330             this, &StartApplicationDialog::updateState);
331     connect(d->buttonBox, &QDialogButtonBox::accepted, this, &QDialog::accept);
332     connect(d->buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject);
333     connect(d->historyComboBox, QOverload<int>::of(&QComboBox::currentIndexChanged),
334             this, &StartApplicationDialog::historyIndexChanged);
335 
336     connect(d->channelOverrideEdit, &QLineEdit::textChanged,
337             this, &StartApplicationDialog::onChannelOverrideChanged);
338 
339     updateState();
340 }
341 
~StartApplicationDialog()342 StartApplicationDialog::~StartApplicationDialog()
343 {
344     delete d;
345 }
346 
setHistory(const QList<StartApplicationParameters> & l)347 void StartApplicationDialog::setHistory(const QList<StartApplicationParameters> &l)
348 {
349     d->historyComboBox->clear();
350     for (int i = l.size(); --i >= 0; ) {
351         const StartApplicationParameters &p = l.at(i);
352         if (!p.runnable.executable.isEmpty())
353             d->historyComboBox->addItem(p.displayName(), QVariant::fromValue(p));
354     }
355 }
356 
onChannelOverrideChanged(const QString & channel)357 void StartApplicationDialog::onChannelOverrideChanged(const QString &channel)
358 {
359     d->serverPortSpinBox->setEnabled(channel.isEmpty());
360     d->serverPortLabel->setEnabled(channel.isEmpty());
361 }
362 
historyIndexChanged(int index)363 void StartApplicationDialog::historyIndexChanged(int index)
364 {
365     if (index < 0)
366         return;
367     const QVariant v = d->historyComboBox->itemData(index);
368     QTC_ASSERT(v.canConvert<StartApplicationParameters>(), return);
369     setParameters(v.value<StartApplicationParameters>());
370 }
371 
updateState()372 void StartApplicationDialog::updateState()
373 {
374     bool okEnabled = d->localExecutablePathChooser->isValid();
375     d->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(okEnabled);
376 }
377 
run(bool attachRemote)378 void StartApplicationDialog::run(bool attachRemote)
379 {
380     const QString settingsGroup = "DebugMode";
381     const QString arrayName = "StartApplication";
382 
383     QList<StartApplicationParameters> history;
384     QSettings *settings = ICore::settings();
385     settings->beginGroup(settingsGroup);
386     if (const int arraySize = settings->beginReadArray(arrayName)) {
387         for (int i = 0; i < arraySize; ++i) {
388             settings->setArrayIndex(i);
389             StartApplicationParameters p;
390             p.fromSettings(settings);
391             history.append(p);
392         }
393     } else {
394         history.append(StartApplicationParameters());
395     }
396     settings->endArray();
397     settings->endGroup();
398 
399     StartApplicationDialog dialog(ICore::dialogParent());
400     dialog.setHistory(history);
401     dialog.setParameters(history.back());
402     if (!attachRemote) {
403         dialog.d->serverInitCommandsTextEdit->setVisible(false);
404         dialog.d->serverInitCommandsLabel->setVisible(false);
405         dialog.d->serverResetCommandsTextEdit->setVisible(false);
406         dialog.d->serverResetCommandsLabel->setVisible(false);
407         dialog.d->serverPortSpinBox->setVisible(false);
408         dialog.d->serverPortLabel->setVisible(false);
409         dialog.d->channelOverrideHintLabel->setVisible(false);
410         dialog.d->channelOverrideLabel->setVisible(false);
411         dialog.d->channelOverrideEdit->setVisible(false);
412     }
413     if (dialog.exec() != QDialog::Accepted)
414         return;
415 
416     Kit *k = dialog.d->kitChooser->currentKit();
417 
418     auto runControl = new RunControl(ProjectExplorer::Constants::DEBUG_RUN_MODE);
419     runControl->setKit(k);
420     auto debugger = new DebuggerRunTool(runControl);
421 
422     const StartApplicationParameters newParameters = dialog.parameters();
423     if (newParameters != history.back()) {
424         history.append(newParameters);
425         while (history.size() > 10)
426             history.takeFirst();
427         settings->beginGroup(settingsGroup);
428         settings->beginWriteArray(arrayName);
429         for (int i = 0; i < history.size(); ++i) {
430             settings->setArrayIndex(i);
431             history.at(i).toSettings(settings);
432         }
433         settings->endArray();
434         settings->endGroup();
435     }
436 
437     IDevice::ConstPtr dev = DeviceKitAspect::device(k);
438     Runnable inferior = newParameters.runnable;
439     const QString inputAddress = dialog.d->channelOverrideEdit->text();
440     if (!inputAddress.isEmpty())
441         debugger->setRemoteChannel(inputAddress);
442     else
443         debugger->setRemoteChannel(dev->sshParameters().host(), newParameters.serverPort);
444     debugger->setRunControlName(newParameters.displayName());
445     debugger->setBreakOnMain(newParameters.breakAtMain);
446     debugger->setDebugInfoLocation(newParameters.debugInfoLocation);
447     debugger->setInferior(inferior);
448     debugger->setCommandsAfterConnect(newParameters.serverInitCommands);
449     debugger->setCommandsForReset(newParameters.serverResetCommands);
450     debugger->setUseTerminal(newParameters.runInTerminal);
451     debugger->setUseExtendedRemote(newParameters.useTargetExtendedRemote);
452     if (!newParameters.sysRoot.isEmpty())
453         debugger->setSysRoot(newParameters.sysRoot);
454 
455     bool isLocal = !dev || (dev->type() == ProjectExplorer::Constants::DESKTOP_DEVICE_TYPE);
456     if (isLocal) // FIXME: Restriction needed?
457         debugger->setInferiorEnvironment(k->runEnvironment());
458 
459     if (!attachRemote)
460         debugger->setStartMode(isLocal ? StartExternal : StartRemoteProcess);
461 
462     if (attachRemote) {
463         debugger->setStartMode(AttachToRemoteServer);
464         debugger->setCloseMode(KillAtClose);
465         debugger->setUseContinueInsteadOfRun(true);
466         debugger->setRunControlName(tr("Attach to %1").arg(debugger->remoteChannel()));
467     }
468     debugger->startRunControl();
469 }
470 
attachToRemoteServer()471 void StartApplicationDialog::attachToRemoteServer()
472 {
473     run(true);
474 }
475 
startAndDebugApplication()476 void StartApplicationDialog::startAndDebugApplication()
477 {
478     run(false);
479 }
480 
parameters() const481 StartApplicationParameters StartApplicationDialog::parameters() const
482 {
483     StartApplicationParameters result;
484     result.serverPort = d->serverPortSpinBox->value();
485     result.serverAddress = d->channelOverrideEdit->text();
486     result.runnable.executable = d->localExecutablePathChooser->filePath();
487     result.sysRoot = d->sysRootPathChooser->filePath();
488     result.serverInitCommands = d->serverInitCommandsTextEdit->toPlainText();
489     result.serverResetCommands = d->serverResetCommandsTextEdit->toPlainText();
490     result.kitId = d->kitChooser->currentKitId();
491     result.debugInfoLocation = d->debuginfoPathChooser->filePath().toString();
492     result.runnable.commandLineArguments = d->arguments->text();
493     result.runnable.workingDirectory = d->workingDirectory->filePath().toString();
494     result.breakAtMain = d->breakAtMainCheckBox->isChecked();
495     result.runInTerminal = d->runInTerminalCheckBox->isChecked();
496     result.useTargetExtendedRemote = d->useTargetExtendedRemoteCheckBox->isChecked();
497     return result;
498 }
499 
setParameters(const StartApplicationParameters & p)500 void StartApplicationDialog::setParameters(const StartApplicationParameters &p)
501 {
502     d->kitChooser->setCurrentKitId(p.kitId);
503     d->serverPortSpinBox->setValue(p.serverPort);
504     d->channelOverrideEdit->setText(p.serverAddress);
505     d->localExecutablePathChooser->setFilePath(p.runnable.executable);
506     d->sysRootPathChooser->setFilePath(p.sysRoot);
507     d->serverInitCommandsTextEdit->setPlainText(p.serverInitCommands);
508     d->serverResetCommandsTextEdit->setPlainText(p.serverResetCommands);
509     d->debuginfoPathChooser->setPath(p.debugInfoLocation);
510     d->arguments->setText(p.runnable.commandLineArguments);
511     d->workingDirectory->setPath(p.runnable.workingDirectory);
512     d->breakAtMainCheckBox->setChecked(p.breakAtMain);
513     d->runInTerminalCheckBox->setChecked(p.runInTerminal);
514     d->useTargetExtendedRemoteCheckBox->setChecked(p.useTargetExtendedRemote);
515     updateState();
516 }
517 
518 ///////////////////////////////////////////////////////////////////////
519 //
520 // AttachToQmlPortDialog
521 //
522 ///////////////////////////////////////////////////////////////////////
523 
524 class AttachToQmlPortDialogPrivate
525 {
526 public:
527     QSpinBox *portSpinBox;
528     KitChooser *kitChooser;
529 };
530 
AttachToQmlPortDialog(QWidget * parent)531 AttachToQmlPortDialog::AttachToQmlPortDialog(QWidget *parent)
532   : QDialog(parent),
533     d(new AttachToQmlPortDialogPrivate)
534 {
535     setWindowTitle(tr("Start Debugger"));
536 
537     d->kitChooser = new KitChooser(this);
538     d->kitChooser->setShowIcons(true);
539     d->kitChooser->populate();
540 
541     d->portSpinBox = new QSpinBox(this);
542     d->portSpinBox->setMaximum(65535);
543     d->portSpinBox->setValue(3768);
544 
545     auto buttonBox = new QDialogButtonBox(this);
546     buttonBox->setStandardButtons(QDialogButtonBox::Cancel|QDialogButtonBox::Ok);
547     buttonBox->button(QDialogButtonBox::Ok)->setDefault(true);
548 
549     auto formLayout = new QFormLayout();
550     formLayout->addRow(tr("Kit:"), d->kitChooser);
551     formLayout->addRow(tr("&Port:"), d->portSpinBox);
552 
553     auto verticalLayout = new QVBoxLayout(this);
554     verticalLayout->addLayout(formLayout);
555     verticalLayout->addWidget(buttonBox);
556 
557     connect(buttonBox, &QDialogButtonBox::accepted, this, &QDialog::accept);
558     connect(buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject);
559 }
560 
~AttachToQmlPortDialog()561 AttachToQmlPortDialog::~AttachToQmlPortDialog()
562 {
563     delete d;
564 }
565 
setPort(const int port)566 void AttachToQmlPortDialog::setPort(const int port)
567 {
568     d->portSpinBox->setValue(port);
569 }
570 
port() const571 int AttachToQmlPortDialog::port() const
572 {
573     return d->portSpinBox->value();
574 }
575 
kit() const576 Kit *AttachToQmlPortDialog::kit() const
577 {
578     return d->kitChooser->currentKit();
579 }
580 
setKitId(Id id)581 void AttachToQmlPortDialog::setKitId(Id id)
582 {
583     d->kitChooser->setCurrentKitId(id);
584 }
585 
586 // --------- StartRemoteCdbDialog
cdbRemoteHelp()587 static QString cdbRemoteHelp()
588 {
589     const char cdbConnectionSyntax[] =
590             "Server:Port<br>"
591             "tcp:server=Server,port=Port[,password=Password][,ipversion=6]\n"
592             "tcp:clicon=Server,port=Port[,password=Password][,ipversion=6]\n"
593             "npipe:server=Server,pipe=PipeName[,password=Password]\n"
594             "com:port=COMPort,baud=BaudRate,channel=COMChannel[,password=Password]\n"
595             "spipe:proto=Protocol,{certuser=Cert|machuser=Cert},server=Server,pipe=PipeName[,password=Password]\n"
596             "ssl:proto=Protocol,{certuser=Cert|machuser=Cert},server=Server,port=Socket[,password=Password]\n"
597             "ssl:proto=Protocol,{certuser=Cert|machuser=Cert},clicon=Server,port=Socket[,password=Password]";
598 
599     const QString ext32 = QDir::toNativeSeparators(CdbEngine::extensionLibraryName(false));
600     const QString ext64 = QDir::toNativeSeparators(CdbEngine::extensionLibraryName(true));
601     return  StartRemoteCdbDialog::tr(
602                 "<html><body><p>The remote CDB needs to load the matching %1 CDB extension "
603                 "(<code>%2</code> or <code>%3</code>, respectively).</p><p>Copy it onto the remote machine and set the "
604                 "environment variable <code>%4</code> to point to its folder.</p><p>"
605                 "Launch the remote CDB as <code>%5 &lt;executable&gt;</code> "
606                 "to use TCP/IP as communication protocol.</p><p>Enter the connection parameters as:</p>"
607                 "<pre>%6</pre></body></html>")
608             .arg(QString(Core::Constants::IDE_DISPLAY_NAME),
609                  ext32, ext64, QString("_NT_DEBUGGER_EXTENSION_PATH"),
610                  QString("cdb.exe -server tcp:port=1234"),
611                  QString(cdbConnectionSyntax));
612 }
613 
StartRemoteCdbDialog(QWidget * parent)614 StartRemoteCdbDialog::StartRemoteCdbDialog(QWidget *parent) :
615     QDialog(parent), m_lineEdit(new QLineEdit)
616 {
617     setWindowTitle(tr("Start a CDB Remote Session"));
618 
619     auto groupBox = new QGroupBox;
620 
621     auto helpLabel = new QLabel(cdbRemoteHelp());
622     helpLabel->setWordWrap(true);
623     helpLabel->setTextInteractionFlags(Qt::TextBrowserInteraction);
624 
625     auto label = new QLabel(tr("&Connection:"));
626     label->setBuddy(m_lineEdit);
627     m_lineEdit->setMinimumWidth(400);
628 
629     auto box = new QDialogButtonBox(QDialogButtonBox::Ok|QDialogButtonBox::Cancel);
630 
631     auto formLayout = new QFormLayout;
632     formLayout->addRow(helpLabel);
633     formLayout->addRow(label, m_lineEdit);
634     groupBox->setLayout(formLayout);
635 
636     auto vLayout = new QVBoxLayout(this);
637     vLayout->addWidget(groupBox);
638     vLayout->addWidget(box);
639 
640     m_okButton = box->button(QDialogButtonBox::Ok);
641     m_okButton->setEnabled(false);
642 
643     connect(m_lineEdit, &QLineEdit::textChanged,
644             this, &StartRemoteCdbDialog::textChanged);
645     connect(m_lineEdit, &QLineEdit::returnPressed,
646             [this] { m_okButton->animateClick(); });
647     connect(box, &QDialogButtonBox::accepted,
648             this, &StartRemoteCdbDialog::accept);
649     connect(box, &QDialogButtonBox::rejected,
650             this, &QDialog::reject);
651 }
652 
accept()653 void StartRemoteCdbDialog::accept()
654 {
655     if (!m_lineEdit->text().isEmpty())
656         QDialog::accept();
657 }
658 
659 StartRemoteCdbDialog::~StartRemoteCdbDialog() = default;
660 
textChanged(const QString & t)661 void StartRemoteCdbDialog::textChanged(const QString &t)
662 {
663     m_okButton->setEnabled(!t.isEmpty());
664 }
665 
connection() const666 QString StartRemoteCdbDialog::connection() const
667 {
668     const QString rc = m_lineEdit->text();
669     // Transform an IP:POrt ('localhost:1234') specification into full spec
670     QRegularExpression ipRegexp("([\\w\\.\\-_]+):([0-9]{1,4})");
671     QTC_ASSERT(ipRegexp.isValid(), return QString());
672     const QRegularExpressionMatch match = ipRegexp.match(rc);
673     if (match.hasMatch())
674         return QString::fromLatin1("tcp:server=%1,port=%2").arg(match.captured(1), match.captured(2));
675     return rc;
676 }
677 
setConnection(const QString & c)678 void StartRemoteCdbDialog::setConnection(const QString &c)
679 {
680     m_lineEdit->setText(c);
681     m_okButton->setEnabled(!c.isEmpty());
682 }
683 
AddressDialog(QWidget * parent)684 AddressDialog::AddressDialog(QWidget *parent) :
685         QDialog(parent),
686         m_lineEdit(new QLineEdit),
687         m_box(new QDialogButtonBox(QDialogButtonBox::Ok|QDialogButtonBox::Cancel))
688 {
689     setWindowTitle(tr("Select Start Address"));
690 
691     auto hLayout = new QHBoxLayout;
692     hLayout->addWidget(new QLabel(tr("Enter an address:") + ' '));
693     hLayout->addWidget(m_lineEdit);
694 
695     auto vLayout = new QVBoxLayout;
696     vLayout->addLayout(hLayout);
697     vLayout->addWidget(m_box);
698     setLayout(vLayout);
699 
700     connect(m_box, &QDialogButtonBox::accepted, this, &AddressDialog::accept);
701     connect(m_box, &QDialogButtonBox::rejected, this, &QDialog::reject);
702     connect(m_lineEdit, &QLineEdit::returnPressed, this, &AddressDialog::accept);
703     connect(m_lineEdit, &QLineEdit::textChanged, this, &AddressDialog::textChanged);
704 
705     setOkButtonEnabled(false);
706 }
707 
setOkButtonEnabled(bool v)708 void AddressDialog::setOkButtonEnabled(bool v)
709 {
710     m_box->button(QDialogButtonBox::Ok)->setEnabled(v);
711 }
712 
isOkButtonEnabled() const713 bool AddressDialog::isOkButtonEnabled() const
714 {
715     return m_box->button(QDialogButtonBox::Ok)->isEnabled();
716 }
717 
setAddress(quint64 a)718 void AddressDialog::setAddress(quint64 a)
719 {
720     m_lineEdit->setText("0x" + QString::number(a, 16));
721 }
722 
address() const723 quint64 AddressDialog::address() const
724 {
725     return m_lineEdit->text().toULongLong(nullptr, 16);
726 }
727 
accept()728 void AddressDialog::accept()
729 {
730     if (isOkButtonEnabled())
731         QDialog::accept();
732 }
733 
textChanged()734 void AddressDialog::textChanged()
735 {
736     setOkButtonEnabled(isValid());
737 }
738 
isValid() const739 bool AddressDialog::isValid() const
740 {
741     const QString text = m_lineEdit->text();
742     bool ok = false;
743     text.toULongLong(&ok, 16);
744     return ok;
745 }
746 
747 ///////////////////////////////////////////////////////////////////////
748 //
749 // StartRemoteEngineDialog
750 //
751 ///////////////////////////////////////////////////////////////////////
752 
753 class StartRemoteEngineDialogPrivate
754 {
755 public:
756     FancyLineEdit *host;
757     FancyLineEdit *username;
758     QLineEdit *password;
759     FancyLineEdit *enginePath;
760     FancyLineEdit *inferiorPath;
761     QDialogButtonBox *buttonBox;
762 };
763 
StartRemoteEngineDialog(QWidget * parent)764 StartRemoteEngineDialog::StartRemoteEngineDialog(QWidget *parent)
765     : QDialog(parent), d(new StartRemoteEngineDialogPrivate)
766 {
767     setWindowTitle(tr("Start Remote Engine"));
768 
769     d->host = new FancyLineEdit(this);
770     d->host->setHistoryCompleter("HostName");
771 
772     d->username = new FancyLineEdit(this);
773     d->username->setHistoryCompleter("UserName");
774 
775     d->password = new QLineEdit(this);
776     d->password->setEchoMode(QLineEdit::Password);
777 
778     d->enginePath = new FancyLineEdit(this);
779     d->enginePath->setHistoryCompleter("EnginePath");
780 
781     d->inferiorPath = new FancyLineEdit(this);
782     d->inferiorPath->setHistoryCompleter("InferiorPath");
783 
784     d->buttonBox = new QDialogButtonBox(this);
785     d->buttonBox->setStandardButtons(QDialogButtonBox::Cancel|QDialogButtonBox::Ok);
786 
787     auto formLayout = new QFormLayout();
788     formLayout->addRow(tr("&Host:"), d->host);
789     formLayout->addRow(tr("&Username:"), d->username);
790     formLayout->addRow(tr("&Password:"), d->password);
791     formLayout->addRow(tr("&Engine path:"), d->enginePath);
792     formLayout->addRow(tr("&Inferior path:"), d->inferiorPath);
793 
794     auto verticalLayout = new QVBoxLayout(this);
795     verticalLayout->addLayout(formLayout);
796     verticalLayout->addItem(new QSpacerItem(0, 0, QSizePolicy::Minimum, QSizePolicy::Expanding));
797     verticalLayout->addWidget(d->buttonBox);
798 
799     connect(d->buttonBox, &QDialogButtonBox::accepted, this, &QDialog::accept);
800     connect(d->buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject);
801 }
802 
~StartRemoteEngineDialog()803 StartRemoteEngineDialog::~StartRemoteEngineDialog()
804 {
805     delete d;
806 }
807 
host() const808 QString StartRemoteEngineDialog::host() const
809 {
810     return d->host->text();
811 }
812 
username() const813 QString StartRemoteEngineDialog::username() const
814 {
815     return d->username->text();
816 }
817 
password() const818 QString StartRemoteEngineDialog::password() const
819 {
820     return d->password->text();
821 }
822 
inferiorPath() const823 QString StartRemoteEngineDialog::inferiorPath() const
824 {
825     return d->inferiorPath->text();
826 }
827 
enginePath() const828 QString StartRemoteEngineDialog::enginePath() const
829 {
830     return d->enginePath->text();
831 }
832 
833 ///////////////////////////////////////////////////////////////////////
834 //
835 // TypeFormatsDialogUi
836 //
837 ///////////////////////////////////////////////////////////////////////
838 
839 class TypeFormatsDialogPage : public QWidget
840 {
841 public:
TypeFormatsDialogPage()842     TypeFormatsDialogPage()
843     {
844         m_layout = new QGridLayout;
845         m_layout->setColumnStretch(0, 2);
846         auto vboxLayout = new QVBoxLayout;
847         vboxLayout->addLayout(m_layout);
848         vboxLayout->addItem(new QSpacerItem(1, 1, QSizePolicy::Ignored,
849                                             QSizePolicy::MinimumExpanding));
850         setLayout(vboxLayout);
851     }
852 
addTypeFormats(const QString & type,const DisplayFormats & typeFormats,int current)853     void addTypeFormats(const QString &type,
854         const DisplayFormats &typeFormats, int current)
855     {
856         const int row = m_layout->rowCount();
857         int column = 0;
858         auto group = new QButtonGroup(this);
859         m_layout->addWidget(new QLabel(type), row, column++);
860         for (int i = -1; i != typeFormats.size(); ++i) {
861             auto choice = new QRadioButton(this);
862             choice->setText(i == -1 ? TypeFormatsDialog::tr("Reset")
863                                     : WatchHandler::nameForFormat(typeFormats.at(i)));
864             m_layout->addWidget(choice, row, column++);
865             if (i == current)
866                 choice->setChecked(true);
867             group->addButton(choice, i);
868         }
869     }
870 private:
871     QGridLayout *m_layout;
872 };
873 
874 class TypeFormatsDialogUi
875 {
876 public:
TypeFormatsDialogUi(TypeFormatsDialog * q)877     TypeFormatsDialogUi(TypeFormatsDialog *q)
878     {
879         tabs = new QTabWidget(q);
880 
881         buttonBox = new QDialogButtonBox(q);
882         buttonBox->setStandardButtons(QDialogButtonBox::Cancel|QDialogButtonBox::Ok);
883 
884         auto layout = new QVBoxLayout(q);
885         layout->addWidget(tabs);
886         layout->addWidget(buttonBox);
887         q->setLayout(layout);
888     }
889 
addPage(const QString & name)890     void addPage(const QString &name)
891     {
892         auto page = new TypeFormatsDialogPage;
893         pages.append(page);
894         auto scroller = new QScrollArea;
895         scroller->setWidgetResizable(true);
896         scroller->setWidget(page);
897         scroller->setFrameStyle(QFrame::NoFrame);
898         tabs->addTab(scroller, name);
899     }
900 
901 public:
902     QList<TypeFormatsDialogPage *> pages;
903     QDialogButtonBox *buttonBox;
904 
905 private:
906     QTabWidget *tabs;
907 };
908 
909 
910 ///////////////////////////////////////////////////////////////////////
911 //
912 // TypeFormatsDialog
913 //
914 ///////////////////////////////////////////////////////////////////////
915 
TypeFormatsDialog(QWidget * parent)916 TypeFormatsDialog::TypeFormatsDialog(QWidget *parent)
917    : QDialog(parent), m_ui(new TypeFormatsDialogUi(this))
918 {
919     setWindowTitle(tr("Type Formats"));
920     m_ui->addPage(tr("Qt Types"));
921     m_ui->addPage(tr("Standard Types"));
922     m_ui->addPage(tr("Misc Types"));
923 
924     connect(m_ui->buttonBox, &QDialogButtonBox::accepted, this, &QDialog::accept);
925     connect(m_ui->buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject);
926 }
927 
~TypeFormatsDialog()928 TypeFormatsDialog::~TypeFormatsDialog()
929 {
930     delete m_ui;
931 }
932 
addTypeFormats(const QString & type0,const DisplayFormats & typeFormats,int current)933 void TypeFormatsDialog::addTypeFormats(const QString &type0,
934     const DisplayFormats &typeFormats, int current)
935 {
936     QString type = type0;
937     type.replace("__", "::");
938     int pos = 2;
939     if (type.startsWith('Q'))
940         pos = 0;
941     else if (type.startsWith("std::"))
942         pos = 1;
943     m_ui->pages[pos]->addTypeFormats(type, typeFormats, current);
944 }
945 
946 } // namespace Internal
947 } // namespace Debugger
948