// SPDX-FileCopyrightText: 2021 Ernie Pasveer // // SPDX-License-Identifier: GPL-3.0-or-later #include "SeerVariableTrackerBrowserWidget.h" #include "SeerUtl.h" #include #include #include #include #include #include #include #include #include SeerVariableTrackerBrowserWidget::SeerVariableTrackerBrowserWidget (QWidget* parent) : QWidget(parent) { // Construct the UI. setupUi(this); // Setup the widgets variablesTreeWidget->setMouseTracking(true); variablesTreeWidget->setSortingEnabled(false); variablesTreeWidget->setContextMenuPolicy(Qt::CustomContextMenu); variablesTreeWidget->setSelectionMode(QAbstractItemView::ExtendedSelection); variablesTreeWidget->resizeColumnToContents(0); // name variablesTreeWidget->resizeColumnToContents(1); // value variablesTreeWidget->resizeColumnToContents(2); // id variablesTreeWidget->resizeColumnToContents(3); // used variablesTreeWidget->setColumnHidden(2, true); // Hide the 'id' column. variablesTreeWidget->setColumnHidden(3, true); // Hide the 'used' column. variablesTreeWidget->clear(); // Connect things. QObject::connect(variableAddLineEdit, &QLineEdit::returnPressed, this, &SeerVariableTrackerBrowserWidget::handleAddLineEdit); QObject::connect(variableDeleteToolButton, &QToolButton::clicked, this, &SeerVariableTrackerBrowserWidget::handleDeleteToolButton); QObject::connect(variableDeleteAllToolButton, &QToolButton::clicked, this, &SeerVariableTrackerBrowserWidget::handleDeleteAllToolButton); QObject::connect(variablesTreeWidget, &QTreeWidget::itemCollapsed, this, &SeerVariableTrackerBrowserWidget::handleItemCollapsed); QObject::connect(variablesTreeWidget, &QTreeWidget::itemExpanded, this, &SeerVariableTrackerBrowserWidget::handleItemExpanded); QObject::connect(variablesTreeWidget, &QTreeWidget::itemEntered, this, &SeerVariableTrackerBrowserWidget::handleItemEntered); QObject::connect(variablesTreeWidget, &QTreeWidget::customContextMenuRequested, this, &SeerVariableTrackerBrowserWidget::handleContextMenu); QObject::connect(variableRememberToolButton, &QToolButton::toggled, this, &SeerVariableTrackerBrowserWidget::handleRememberToolButton); // Read state. readSettings(); } SeerVariableTrackerBrowserWidget::~SeerVariableTrackerBrowserWidget () { } void SeerVariableTrackerBrowserWidget::handleText (const QString& text) { QApplication::setOverrideCursor(Qt::BusyCursor); while (1) { if (text.startsWith("^done,DataExpressionTable={") && text.endsWith("}")) { // "^done,DataExpressionTable={ // entry={id=\"1\",expression=\"s\"}, // entry={id=\"2\",expression=\"v\"}, // entry={id=\"4\",expression=\"l\"}, // entry={id=\"5\",expression=\"m\"} // }" QString frame_text = Seer::parseFirst(text, "DataExpressionTable=", '{', '}', false); QStringList entries_list = Seer::parse(frame_text, "entry=", '{', '}', false); for (int i=0; i matches = variablesTreeWidget->findItems(id_text, Qt::MatchExactly, 2); if (matches.count() == 0) { QTreeWidgetItem* topItem = new QTreeWidgetItem; topItem->setText(0, expression_text); topItem->setText(1, ""); topItem->setFont(1, QFontDatabase::systemFont(QFontDatabase::FixedFont)); topItem->setText(2, id_text); topItem->setText(3, "new"); variablesTreeWidget->addTopLevelItem(topItem); } } }else if (text.startsWith("^done,DataExpressionAdded={") && text.endsWith("}")) { // "^done,DataExpressionAdded={ // id=\"5\", // expression=\"m\" // }" QString frame_text = Seer::parseFirst(text, "DataExpressionAdded=", '{', '}', false); QString id_text = Seer::parseFirst(frame_text, "id=", '"', '"', false); QString expression_text = Seer::parseFirst(frame_text, "expression=", '"', '"', false); QList matches = variablesTreeWidget->findItems(id_text, Qt::MatchExactly, 2); if (matches.count() == 0) { QTreeWidgetItem* topItem = new QTreeWidgetItem; topItem->setText(0, expression_text); topItem->setText(1, ""); topItem->setFont(1, QFontDatabase::systemFont(QFontDatabase::FixedFont)); topItem->setText(2, id_text); topItem->setText(3, "new"); variablesTreeWidget->addTopLevelItem(topItem); refreshValues(); emit raiseTab(); } }else if (text.startsWith("^done,DataExpressionDeleted={") && text.endsWith("}")) { // "^done,DataExpressionDeleted={ // entry={id=\"1\",expression=\"s\"}, // entry={id=\"3\",expression=\"vb\"} // }" QString frame_text = Seer::parseFirst(text, "DataExpressionDeleted=", '{', '}', false); QStringList entries_list = Seer::parse(frame_text, "entry=", '{', '}', false); for (int i=0; i matches = variablesTreeWidget->findItems(id_text, Qt::MatchExactly, 2); qDeleteAll(matches); } }else if (text.contains(QRegularExpression("^([0-9]+)\\^done,value="))) { // "6^done,value=\"\\\"abc\\\"\"" QString id_text = text.section('^', 0,0); QString value_text = Seer::parseFirst(text, "value=", '"', '"', false); // If with brackets (a structure), filter out excess '\n' // in case pretty-print-on is used. if (value_text.front() == '{' && value_text.back() == '}') { value_text = Seer::filterBareNewLines(value_text); } // Find the ones that match our 'id'. QList matches = variablesTreeWidget->findItems(id_text, Qt::MatchExactly, 2); if (matches.count() == 1) { Q_ASSERT(matches.count() == 1); // There should be only one. QTreeWidgetItem* item = matches[0]; // Mark each entry initially as "unused". // Later, some will be marked as "reused" or "new". Then the "unused" ones will // be deleted. QTreeWidgetItemIterator itmark(item); while (*itmark) { (*itmark)->setText(3, "unused"); ++itmark; } QString old_text = item->text(1); old_text.replace("\\\"", "\""); old_text.remove('\\'); value_text.replace("\\\"", "\""); value_text.remove('\\'); if (old_text == "") { // If old_text is empty -> This node is created for the first time. Pass the same text for old and new handleItemCreate (item, value_text, value_text); } else { old_text = Seer::filterBareNewLines(old_text); handleItemCreate (item, value_text, old_text); } emit raiseTab(); // At this point, there are some new entries, some reused entries, and some unused ones. // For now, don't bother deleting 'unused' ones. } }else if (text.contains(QRegularExpression("^([0-9]+)\\^error,msg="))) { // "1^error,msg=\"No symbol \\\"j\\\" in current context.\"" QString id_text = text.section('^', 0,0); QString msg_text = Seer::parseFirst(text, "msg=", '"', '"', false); // Find the ones that match our 'id'. QList matches = variablesTreeWidget->findItems(id_text, Qt::MatchExactly, 2); if (matches.count() == 1) { // There should be only one. QTreeWidgetItem* item = matches[0]; // Remove any children. QList children = item->takeChildren(); qDeleteAll(children); // Set the text with the error message. item->setText(1, Seer::filterEscapes(msg_text)); item->setText(3, "used"); } }else{ // Ignore others. } variablesTreeWidget->resizeColumnToContents(0); variablesTreeWidget->resizeColumnToContents(1); variablesTreeWidget->resizeColumnToContents(2); variablesTreeWidget->resizeColumnToContents(3); break; } QApplication::restoreOverrideCursor(); } void SeerVariableTrackerBrowserWidget::handleStoppingPointReached () { // Don't do any work if the widget is hidden. if (isHidden()) { return; } refresh(); } void SeerVariableTrackerBrowserWidget::handleSessionTerminated () { // Remember the variable names? if (variableRememberToolButton->isChecked() == true) { return; } // Delete previous contents. variablesTreeWidget->clear(); // Tell the GdbWidget to forget all tracked expressions. emit deleteVariableExpressions("*"); } void SeerVariableTrackerBrowserWidget::refresh () { emit refreshVariableTrackerNames(); emit refreshVariableTrackerValues(); } void SeerVariableTrackerBrowserWidget::refreshValues () { emit refreshVariableTrackerValues(); } void SeerVariableTrackerBrowserWidget::handleAddLineEdit () { QString variable = variableAddLineEdit->text(); variableAddLineEdit->clear(); if (variable != "") { emit addVariableExpression(variable); // After sending the 'add' signal, schedule a 'refresh' 200ms later. QTimer::singleShot(200, this, &SeerVariableTrackerBrowserWidget::refresh); } } void SeerVariableTrackerBrowserWidget::handleDeleteToolButton () { // Get selected tree items. QList items = variablesTreeWidget->selectedItems(); // Build a string that is a list of variable ids. QString variableids; QList::iterator i; for (i = items.begin(); i != items.end(); ++i) { if (i != items.begin()) { variableids += " "; } variableids += (*i)->text(2); } // Don't do anything if the list of variables is empty. if (variableids == "") { return; } // Send the signal. emit deleteVariableExpressions(variableids); } void SeerVariableTrackerBrowserWidget::handleDeleteAllToolButton () { emit deleteVariableExpressions("*"); } void SeerVariableTrackerBrowserWidget::handleItemExpanded (QTreeWidgetItem* item) { Q_UNUSED(item); variablesTreeWidget->resizeColumnToContents(0); variablesTreeWidget->resizeColumnToContents(1); variablesTreeWidget->resizeColumnToContents(2); variablesTreeWidget->resizeColumnToContents(3); } void SeerVariableTrackerBrowserWidget::handleItemCollapsed (QTreeWidgetItem* item) { Q_UNUSED(item); variablesTreeWidget->resizeColumnToContents(0); variablesTreeWidget->resizeColumnToContents(1); variablesTreeWidget->resizeColumnToContents(2); variablesTreeWidget->resizeColumnToContents(3); } void SeerVariableTrackerBrowserWidget::handleItemEntered (QTreeWidgetItem* item, int column) { Q_UNUSED(column); item->setToolTip(0, item->text(0) + " : " + Seer::elideText(item->text(1), Qt::ElideRight, 100)); for (int i=1; icolumnCount(); i++) { // Copy tooltip to other columns. item->setToolTip(i, item->toolTip(0)); } } void SeerVariableTrackerBrowserWidget::showEvent (QShowEvent* event) { QWidget::showEvent(event); refresh(); } void SeerVariableTrackerBrowserWidget::handleItemCreate (QTreeWidgetItem* item, const QString& value_text, const QString& old_text) { handleItemCreate(item, item->text(2), item->text(0), value_text, old_text); } void SeerVariableTrackerBrowserWidget::handleItemCreate (QTreeWidgetItem* parentItem, const QString& id_text, const QString& name_text, const QString& value_text, const QString& old_text) { // Fill in parent item. Whether is a simple or complex entry. parentItem->setText(0, name_text); parentItem->setText(1, Seer::filterEscapes(value_text)); parentItem->setText(2, id_text); parentItem->setText(3, "reused"); // Look for bookmarks. This indicates a nested structure. // There are two types. With and without an address. QString capture0; // With bookends. QString capture1; // Without. QRegularExpression withaddress_re("^@0[xX][0-9a-fA-F]+: \\{(.*?)\\}$"); QRegularExpressionMatch withaddress_match = withaddress_re.match(value_text, 0, QRegularExpression::PartialPreferCompleteMatch); if (withaddress_match.hasMatch()) { capture0 = withaddress_match.captured(0); capture1 = withaddress_match.captured(1); }else{ QRegularExpression noaddress_re("^\\{(.*?)\\}$"); QRegularExpressionMatch noaddress_match = noaddress_re.match(value_text, 0, QRegularExpression::PartialPreferCompleteMatch); if (noaddress_match.hasMatch()) { capture0 = noaddress_match.captured(0); capture1 = noaddress_match.captured(1); } } // Simple entries don't have children. Delete them. // Then we're done. if (capture0 == "" || capture1 == "") { while (parentItem->childCount() > 0) { QTreeWidgetItem* item = parentItem->child(0); delete item; } } // Do the same for old_text QString captureOld0; // With bookends. QString captureOld1; // Without. QRegularExpressionMatch withaddress_match_Old = withaddress_re.match(old_text, 0, QRegularExpression::PartialPreferCompleteMatch); if (withaddress_match_Old.hasMatch()) { captureOld0 = withaddress_match_Old.captured(0); captureOld1 = withaddress_match_Old.captured(1); }else{ QRegularExpression noaddress_re("^\\{(.*?)\\}$"); QRegularExpressionMatch noaddress_match = noaddress_re.match(old_text, 0, QRegularExpression::PartialPreferCompleteMatch); if (noaddress_match.hasMatch()) { captureOld0 = noaddress_match.captured(0); captureOld1 = noaddress_match.captured(1); } } // Add the complex entry to the tree. Reuse, if possible. // Instead of creating a new tree each time, we will reuse existing items, if they are there. // This allows the expanded items to remain expanded. We start by looking for matches that // may already be there. If there are matches, the code will reuse it. If not, a new item // is created by the code. Note, when searching, we only look at the current level. Not any // children. // Use the one without bookends. QString text = capture1; // Convert to a list of name/value pairs. QStringList nv_pairs; if (text.startsWith("{")) { // String might describe an array: {a=1, b=1},{a=1, b=1},{a=1, b=1},{a=1, b=1} nv_pairs = Seer::parseArray(parentItem->text(0), text); } else { nv_pairs = Seer::parseCommaList(text, '{', '}'); } QStringList nv_old_pairs; if (text.startsWith("{")) { // String might describe an array: {a=1, b=1},{a=1, b=1},{a=1, b=1},{a=1, b=1} nv_old_pairs = Seer::parseArray(parentItem->text(0), captureOld1); } else { nv_old_pairs = Seer::parseCommaList(captureOld1, '{', '}'); } if (nv_old_pairs.size() != nv_pairs.size()) // Check if variable has changed type or is out of scope { // If sizes don't match, it seems that variables is out of scope or changed type // First, delete all children, otherwise we might have leftovers while (parentItem->childCount() > 0) { QTreeWidgetItem* item = parentItem->child(0); delete item; } // Then, recreate all children for (const auto& nv : nv_pairs) { QStringPair pair = Seer::parseNameValue(nv, '='); // Look for the existing child, if any so we can reuse it. QTreeWidgetItem* childItem = 0; for (int i=0; ichildCount(); i++) { if (parentItem->child(i)->text(0) == pair.first) { childItem = parentItem->child(i); childItem->setText(0, pair.first); childItem->setText(1, pair.second); childItem->setFont(1, QFontDatabase::systemFont(QFontDatabase::FixedFont)); childItem->setText(2, id_text); childItem->setText(3, "reused"); break; } } // Otherwise, create a new child. if (childItem == 0) { childItem = new QTreeWidgetItem; childItem->setText(0, pair.first); childItem->setText(1, pair.second); childItem->setFont(1, QFontDatabase::systemFont(QFontDatabase::FixedFont)); childItem->setText(2, id_text); childItem->setText(3, "new"); parentItem->addChild(childItem); } handleItemCreate(childItem, id_text, childItem->text(0), childItem->text(1), ""); } return; } QFont parentFont = parentItem->font(0); parentFont.setBold(old_text != value_text); // if value has changed -> set font to bold parentItem->setFont(0, parentFont); parentItem->setFont(1, parentFont); // Go through each pair and add the name and its value to the tree. for (int i = 0; i < nv_pairs.size(); ++i) { QString nv = nv_pairs[i]; QString nv_old = nv_old_pairs[i]; QStringPair pair = Seer::parseNameValue(nv, '='); QStringPair old_pair = Seer::parseNameValue(nv_old, '='); // Look for the existing child, if any so we can reuse it. QTreeWidgetItem* childItem = 0; for (int i=0; ichildCount(); i++) { if (parentItem->child(i)->text(0) == pair.first) { childItem = parentItem->child(i); childItem->setText(0, pair.first); childItem->setText(1, pair.second); childItem->setFont(1, QFontDatabase::systemFont(QFontDatabase::FixedFont)); childItem->setText(2, id_text); childItem->setText(3, "reused"); QFont childFontCol0 = childItem->font(0); QFont childFontCol1 = QFontDatabase::systemFont(QFontDatabase::FixedFont); childFontCol0.setBold(pair.second != old_pair.second); childFontCol1.setBold(pair.second != old_pair.second); childItem->setFont(0, childFontCol0); childItem->setFont(1, childFontCol1); break; } } // Otherwise, create a new child. if (childItem == 0) { childItem = new QTreeWidgetItem; childItem->setText(0, pair.first); childItem->setText(1, pair.second); childItem->setFont(1, QFontDatabase::systemFont(QFontDatabase::FixedFont)); childItem->setText(2, id_text); childItem->setText(3, "new"); parentItem->addChild(childItem); } handleItemCreate(childItem, id_text, childItem->text(0), childItem->text(1), old_pair.second); } } void SeerVariableTrackerBrowserWidget::handleContextMenu (const QPoint& pos) { // Get the item at the cursor. QTreeWidgetItem* item = variablesTreeWidget->itemAt(pos); // Construct the menu. QAction* addMemoryVisualizerAction = new QAction(); QAction* addMemoryAsteriskVisualizerAction = new QAction(); QAction* addMemoryAmpersandVisualizerAction = new QAction(); QAction* addArrayVisualizerAction = new QAction(); QAction* addArrayAsteriskVisualizerAction = new QAction(); QAction* addArrayAmpersandVisualizerAction = new QAction(); QAction* addMatrixVisualizerAction = new QAction(); QAction* addMatrixAsteriskVisualizerAction = new QAction(); QAction* addMatrixAmpersandVisualizerAction = new QAction(); QAction* addStructVisualizerAction = new QAction(); QAction* addStructAsteriskVisualizerAction = new QAction(); QAction* addStructAmpersandVisualizerAction = new QAction(); QMenu menu("Options", this); QMenu memoryVisualizerMenu("Add variable to a Memory Visualizer"); memoryVisualizerMenu.addAction(addMemoryVisualizerAction); memoryVisualizerMenu.addAction(addMemoryAsteriskVisualizerAction); memoryVisualizerMenu.addAction(addMemoryAmpersandVisualizerAction); menu.addMenu(&memoryVisualizerMenu); QMenu arrayVisualizerMenu("Add variable to an Array Visualizer"); arrayVisualizerMenu.addAction(addArrayVisualizerAction); arrayVisualizerMenu.addAction(addArrayAsteriskVisualizerAction); arrayVisualizerMenu.addAction(addArrayAmpersandVisualizerAction); menu.addMenu(&arrayVisualizerMenu); QMenu matrixVisualizerMenu("Add variable to a Matrix Visualizer"); matrixVisualizerMenu.addAction(addMatrixVisualizerAction); matrixVisualizerMenu.addAction(addMatrixAsteriskVisualizerAction); matrixVisualizerMenu.addAction(addMatrixAmpersandVisualizerAction); menu.addMenu(&matrixVisualizerMenu); QMenu structVisualizerMenu("Add variable to a Struct Visualizer"); structVisualizerMenu.addAction(addStructVisualizerAction); structVisualizerMenu.addAction(addStructAsteriskVisualizerAction); structVisualizerMenu.addAction(addStructAmpersandVisualizerAction); menu.addMenu(&structVisualizerMenu); QAction* deleteAction = menu.addAction("Delete selected"); QAction* deleteAllAction = menu.addAction("Delete all"); QAction* copyAction = menu.addAction("Copy selected"); QAction* copyValueOnlyAction = menu.addAction("Copy selected value"); QAction* copyAllAction = menu.addAction("Copy all"); QString actionText; if (item != 0) { actionText = item->text(0); } QString variable; if (item != 0) { // Build up a variable string, incase it is a nested struct. QTreeWidgetItem* i = item; variable = item->text(0); while (i->parent() != 0) { variable = i->parent()->text(0) + "." + variable; i = i->parent(); } } addMemoryVisualizerAction->setText(QString("\"%1\"").arg(actionText)); addMemoryAsteriskVisualizerAction->setText(QString("\"*%1\"").arg(actionText)); addMemoryAmpersandVisualizerAction->setText(QString("\"&&%1\"").arg(actionText)); addArrayVisualizerAction->setText(QString("\"%1\"").arg(actionText)); addArrayAsteriskVisualizerAction->setText(QString("\"*%1\"").arg(actionText)); addArrayAmpersandVisualizerAction->setText(QString("\"&&%1\"").arg(actionText)); addMatrixVisualizerAction->setText(QString("\"%1\"").arg(actionText)); addMatrixAsteriskVisualizerAction->setText(QString("\"*%1\"").arg(actionText)); addMatrixAmpersandVisualizerAction->setText(QString("\"&&%1\"").arg(actionText)); addStructVisualizerAction->setText(QString("\"%1\"").arg(actionText)); addStructAsteriskVisualizerAction->setText(QString("\"*%1\"").arg(actionText)); addStructAmpersandVisualizerAction->setText(QString("\"&&%1\"").arg(actionText)); // If no selected item, disable everything but allow 'copyall' and 'deleteall'. if (item == 0) { memoryVisualizerMenu.setEnabled(false); arrayVisualizerMenu.setEnabled(false); matrixVisualizerMenu.setEnabled(false); structVisualizerMenu.setEnabled(false); deleteAction->setEnabled(false); copyAction->setEnabled(false); } // Execute the menu. Return if nothing. QAction* action = menu.exec(variablesTreeWidget->mapToGlobal(pos)); if (action == 0) { return; } if (action == deleteAction) { handleDeleteToolButton(); } if (action == deleteAllAction) { handleDeleteAllToolButton(); } if (action == copyAction || action == copyAllAction || action == copyValueOnlyAction) { // Get selected tree items. QList items; // Get list of 'select' items. if (action == copyAction || copyValueOnlyAction) { items = variablesTreeWidget->selectedItems(); } // Get list of 'all' items. if (action == copyAllAction) { items = variablesTreeWidget->findItems(QString("*"), Qt::MatchWrap | Qt::MatchWildcard); } // Populate the clipboard. if (items.count() == 0) { return; } QClipboard* clipboard = QGuiApplication::clipboard(); QString text; for (int i=0; itext(0) + ":" + items[i]->text(1); } else { text += items[i]->text(1); } } clipboard->setText(text, QClipboard::Clipboard); clipboard->setText(text, QClipboard::Selection); } // Handle adding memory to visualize. if (action == addMemoryVisualizerAction) { // Emit the signals. if (variable != "") { emit addMemoryVisualizer(variable); } return; } // Handle adding memory to visualize. if (action == addMemoryAsteriskVisualizerAction) { // Emit the signals. if (variable != "") { emit addMemoryVisualizer(QString("*") + variable); } return; } // Handle adding memory to visualize. if (action == addMemoryAmpersandVisualizerAction) { // Emit the signals. if (variable != "") { emit addMemoryVisualizer(QString("&") + variable); } return; } // Handle adding array to visualize. if (action == addArrayVisualizerAction) { // Emit the signals. if (variable != "") { emit addArrayVisualizer(variable); } return; } // Handle adding array to visualize. if (action == addArrayAsteriskVisualizerAction) { // Emit the signals. if (variable != "") { emit addArrayVisualizer(QString("*") + variable); } return; } // Handle adding array to visualize. if (action == addArrayAmpersandVisualizerAction) { // Emit the signals. if (variable != "") { emit addArrayVisualizer(QString("&") + variable); } return; } // Handle adding matrix to visualize. if (action == addMatrixVisualizerAction) { // Emit the signals. if (variable != "") { emit addMatrixVisualizer(variable); } return; } // Handle adding matrix to visualize. if (action == addMatrixAsteriskVisualizerAction) { // Emit the signals. if (variable != "") { emit addMatrixVisualizer(QString("*") + variable); } return; } // Handle adding matrix to visualize. if (action == addMatrixAmpersandVisualizerAction) { // Emit the signals. if (variable != "") { emit addMatrixVisualizer(QString("&") + variable); } return; } // Handle adding struct to visualize. if (action == addStructVisualizerAction) { // Emit the signals. if (variable != "") { emit addStructVisualizer(variable); } return; } // Handle adding struct to visualize. if (action == addStructAsteriskVisualizerAction) { // Emit the signals. if (variable != "") { emit addStructVisualizer(QString("*") + variable); } return; } // Handle adding struct to visualize. if (action == addStructAmpersandVisualizerAction) { // Emit the signals. if (variable != "") { emit addStructVisualizer(QString("&") + variable); } return; } } void SeerVariableTrackerBrowserWidget::handleRememberToolButton (bool checked) { Q_UNUSED(checked); writeSettings(); } void SeerVariableTrackerBrowserWidget::readSettings () { QSettings settings; bool remember = false; settings.beginGroup("variabletrackerwindow"); { remember = settings.value("remembervariablenames", remember).toBool(); } settings.endGroup(); variableRememberToolButton->setChecked(remember); } void SeerVariableTrackerBrowserWidget::writeSettings () { QSettings settings; settings.beginGroup("variabletrackerwindow"); { settings.setValue("remembervariablenames", variableRememberToolButton->isChecked()); } settings.endGroup(); }