File indexing completed on 2024-04-28 05:48:31
0001 // 0002 // configview.cpp 0003 // 0004 // Description: View for configuring the set of targets to be used with the debugger 0005 // 0006 // 0007 // SPDX-FileCopyrightText: 2010 Ian Wakeling <ian.wakeling@ntlworld.com> 0008 // SPDX-FileCopyrightText: 2012 Kåre Särs <kare.sars@iki.fi> 0009 // 0010 // SPDX-License-Identifier: LGPL-2.0-only 0011 0012 #include "configview.h" 0013 0014 #include <QCheckBox> 0015 #include <QCompleter> 0016 #include <QFileDialog> 0017 #include <QFileSystemModel> 0018 #include <QJsonArray> 0019 #include <QJsonDocument> 0020 #include <QLayout> 0021 #include <QPushButton> 0022 #include <QSpinBox> 0023 #include <QStandardPaths> 0024 0025 #include <KTextEditor/Document> 0026 #include <KTextEditor/View> 0027 0028 #include <KActionCollection> 0029 #include <KConfigGroup> 0030 #include <KLocalizedString> 0031 #include <KSelectAction> 0032 0033 #include "advanced_settings.h" 0034 #include "dap/settings.h" 0035 #include "json_placeholders.h" 0036 #include "plugin_kategdb.h" 0037 #include <json_utils.h> 0038 0039 constexpr int CONFIG_VERSION = 5; 0040 const static QString F_TARGET = QStringLiteral("target"); 0041 const static QString F_DEBUGGER = QStringLiteral("debuggerKey"); 0042 const static QString F_PROFILE = QStringLiteral("debuggerProfile"); 0043 const static QString F_FILE = QStringLiteral("file"); 0044 const static QString F_WORKDIR = QStringLiteral("workdir"); 0045 const static QString F_ARGS = QStringLiteral("args"); 0046 const static QString F_PID = QStringLiteral("pid"); 0047 0048 void ConfigView::refreshUI() 0049 { 0050 // first false then true to make sure a layout is set 0051 m_useBottomLayout = false; 0052 resizeEvent(nullptr); 0053 m_useBottomLayout = true; 0054 resizeEvent(nullptr); 0055 } 0056 0057 std::optional<QJsonDocument> loadJSON(const QString &path) 0058 { 0059 QFile file(path); 0060 if (!file.open(QIODevice::ReadOnly)) { 0061 return std::nullopt; 0062 } 0063 QJsonParseError error; 0064 const auto json = QJsonDocument::fromJson(file.readAll(), &error); 0065 file.close(); 0066 if (error.error != QJsonParseError::NoError) { 0067 return std::nullopt; 0068 } 0069 return json; 0070 } 0071 0072 ConfigView::ConfigView(QWidget *parent, KTextEditor::MainWindow *mainWin, KatePluginGDB *plugin) 0073 : QWidget(parent) 0074 , m_mainWindow(mainWin) 0075 { 0076 m_clientCombo = new QComboBox(this); 0077 m_clientCombo->setEditable(false); 0078 m_clientCombo->addItem(QStringLiteral("GDB")); 0079 m_clientCombo->insertSeparator(1); 0080 m_dapConfigPath = plugin->configPath(); 0081 readDAPSettings(); 0082 0083 m_targetCombo = new QComboBox(this); 0084 m_targetCombo->setEditable(true); 0085 // don't let Qt insert items when the user edits; new targets are only 0086 // added when the user explicitly says so 0087 m_targetCombo->setInsertPolicy(QComboBox::NoInsert); 0088 m_targetCombo->setDuplicatesEnabled(true); 0089 0090 m_addTarget = new QToolButton(this); 0091 m_addTarget->setIcon(QIcon::fromTheme(QStringLiteral("document-new"))); 0092 m_addTarget->setToolTip(i18n("Add new target")); 0093 0094 m_copyTarget = new QToolButton(this); 0095 m_copyTarget->setIcon(QIcon::fromTheme(QStringLiteral("edit-copy"))); 0096 m_copyTarget->setToolTip(i18n("Copy target")); 0097 0098 m_deleteTarget = new QToolButton(this); 0099 m_deleteTarget->setIcon(QIcon::fromTheme(QStringLiteral("edit-delete"))); 0100 m_deleteTarget->setToolTip(i18n("Delete target")); 0101 0102 m_line = new QFrame(this); 0103 m_line->setFrameShadow(QFrame::Sunken); 0104 0105 m_execLabel = new QLabel(i18n("Executable:")); 0106 m_execLabel->setBuddy(m_targetCombo); 0107 0108 m_executable = new QLineEdit(this); 0109 QCompleter *completer1 = new QCompleter(this); 0110 QFileSystemModel *model = new QFileSystemModel(this); 0111 model->setFilter(QDir::AllDirs | QDir::NoDotAndDotDot); 0112 completer1->setModel(model); 0113 m_executable->setCompleter(completer1); 0114 m_executable->setClearButtonEnabled(true); 0115 m_browseExe = new QToolButton(this); 0116 m_browseExe->setIcon(QIcon::fromTheme(QStringLiteral("application-x-executable"))); 0117 0118 m_workingDirectory = new QLineEdit(this); 0119 QCompleter *completer2 = new QCompleter(this); 0120 QFileSystemModel *model2 = new QFileSystemModel(completer2); 0121 0122 completer2->setModel(model2); 0123 m_workingDirectory->setCompleter(completer2); 0124 m_workingDirectory->setClearButtonEnabled(true); 0125 m_workDirLabel = new QLabel(i18n("Working Directory:")); 0126 m_workDirLabel->setBuddy(m_workingDirectory); 0127 m_browseDir = new QToolButton(this); 0128 m_browseDir->setIcon(QIcon::fromTheme(QStringLiteral("inode-directory"))); 0129 0130 m_processId = new QSpinBox(this); 0131 m_processId->setMinimum(1); 0132 m_processId->setMaximum(4194304); 0133 m_processIdLabel = new QLabel(i18n("Process Id:"), this); 0134 m_processIdLabel->setBuddy(m_processId); 0135 0136 m_arguments = new QLineEdit(this); 0137 m_arguments->setClearButtonEnabled(true); 0138 m_argumentsLabel = new QLabel(i18nc("Program argument list", "Arguments:")); 0139 m_argumentsLabel->setBuddy(m_arguments); 0140 0141 m_takeFocus = new QCheckBox(i18nc("Checkbox to for keeping focus on the command line", "Keep focus")); 0142 m_takeFocus->setToolTip(i18n("Keep the focus on the command line")); 0143 0144 m_redirectTerminal = new QCheckBox(i18n("Redirect IO")); 0145 m_redirectTerminal->setToolTip(i18n("Redirect the debugged programs IO to a separate tab")); 0146 0147 m_advancedSettings = new QPushButton(i18n("Advanced Settings")); 0148 0149 m_checBoxLayout = nullptr; 0150 0151 // ensure layout is set 0152 refreshUI(); 0153 0154 m_advanced = new AdvancedGDBSettings(this); 0155 m_advanced->hide(); 0156 0157 connect(m_targetCombo, &QComboBox::editTextChanged, this, &ConfigView::slotTargetEdited); 0158 connect(m_targetCombo, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this, &ConfigView::slotTargetSelected); 0159 connect(m_addTarget, &QToolButton::clicked, this, &ConfigView::slotAddTarget); 0160 connect(m_copyTarget, &QToolButton::clicked, this, &ConfigView::slotCopyTarget); 0161 connect(m_deleteTarget, &QToolButton::clicked, this, &ConfigView::slotDeleteTarget); 0162 connect(m_browseExe, &QToolButton::clicked, this, &ConfigView::slotBrowseExec); 0163 connect(m_browseDir, &QToolButton::clicked, this, &ConfigView::slotBrowseDir); 0164 connect(m_redirectTerminal, &QCheckBox::toggled, this, &ConfigView::showIO); 0165 connect(m_advancedSettings, &QPushButton::clicked, this, &ConfigView::slotAdvancedClicked); 0166 0167 connect(m_clientCombo, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &ConfigView::refreshUI); 0168 } 0169 0170 ConfigView::~ConfigView() 0171 { 0172 } 0173 0174 void ConfigView::readDAPSettings() 0175 { 0176 // read servers file 0177 const auto json = loadJSON(QStringLiteral(":/debugger/dap.json")); 0178 if (!json) 0179 return; 0180 0181 auto baseObject = json->object(); 0182 0183 { 0184 const QString settingsPath = m_dapConfigPath.toLocalFile(); 0185 0186 const auto userJson = loadJSON(settingsPath); 0187 if (userJson) { 0188 baseObject = json::merge(baseObject, userJson->object()); 0189 } 0190 } 0191 0192 const auto servers = baseObject[QStringLiteral("dap")].toObject(); 0193 0194 int index = m_clientCombo->count(); 0195 0196 for (auto itServer = servers.constBegin(); itServer != servers.constEnd(); ++itServer) { 0197 const auto server = itServer.value().toObject(); 0198 const auto jsonProfiles = dap::settings::expandConfigurations(server); 0199 if (!jsonProfiles) 0200 continue; 0201 0202 QHash<QString, DAPAdapterSettings> profiles; 0203 0204 for (auto itProfile = jsonProfiles->constBegin(); itProfile != jsonProfiles->constEnd(); ++itProfile) { 0205 profiles[itProfile.key()] = DAPAdapterSettings(); 0206 DAPAdapterSettings &conf = profiles[itProfile.key()]; 0207 conf.settings = itProfile->toObject(); 0208 conf.index = index++; 0209 0210 QSet<QString> variables; 0211 json::findVariables(conf.settings, variables); 0212 0213 for (const auto &var : variables) { 0214 if (var.startsWith(QStringLiteral("#"))) 0215 continue; 0216 conf.variables.append(var); 0217 } 0218 0219 m_clientCombo->addItem(QStringLiteral("%1 | %2").arg(itServer.key()).arg(itProfile.key()), conf.variables); 0220 } 0221 0222 m_dapAdapterSettings[itServer.key()] = profiles; 0223 0224 m_clientCombo->insertSeparator(index++); 0225 } 0226 } 0227 0228 void ConfigView::registerActions(KActionCollection *actionCollection) 0229 { 0230 m_targetSelectAction = actionCollection->add<KSelectAction>(QStringLiteral("targets")); 0231 m_targetSelectAction->setText(i18n("Targets")); 0232 connect(m_targetSelectAction, &KSelectAction::indexTriggered, this, &ConfigView::slotTargetSelected); 0233 } 0234 0235 void upgradeConfigV1_3(QStringList &targetConfStrs) 0236 { 0237 if (targetConfStrs.count() == 3) { 0238 // valid old style config, translate it now; note the 0239 // reordering happening here! 0240 QStringList temp; 0241 temp << targetConfStrs[2]; 0242 temp << targetConfStrs[1]; 0243 targetConfStrs.swap(temp); 0244 } 0245 } 0246 0247 void upgradeConfigV3_4(QStringList &targetConfStrs, const QStringList &args) 0248 { 0249 targetConfStrs.prepend(targetConfStrs[0].right(15)); 0250 0251 const QString targetName(QStringLiteral("%1<%2>")); 0252 0253 for (int i = 0; i < args.size(); ++i) { 0254 const QString &argStr = args.at(i); 0255 if (i > 0) { 0256 // copy the firsts and change the arguments 0257 targetConfStrs[0] = targetName.arg(targetConfStrs[0]).arg(i + 1); 0258 if (targetConfStrs.count() > 3) { 0259 targetConfStrs[3] = argStr; 0260 } 0261 } 0262 } 0263 } 0264 0265 void upgradeConfigV4_5(QStringList targetConfStrs, QJsonObject &conf) 0266 { 0267 typedef ConfigView::TargetStringOrder I; 0268 0269 while (targetConfStrs.count() < I::CustomStartIndex) { 0270 targetConfStrs << QString(); 0271 } 0272 0273 auto insertField = [&conf, targetConfStrs](const QString &field, I index) { 0274 const QString value = targetConfStrs[index].trimmed(); 0275 if (!value.isEmpty()) { 0276 conf[field] = value; 0277 } 0278 }; 0279 0280 // read fields 0281 insertField(F_TARGET, I::NameIndex); 0282 insertField(F_FILE, I::ExecIndex); 0283 insertField(F_WORKDIR, I::WorkDirIndex); 0284 insertField(F_ARGS, I::ArgsIndex); 0285 // read advanced settings 0286 for (int i = 0; i < I::GDBIndex; ++i) { 0287 targetConfStrs.takeFirst(); 0288 } 0289 const auto advanced = AdvancedGDBSettings::upgradeConfigV4_5(targetConfStrs); 0290 if (!advanced.isEmpty()) { 0291 conf[QStringLiteral("advanced")] = advanced; 0292 } 0293 } 0294 0295 QByteArray serialize(const QJsonObject obj) 0296 { 0297 const QJsonDocument doc(obj); 0298 return doc.toJson(QJsonDocument::Compact); 0299 } 0300 0301 QJsonObject unserialize(const QString map) 0302 { 0303 const auto doc = QJsonDocument::fromJson(map.toLatin1()); 0304 return doc.object(); 0305 } 0306 0307 void ConfigView::readConfig(const KConfigGroup &group) 0308 { 0309 m_targetCombo->clear(); 0310 0311 const int version = group.readEntry(QStringLiteral("version"), CONFIG_VERSION); 0312 const int targetCount = group.readEntry(QStringLiteral("targetCount"), 1); 0313 int lastTarget = group.readEntry(QStringLiteral("lastTarget"), 0); 0314 const QString targetKey(QStringLiteral("target_%1")); 0315 0316 QStringList args; 0317 if (version < 4) { 0318 const int argsListsCount = group.readEntry(QStringLiteral("argsCount"), 0); 0319 const QString argsKey(QStringLiteral("args_%1")); 0320 const QString targetName(QStringLiteral("%1<%2>")); 0321 0322 for (int nArg = 0; nArg < argsListsCount; ++nArg) { 0323 const QString argStr = group.readEntry(argsKey.arg(nArg), QString()); 0324 } 0325 } 0326 0327 for (int i = 0; i < targetCount; i++) { 0328 QJsonObject targetConf; 0329 0330 if (version < 5) { 0331 QStringList targetConfStrs; 0332 targetConfStrs = group.readEntry(targetKey.arg(i), QStringList()); 0333 if (targetConfStrs.count() == 0) { 0334 continue; 0335 } 0336 0337 if (version == 1) { 0338 upgradeConfigV1_3(targetConfStrs); 0339 } 0340 0341 if (version < 4) { 0342 upgradeConfigV3_4(targetConfStrs, args); 0343 } 0344 if (version < 5) { 0345 upgradeConfigV4_5(targetConfStrs, targetConf); 0346 } 0347 } else { 0348 const QString data = group.readEntry(targetKey.arg(i), QString()); 0349 targetConf = unserialize(data); 0350 } 0351 0352 if (!targetConf.isEmpty()) { 0353 m_targetCombo->addItem(targetConf[QStringLiteral("target")].toString(), targetConf); 0354 } 0355 } 0356 0357 // make sure there is at least one item. 0358 if (m_targetCombo->count() == 0) { 0359 slotAddTarget(); 0360 } 0361 0362 QStringList targetNames; 0363 for (int i = 0; i < m_targetCombo->count(); i++) { 0364 targetNames << m_targetCombo->itemText(i); 0365 } 0366 m_targetSelectAction->setItems(targetNames); 0367 0368 if (lastTarget < 0 || lastTarget >= m_targetCombo->count()) { 0369 lastTarget = 0; 0370 } 0371 m_targetCombo->setCurrentIndex(lastTarget); 0372 0373 m_takeFocus->setChecked(group.readEntry("alwaysFocusOnInput", false)); 0374 0375 m_redirectTerminal->setChecked(group.readEntry("redirectTerminal", false)); 0376 } 0377 0378 void ConfigView::writeConfig(KConfigGroup &group) 0379 { 0380 // make sure the data is up to date before writing 0381 saveCurrentToIndex(m_currentTarget); 0382 0383 group.writeEntry("version", CONFIG_VERSION); 0384 0385 QString targetKey(QStringLiteral("target_%1")); 0386 0387 group.writeEntry("targetCount", m_targetCombo->count()); 0388 group.writeEntry("lastTarget", m_targetCombo->currentIndex()); 0389 for (int i = 0; i < m_targetCombo->count(); i++) { 0390 QJsonObject targetConf = m_targetCombo->itemData(i).toJsonObject(); 0391 group.writeEntry(targetKey.arg(i), serialize(targetConf)); 0392 } 0393 0394 group.writeEntry("alwaysFocusOnInput", m_takeFocus->isChecked()); 0395 group.writeEntry("redirectTerminal", m_redirectTerminal->isChecked()); 0396 } 0397 0398 const GDBTargetConf ConfigView::currentGDBTarget() const 0399 { 0400 GDBTargetConf cfg; 0401 cfg.targetName = m_targetCombo->currentText(); 0402 cfg.executable = m_executable->text(); 0403 cfg.workDir = m_workingDirectory->text(); 0404 cfg.arguments = m_arguments->text(); 0405 0406 const auto advancedConfig = m_advanced->configs(); 0407 { 0408 cfg.gdbCmd = advancedConfig[AdvancedGDBSettings::F_GDB].toString(QStringLiteral("gdb")); 0409 cfg.srcPaths.clear(); 0410 for (const auto &value : advancedConfig[AdvancedGDBSettings::F_SRC_PATHS].toArray()) { 0411 cfg.srcPaths << value.toString(); 0412 } 0413 cfg.customInit = AdvancedGDBSettings::commandList(advancedConfig); 0414 } 0415 0416 return cfg; 0417 } 0418 0419 const DAPTargetConf ConfigView::currentDAPTarget(bool full) const 0420 { 0421 DAPTargetConf cfg; 0422 cfg.targetName = m_targetCombo->currentText(); 0423 0424 const int comboIndex = m_clientCombo->currentIndex(); 0425 bool found = false; 0426 // find config 0427 for (auto itS = m_dapAdapterSettings.constBegin(); !found && itS != m_dapAdapterSettings.constEnd(); ++itS) { 0428 for (auto itP = itS->constBegin(); itP != itS->constEnd(); ++itP) { 0429 if (itP->index == comboIndex) { 0430 cfg.debugger = itS.key(); 0431 cfg.debuggerProfile = itP.key(); 0432 if (full) { 0433 cfg.dapSettings = itP.value(); 0434 } 0435 found = true; 0436 break; 0437 } 0438 } 0439 } 0440 const QStringList &variables = m_clientCombo->currentData().toStringList(); 0441 for (const auto &field : variables) { 0442 // file 0443 if (field == F_FILE) { 0444 cfg.variables[F_FILE] = m_executable->text(); 0445 // working dir 0446 } else if (field == F_WORKDIR) { 0447 cfg.variables[F_WORKDIR] = m_workingDirectory->text(); 0448 // pid 0449 } else if (field == F_PID) { 0450 cfg.variables[F_PID] = m_processId->value(); 0451 // arguments 0452 } else if (field == F_ARGS) { 0453 cfg.variables[F_ARGS] = m_arguments->text(); 0454 // other 0455 } else if (m_dapFields.contains(field)) { 0456 cfg.variables[field] = m_dapFields[field].input->text(); 0457 } 0458 } 0459 return cfg; 0460 } 0461 0462 bool ConfigView::takeFocusAlways() const 0463 { 0464 return m_takeFocus->isChecked(); 0465 } 0466 0467 bool ConfigView::showIOTab() const 0468 { 0469 return m_redirectTerminal->isChecked(); 0470 } 0471 0472 void ConfigView::slotTargetEdited(const QString &newText) 0473 { 0474 QString newComboText(newText); 0475 for (int i = 0; i < m_targetCombo->count(); ++i) { 0476 if (i != m_targetCombo->currentIndex() && m_targetCombo->itemText(i) == newComboText) { 0477 newComboText = newComboText + QStringLiteral(" 2"); 0478 } 0479 } 0480 int cursorPosition = m_targetCombo->lineEdit()->cursorPosition(); 0481 m_targetCombo->setItemText(m_targetCombo->currentIndex(), newComboText); 0482 m_targetCombo->lineEdit()->setCursorPosition(cursorPosition); 0483 0484 // rebuild the target menu 0485 QStringList targets; 0486 for (int i = 0; i < m_targetCombo->count(); ++i) { 0487 targets.append(m_targetCombo->itemText(i)); 0488 } 0489 m_targetSelectAction->setItems(targets); 0490 m_targetSelectAction->setCurrentItem(m_targetCombo->currentIndex()); 0491 } 0492 0493 void ConfigView::slotTargetSelected(int index) 0494 { 0495 if ((index < 0) || (index >= m_targetCombo->count())) { 0496 return; 0497 } 0498 0499 if ((m_currentTarget > 0) && (m_currentTarget < m_targetCombo->count())) { 0500 saveCurrentToIndex(m_currentTarget); 0501 } 0502 0503 const int clientIndex = loadFromIndex(index); 0504 if (clientIndex < 0) 0505 return; 0506 0507 m_currentTarget = index; 0508 0509 if (clientIndex == 0) { 0510 setAdvancedOptions(); 0511 } 0512 0513 // Keep combo box and menu in sync 0514 m_targetCombo->setCurrentIndex(index); 0515 m_targetSelectAction->setCurrentItem(index); 0516 0517 m_clientCombo->setCurrentIndex(clientIndex); 0518 } 0519 0520 void ConfigView::slotAddTarget() 0521 { 0522 QJsonObject targetConf; 0523 0524 targetConf[F_TARGET] = i18n("Target %1", m_targetCombo->count() + 1); 0525 0526 m_targetCombo->addItem(targetConf[F_TARGET].toString(), targetConf); 0527 m_targetCombo->setCurrentIndex(m_targetCombo->count() - 1); 0528 } 0529 0530 void ConfigView::slotCopyTarget() 0531 { 0532 QJsonObject tmp = m_targetCombo->itemData(m_targetCombo->currentIndex()).toJsonObject(); 0533 if (tmp.isEmpty()) { 0534 slotAddTarget(); 0535 return; 0536 } 0537 tmp[F_TARGET] = i18n("Target %1", m_targetCombo->count() + 1); 0538 m_targetCombo->addItem(tmp[F_TARGET].toString(), tmp); 0539 m_targetCombo->setCurrentIndex(m_targetCombo->count() - 1); 0540 } 0541 0542 void ConfigView::slotDeleteTarget() 0543 { 0544 m_targetCombo->blockSignals(true); 0545 int currentIndex = m_targetCombo->currentIndex(); 0546 m_targetCombo->removeItem(currentIndex); 0547 if (m_targetCombo->count() == 0) { 0548 slotAddTarget(); 0549 } 0550 0551 const int clientIndex = loadFromIndex(m_targetCombo->currentIndex()); 0552 m_targetCombo->blockSignals(false); 0553 0554 if (clientIndex >= 0) { 0555 m_clientCombo->setCurrentIndex(clientIndex); 0556 } 0557 } 0558 0559 bool ConfigView::debuggerIsGDB() const 0560 { 0561 return m_clientCombo->currentIndex() == 0; 0562 } 0563 0564 void ConfigView::resizeEvent(QResizeEvent *) 0565 { 0566 const bool toVertical = m_useBottomLayout && size().height() > size().width(); 0567 const bool toHorizontal = !m_useBottomLayout && (size().height() < size().width()); 0568 0569 if (!toVertical && !toHorizontal) 0570 return; 0571 0572 const bool is_dbg = debuggerIsGDB(); 0573 const QStringList debuggerVariables = m_clientCombo->currentData().toStringList(); 0574 0575 // check if preformatted inputs are required 0576 m_advancedSettings->setVisible(is_dbg); 0577 const bool needsExe = is_dbg || debuggerVariables.contains(F_FILE); 0578 const bool needsWdir = is_dbg || debuggerVariables.contains(F_WORKDIR); 0579 const bool needsArgs = is_dbg || debuggerVariables.contains(F_ARGS); 0580 const bool needsPid = !is_dbg && debuggerVariables.contains(F_PID); 0581 0582 if (toVertical) { 0583 // Set layout for the side 0584 delete m_checBoxLayout; 0585 m_checBoxLayout = nullptr; 0586 delete layout(); 0587 QGridLayout *layout = new QGridLayout(this); 0588 layout->setContentsMargins(0, 0, 0, 0); 0589 0590 layout->addWidget(m_clientCombo, 0, 0); 0591 layout->addWidget(m_targetCombo, 1, 0); 0592 layout->addWidget(m_addTarget, 1, 1); 0593 layout->addWidget(m_copyTarget, 1, 2); 0594 layout->addWidget(m_deleteTarget, 1, 3); 0595 m_line->setFrameShape(QFrame::HLine); 0596 layout->addWidget(m_line, 2, 0, 1, 4); 0597 0598 int row = 3; 0599 0600 if (needsExe) { 0601 layout->addWidget(m_execLabel, ++row, 0, Qt::AlignLeft); 0602 layout->addWidget(m_executable, ++row, 0, 1, 3); 0603 layout->addWidget(m_browseExe, row, 3); 0604 } 0605 0606 if (needsWdir) { 0607 layout->addWidget(m_workDirLabel, ++row, 0, Qt::AlignLeft); 0608 layout->addWidget(m_workingDirectory, ++row, 0, 1, 3); 0609 layout->addWidget(m_browseDir, row, 3); 0610 } 0611 0612 if (needsArgs) { 0613 layout->addWidget(m_argumentsLabel, ++row, 0, Qt::AlignLeft); 0614 layout->addWidget(m_arguments, ++row, 0, 1, 4); 0615 } 0616 0617 if (needsPid) { 0618 layout->addWidget(m_processIdLabel, ++row, 0, Qt::AlignLeft); 0619 layout->addWidget(m_processId, ++row, 0, 1, 4); 0620 } 0621 0622 for (const auto &fieldName : debuggerVariables) { 0623 if (fieldName == F_FILE) 0624 continue; 0625 if (fieldName == F_ARGS) 0626 continue; 0627 if (fieldName == F_PID) 0628 continue; 0629 if (fieldName == F_WORKDIR) 0630 continue; 0631 0632 const auto &field = getDapField(fieldName); 0633 0634 layout->addWidget(field.label, ++row, 0, Qt::AlignLeft); 0635 layout->addWidget(field.input, ++row, 0, 1, 4); 0636 } 0637 0638 layout->addWidget(m_takeFocus, ++row, 0, 1, 4); 0639 layout->addWidget(m_redirectTerminal, ++row, 0, 1, 4); 0640 if (is_dbg) { 0641 layout->addWidget(m_advancedSettings, ++row, 0, 1, 4); 0642 } 0643 0644 layout->addItem(new QSpacerItem(1, 1), ++row, 0); 0645 layout->setColumnStretch(0, 1); 0646 layout->setRowStretch(row, 1); 0647 0648 m_useBottomLayout = false; 0649 } else if (toHorizontal) { 0650 // Set layout for the bottom 0651 delete m_checBoxLayout; 0652 delete layout(); 0653 m_checBoxLayout = new QHBoxLayout(); 0654 m_checBoxLayout->addWidget(m_takeFocus, 10); 0655 m_checBoxLayout->addWidget(m_redirectTerminal, 10); 0656 if (is_dbg) { 0657 m_checBoxLayout->addWidget(m_advancedSettings, 0); 0658 } 0659 0660 QGridLayout *layout = new QGridLayout(this); 0661 layout->setContentsMargins(0, 0, 0, 0); 0662 layout->addWidget(m_clientCombo, 0, 0, 1, 6); 0663 0664 layout->addWidget(m_targetCombo, 1, 0, 1, 3); 0665 0666 layout->addWidget(m_addTarget, 2, 0); 0667 layout->addWidget(m_copyTarget, 2, 1); 0668 layout->addWidget(m_deleteTarget, 2, 2); 0669 0670 int row = 0; 0671 0672 if (needsExe) { 0673 layout->addWidget(m_execLabel, ++row, 5, Qt::AlignRight); 0674 layout->addWidget(m_executable, row, 6); 0675 layout->addWidget(m_browseExe, row, 7); 0676 } 0677 0678 if (needsWdir) { 0679 layout->addWidget(m_workDirLabel, ++row, 5, Qt::AlignRight); 0680 layout->addWidget(m_workingDirectory, row, 6); 0681 layout->addWidget(m_browseDir, row, 7); 0682 } 0683 0684 if (needsArgs) { 0685 layout->addWidget(m_argumentsLabel, ++row, 5, Qt::AlignRight); 0686 layout->addWidget(m_arguments, row, 6, 1, 2); 0687 } 0688 0689 if (needsPid) { 0690 layout->addWidget(m_processIdLabel, ++row, 5, Qt::AlignRight); 0691 layout->addWidget(m_processId, row, 6); 0692 } 0693 0694 for (const auto &fieldName : debuggerVariables) { 0695 if (fieldName == F_FILE) 0696 continue; 0697 if (fieldName == F_ARGS) 0698 continue; 0699 if (fieldName == F_PID) 0700 continue; 0701 if (fieldName == F_WORKDIR) 0702 continue; 0703 0704 const auto &field = getDapField(fieldName); 0705 0706 layout->addWidget(field.label, ++row, 5, Qt::AlignRight); 0707 layout->addWidget(field.input, row, 6); 0708 } 0709 0710 layout->addLayout(m_checBoxLayout, ++row, 5, 1, 3); 0711 0712 layout->addItem(new QSpacerItem(1, 1), ++row, 0); 0713 layout->setColumnStretch(6, 100); 0714 layout->setRowStretch(row, 100); 0715 0716 m_line->setFrameShape(QFrame::VLine); 0717 layout->addWidget(m_line, 1, 3, row - 1, 1); 0718 0719 m_useBottomLayout = true; 0720 } 0721 0722 if (toVertical || toHorizontal) { 0723 m_advancedSettings->setVisible(is_dbg); 0724 0725 // exe 0726 m_execLabel->setVisible(needsExe); 0727 m_executable->setVisible(needsExe); 0728 m_browseExe->setVisible(needsExe); 0729 0730 // working dir 0731 m_workDirLabel->setVisible(needsWdir); 0732 m_workingDirectory->setVisible(needsWdir); 0733 m_browseDir->setVisible(needsWdir); 0734 0735 // arguments 0736 m_argumentsLabel->setVisible(needsArgs); 0737 m_arguments->setVisible(needsArgs); 0738 0739 // pid 0740 m_processIdLabel->setVisible(needsPid); 0741 m_processId->setVisible(needsPid); 0742 0743 // additional dap fields 0744 for (auto it = m_dapFields.cbegin(); it != m_dapFields.cend(); ++it) { 0745 const bool visible = debuggerVariables.contains(it.key()); 0746 it->label->setVisible(visible); 0747 it->input->setVisible(visible); 0748 } 0749 } 0750 } 0751 0752 ConfigView::Field &ConfigView::getDapField(const QString &fieldName) 0753 { 0754 if (!m_dapFields.contains(fieldName)) { 0755 m_dapFields[fieldName] = Field{new QLabel(fieldName, this), new QLineEdit(this)}; 0756 } 0757 return m_dapFields[fieldName]; 0758 } 0759 0760 void ConfigView::setAdvancedOptions() 0761 { 0762 const QJsonObject tmp = m_targetCombo->itemData(m_targetCombo->currentIndex()).toJsonObject(); 0763 0764 QJsonObject advanced = tmp[QStringLiteral("advanced")].toObject(); 0765 const auto strGdb = advanced[QStringLiteral("gdb")].toString(); 0766 if (strGdb.isEmpty()) { 0767 advanced[QStringLiteral("gdb")] = QStringLiteral("gdb"); 0768 } 0769 0770 m_advanced->setConfigs(advanced); 0771 } 0772 0773 void ConfigView::slotAdvancedClicked() 0774 { 0775 setAdvancedOptions(); 0776 0777 QJsonObject conf = m_targetCombo->itemData(m_targetCombo->currentIndex()).toJsonObject(); 0778 0779 // Remove old advanced settings 0780 if (m_advanced->exec() == QDialog::Accepted) { 0781 // save the new values 0782 conf[QStringLiteral("advanced")] = m_advanced->configs(); 0783 m_targetCombo->setItemData(m_targetCombo->currentIndex(), conf); 0784 Q_EMIT configChanged(); 0785 } 0786 } 0787 0788 void ConfigView::slotBrowseExec() 0789 { 0790 QString exe = m_executable->text(); 0791 0792 if (m_executable->text().isEmpty()) { 0793 // try current document dir 0794 KTextEditor::View *view = m_mainWindow->activeView(); 0795 0796 if (view != nullptr) { 0797 exe = view->document()->url().toLocalFile(); 0798 } 0799 } 0800 m_executable->setText(QFileDialog::getOpenFileName(nullptr, QString(), exe, QStringLiteral("application/x-executable"))); 0801 } 0802 0803 void ConfigView::slotBrowseDir() 0804 { 0805 QString dir = m_workingDirectory->text(); 0806 0807 if (m_workingDirectory->text().isEmpty()) { 0808 // try current document dir 0809 KTextEditor::View *view = m_mainWindow->activeView(); 0810 0811 if (view != nullptr) { 0812 dir = view->document()->url().toLocalFile(); 0813 } 0814 } 0815 m_workingDirectory->setText(QFileDialog::getExistingDirectory(this, QString(), dir)); 0816 } 0817 0818 void ConfigView::saveCurrentToIndex(int index) 0819 { 0820 if ((index < 0) || (index >= m_targetCombo->count())) { 0821 return; 0822 } 0823 0824 QJsonObject tmp = m_targetCombo->itemData(index).toJsonObject(); 0825 0826 tmp[F_TARGET] = m_targetCombo->itemText(index); 0827 if (debuggerIsGDB()) { 0828 if (tmp.contains(F_DEBUGGER)) 0829 tmp.remove(F_DEBUGGER); 0830 if (tmp.contains(F_PROFILE)) 0831 tmp.remove(F_PROFILE); 0832 tmp[F_FILE] = m_executable->text(); 0833 tmp[F_WORKDIR] = m_workingDirectory->text(); 0834 tmp[F_ARGS] = m_arguments->text(); 0835 } else { 0836 const auto cfg = currentDAPTarget(); 0837 tmp[F_DEBUGGER] = cfg.debugger; 0838 tmp[F_PROFILE] = cfg.debuggerProfile; 0839 tmp[QStringLiteral("variables")] = QJsonObject::fromVariantHash(cfg.variables); 0840 } 0841 0842 m_targetCombo->setItemData(index, tmp); 0843 } 0844 0845 int ConfigView::loadFromIndex(int index) 0846 { 0847 if ((index < 0) || (index >= m_targetCombo->count())) { 0848 return -1; 0849 } 0850 0851 QJsonObject tmp = m_targetCombo->itemData(index).toJsonObject(); 0852 // The custom init strings are set in slotAdvancedClicked(). 0853 0854 const QString &debuggerKey = tmp[F_DEBUGGER].toString(); 0855 if (debuggerKey.isNull() || debuggerKey.isEmpty()) { 0856 // GDB 0857 m_executable->setText(tmp[F_FILE].toString()); 0858 m_workingDirectory->setText(tmp[F_WORKDIR].toString()); 0859 m_arguments->setText(tmp[F_ARGS].toString()); 0860 0861 return 0; 0862 } else { 0863 // DAP 0864 if (!m_dapAdapterSettings.contains(debuggerKey)) 0865 return -1; 0866 const QString &debuggerProfile = tmp[F_PROFILE].toString(); 0867 if (!m_dapAdapterSettings[debuggerKey].contains(debuggerProfile)) 0868 return -1; 0869 auto map = tmp[QStringLiteral("variables")].toObject(); 0870 0871 m_executable->setText(map[F_FILE].toString()); 0872 map.remove(F_FILE); 0873 m_workingDirectory->setText(map[F_WORKDIR].toString()); 0874 map.remove(F_WORKDIR); 0875 m_arguments->setText(map[F_ARGS].toString()); 0876 map.remove(F_ARGS); 0877 m_processId->setValue(map[F_PID].toInt()); 0878 map.remove(F_PID); 0879 0880 for (auto it = map.constBegin(); it != map.constEnd(); ++it) { 0881 const auto &field = getDapField(it.key()); 0882 field.input->setText(it.value().toString()); 0883 } 0884 0885 return m_dapAdapterSettings[debuggerKey][debuggerProfile].index; 0886 } 0887 } 0888 0889 #include "moc_configview.cpp"