diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 1bee6c7..a3a3c08 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -36,6 +36,7 @@ set(HEADER_FILES SeerHexWidget.h SeerLogWidget.h SeerMainWindow.h + SeerArrayVisualizerWidget.h SeerMemoryVisualizerWidget.h SeerRegisterValuesBrowserWidget.h SeerRunStatusIndicator.h @@ -84,6 +85,7 @@ set(SOURCE_FILES SeerHexWidget.cpp SeerLogWidget.cpp SeerMainWindow.cpp + SeerArrayVisualizerWidget.cpp SeerMemoryVisualizerWidget.cpp SeerRegisterValuesBrowserWidget.cpp SeerRunStatusIndicator.cpp diff --git a/src/SeerArrayVisualizerWidget.cpp b/src/SeerArrayVisualizerWidget.cpp new file mode 100644 index 0000000..6a6e523 --- /dev/null +++ b/src/SeerArrayVisualizerWidget.cpp @@ -0,0 +1,314 @@ +#include "SeerArrayVisualizerWidget.h" +#include "SeerUtl.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +SeerArrayVisualizerWidget::SeerArrayVisualizerWidget (QWidget* parent) : QWidget(parent) { + + // Init variables. + _variableId = Seer::createID(); // Create two id's for queries. + _memoryId = Seer::createID(); + + // Set up UI. + setupUi(this); + + // Setup the widgets + setWindowIcon(QIcon(":/seer/resources/seer_64x64.png")); + setWindowTitle("Seer Array Visualizer"); + + arrayLengthLineEdit->setValidator(new QIntValidator(1, 9999999, this)); + columnCountSpinBox->setValue(memoryHexEditor->bytesPerLine()); + + arrayDisplayFormatComboBox->setCurrentIndex(0); + + // Connect things. + QObject::connect(refreshToolButton, &QToolButton::clicked, this, &SeerArrayVisualizerWidget::handleRefreshButton); + QObject::connect(arrayLengthLineEdit, &QLineEdit::returnPressed, this, &SeerArrayVisualizerWidget::handleRefreshButton); + QObject::connect(variableNameLineEdit, &QLineEdit::returnPressed, this, &SeerArrayVisualizerWidget::handleVariableNameLineEdit); + QObject::connect(arrayDisplayFormatComboBox, QOverload::of(&QComboBox::currentIndexChanged), this, &SeerArrayVisualizerWidget::handleArrayDisplayFormatComboBox); + QObject::connect(columnCountSpinBox, QOverload::of(&QSpinBox::valueChanged), this, &SeerArrayVisualizerWidget::handleColumnCountSpinBox); + QObject::connect(printToolButton, &QToolButton::clicked, this, &SeerArrayVisualizerWidget::handlePrintButton); + QObject::connect(saveToolButton, &QToolButton::clicked, this, &SeerArrayVisualizerWidget::handleSaveButton); + + // Restore window settings. + readSettings(); +} + +SeerArrayVisualizerWidget::~SeerArrayVisualizerWidget () { +} + +void SeerArrayVisualizerWidget::setVariableName (const QString& name) { + + setWindowTitle("Seer Array Visualizer - '" + name + "'"); + + variableNameLineEdit->setText(name); + setVariableAddress(""); + + if (variableNameLineEdit->text() == "") { + return; + } + + // Clear old contents. + QByteArray array; + + memoryHexEditor->setData(new SeerHexWidget::DataStorageArray(array)); + + // Send signal to get variable address. + emit evaluateVariableExpression(_variableId, variableNameLineEdit->text()); +} + +QString SeerArrayVisualizerWidget::variableName () const { + return variableNameLineEdit->text(); +} + +void SeerArrayVisualizerWidget::setVariableAddress (const QString& address) { + + unsigned long offset = 0; + bool ok = false; + + if (address.startsWith("0x")) { + + offset = address.toULong(&ok, 16); + + if (ok == true) { + variableAddressLineEdit->setText(address); + }else{ + variableAddressLineEdit->setText("not an address"); + offset = 0; + } + + }else{ + variableAddressLineEdit->setText("not an address"); + offset = 0; + } + + //qDebug() << __PRETTY_FUNCTION__ << ":" << address << offset << ok; + + memoryHexEditor->setAddressOffset(offset); +} + +QString SeerArrayVisualizerWidget::variableAddress () const { + return variableAddressLineEdit->text(); +} + +void SeerArrayVisualizerWidget::handleText (const QString& text) { + + //qDebug() << __PRETTY_FUNCTION__ << ":" << text; + + if (text.contains(QRegExp("^([0-9]+)\\^done,value="))) { + + // 10^done,value="1" + // 11^done,value="0x7fffffffd538" + + QString id_text = text.section('^', 0,0); + + if (id_text.toInt() == _variableId) { + + QStringList words = Seer::filterEscapes(Seer::parseFirst(text, "value=", '"', '"', false)).split(' ', QString::SkipEmptyParts); + + setVariableAddress(words.first()); + } + + }else if (text.contains(QRegExp("^([0-9]+)\\^done,memory="))) { + + // 3^done,memory=[{begin="0x0000000000613e70",offset="0x0000000000000000",end="0x0000000000613e71",contents="00"}] + // 4^done,memory=[{begin="0x0000000000613e70",offset="0x0000000000000000",end="0x0000000000613ed4",contents="000000000000000000000000"}] + + QString id_text = text.section('^', 0,0); + + if (id_text.toInt() == _memoryId) { + + //qDebug() << __PRETTY_FUNCTION__ << ":" << text; + + QString memory_text = Seer::parseFirst(text, "memory=", '[', ']', false); + + QStringList range_list = Seer::parse(memory_text, "", '{', '}', false); + + // Loop through the memory ranges. + for ( const auto& range_text : range_list ) { + + QString contents_text = Seer::parseFirst(range_text, "contents=", '"', '"', false); + + //qDebug() << __PRETTY_FUNCTION__ << ":" << contents_text; + + // Convert hex string to byte array. + QByteArray array; + + for (int i = 0; isetData(new SeerHexWidget::DataStorageArray(array)); + + break; // Take just the first range for now. + } + } + + }else if (text.contains(QRegExp("^([0-9]+)\\^error,msg="))) { + + // 12^error,msg="No symbol \"return\" in current context." + // 13^error,msg="No symbol \"cout\" in current context." + // 3^error,msg="Unable to read memory." + + QString id_text = text.section('^', 0,0); + + if (id_text.toInt() == _variableId) { + variableAddressLineEdit->setText( Seer::filterEscapes(Seer::parseFirst(text, "msg=", '"', '"', false)) ); + } + + if (id_text.toInt() == _memoryId) { + // Display the error message. + QString msg_text = Seer::parseFirst(text, "msg=", false); + + if (msg_text != "") { + QMessageBox::warning(this, "Error.", Seer::filterEscapes(msg_text)); + } + } + + }else{ + // Ignore anything else. + } +} + +void SeerArrayVisualizerWidget::handleRefreshButton () { + + if (variableNameLineEdit->text() == "") { + return; + } + + if (variableAddressLineEdit->text() == "") { + return; + } + + if (variableAddressLineEdit->text() == "not an address") { + return; + } + + emit evaluateMemoryExpression(_memoryId, variableAddressLineEdit->text(), arrayLengthLineEdit->text().toInt()); +} + +void SeerArrayVisualizerWidget::handleVariableNameLineEdit () { + + setVariableName (variableNameLineEdit->text()); +} + +void SeerArrayVisualizerWidget::handleArrayDisplayFormatComboBox (int index) { + + memoryHexEditor->setMemoryMode(SeerHexWidget::HexMemoryMode); +} + +void SeerArrayVisualizerWidget::handleColumnCountSpinBox (int value) { + + memoryHexEditor->setBytesPerLine(value); +} + +void SeerArrayVisualizerWidget::handlePrintButton () { + + QPrinter printer; + + QPrintDialog* dlg = new QPrintDialog(&printer, this); + + if (dlg->exec() != QDialog::Accepted) { + return; + } + + + // Make a copy so we can temporarily add a header. + QTextDocument* clone = memoryHexEditor->document()->clone(this); + + QTextCursor cursor(clone); + QTextCharFormat format = cursor.charFormat(); + format.setBackground(QBrush(Qt::transparent)); + + cursor.insertText("\n", format); + cursor.insertText("name=" + variableName() + " address=" + variableAddress() + " bytesPerLine=" + QString::number(memoryHexEditor->bytesPerLine()) + " bytes=" + QString::number(memoryHexEditor->size()) + " memory=" + memoryHexEditor->memoryModeString() + " char=" + memoryHexEditor->charModeString() + "\n", format); + cursor.insertText("\n", format); + + clone->print(&printer); + + delete clone; +} + +void SeerArrayVisualizerWidget::handleSaveButton () { + + QFileDialog dialog(this, "Seer visualizer file", "", "Logs (*.log);;Text files (*.txt);;All files (*.*)"); + dialog.setAcceptMode(QFileDialog::AcceptSave); + dialog.setFileMode(QFileDialog::AnyFile); + dialog.setDefaultSuffix("log"); + dialog.selectFile("array.log"); + + if (dialog.exec() != QDialog::Accepted) { + return; + } + + QStringList files = dialog.selectedFiles(); + + if (files.size() == 0) { + return; + } + + if (files.size() > 1) { + QMessageBox::critical(this, tr("Error"), tr("Select only 1 file.")); + return; + } + + QFile file(files[0]); + + if (file.open(QIODevice::ReadWrite)) { + QTextStream stream(&file); + + stream << "\n"; + stream << "name=" << variableName() << " address=" << variableAddress() << " bytesPerLine=" << memoryHexEditor->bytesPerLine() << " bytes=" << memoryHexEditor->size() << " memory=" << memoryHexEditor->memoryModeString() << " char=" << memoryHexEditor->charModeString() << "\n"; + stream << "\n"; + stream << memoryHexEditor->toPlainText(); + stream << "\n"; + + file.flush(); + file.close(); + + }else{ + QMessageBox::critical(this, tr("Error"), tr("Cannot save display to file.")); + return; + } +} + +void SeerArrayVisualizerWidget::writeSettings() { + + QSettings settings; + + settings.beginGroup("arrayvisualizerwindow"); + settings.setValue("size", size()); + settings.endGroup(); + + //qDebug() << __PRETTY_FUNCTION__ << ":" << size(); +} + +void SeerArrayVisualizerWidget::readSettings() { + + QSettings settings; + + settings.beginGroup("arrayvisualizerwindow"); + resize(settings.value("size", QSize(800, 400)).toSize()); + settings.endGroup(); + + //qDebug() << __PRETTY_FUNCTION__ << ":" << size(); +} + +void SeerArrayVisualizerWidget::resizeEvent (QResizeEvent* event) { + + writeSettings(); + + QWidget::resizeEvent(event); +} + diff --git a/src/SeerArrayVisualizerWidget.h b/src/SeerArrayVisualizerWidget.h new file mode 100644 index 0000000..f5362de --- /dev/null +++ b/src/SeerArrayVisualizerWidget.h @@ -0,0 +1,43 @@ +#pragma once + +#include +#include "ui_SeerArrayVisualizerWidget.h" + +class SeerArrayVisualizerWidget : public QWidget, protected Ui::SeerArrayVisualizerWidgetForm { + + Q_OBJECT + + public: + explicit SeerArrayVisualizerWidget (QWidget* parent = 0); + ~SeerArrayVisualizerWidget (); + + void setVariableName (const QString& name); + QString variableName () const; + void setVariableAddress (const QString& address); + QString variableAddress () const; + + signals: + void evaluateVariableExpression (int expressionid, QString expression); + void evaluateMemoryExpression (int expressionid, QString address, int count); + + public slots: + void handleText (const QString& text); + + protected slots: + void handleRefreshButton (); + void handleVariableNameLineEdit (); + void handleArrayDisplayFormatComboBox (int index); + void handleColumnCountSpinBox (int value); + void handlePrintButton (); + void handleSaveButton (); + + protected: + void writeSettings (); + void readSettings (); + void resizeEvent (QResizeEvent* event); + + private: + int _variableId; + int _memoryId; +}; + diff --git a/src/SeerArrayVisualizerWidget.ui b/src/SeerArrayVisualizerWidget.ui new file mode 100644 index 0000000..195f367 --- /dev/null +++ b/src/SeerArrayVisualizerWidget.ui @@ -0,0 +1,172 @@ + + + SeerArrayVisualizerWidgetForm + + + + 0 + 0 + 640 + 657 + + + + Form + + + + + + + + Variable name or expression. + + + variable name + + + false + + + + + + + Variable address. + + + true + + + variable address + + + + + + + Number of bytes to display. + + + + + + # Elements + + + false + + + + + + + Array display format. + + + + int16 + + + + + int32 + + + + + int64 + + + + + float32 + + + + + float64 + + + + + + + + Number of columns in display. + + + 1 + + + 10 + + + + + + + Print the display. + + + ... + + + + .. + + + + + + + Save the display to a file. + + + ... + + + + .. + + + + + + + Refresh the display. + + + ... + + + + .. + + + + + + + + + + 0 + 100 + + + + + + + + + SeerHexWidget + QWidget +
SeerHexWidget.h
+ 1 +
+
+ + +
diff --git a/src/SeerGdbWidget.cpp b/src/SeerGdbWidget.cpp index b20ec78..62d7102 100644 --- a/src/SeerGdbWidget.cpp +++ b/src/SeerGdbWidget.cpp @@ -1,6 +1,7 @@ #include "SeerGdbWidget.h" #include "SeerLogWidget.h" #include "SeerMemoryVisualizerWidget.h" +#include "SeerArrayVisualizerWidget.h" #include "SeerUtl.h" #include #include @@ -1087,7 +1088,29 @@ void SeerGdbWidget::handleGdbMemoryVisualizer () { handleGdbMemoryAddExpression(""); } +void SeerGdbWidget::handleGdbArrayAddExpression (QString expression) { + + Q_UNUSED(expression); + + if (executableLaunchMode() == "") { + return; + } + + SeerArrayVisualizerWidget* w = new SeerArrayVisualizerWidget(0); + w->show(); + + // Connect things. + QObject::connect(_gdbMonitor, &GdbMonitor::astrixTextOutput, w, &SeerArrayVisualizerWidget::handleText); + QObject::connect(_gdbMonitor, &GdbMonitor::caretTextOutput, w, &SeerArrayVisualizerWidget::handleText); + QObject::connect(w, &SeerArrayVisualizerWidget::evaluateVariableExpression, this, &SeerGdbWidget::handleGdbDataEvaluateExpression); + QObject::connect(w, &SeerArrayVisualizerWidget::evaluateMemoryExpression, this, &SeerGdbWidget::handleGdbMemoryEvaluateExpression); + + // Tell the visualizer what variable to use. + w->setVariableName(expression); +} + void SeerGdbWidget::handleGdbArrayVisualizer () { + handleGdbArrayAddExpression(""); } void SeerGdbWidget::handleSplitterMoved (int pos, int index) { diff --git a/src/SeerGdbWidget.h b/src/SeerGdbWidget.h index ef41901..7b17b33 100644 --- a/src/SeerGdbWidget.h +++ b/src/SeerGdbWidget.h @@ -102,6 +102,7 @@ class SeerGdbWidget : public QWidget, protected Ui::SeerGdbWidgetForm { void handleGdbMemoryAddExpression (QString expression); void handleGdbMemoryEvaluateExpression (int expressionid, QString address, int count); void handleGdbMemoryVisualizer (); + void handleGdbArrayAddExpression (QString expression); void handleGdbArrayVisualizer (); void handleSplitterMoved (int pos, int index); diff --git a/src/SeerMemoryVisualizerWidget.ui b/src/SeerMemoryVisualizerWidget.ui index a65c68f..13e2a3b 100644 --- a/src/SeerMemoryVisualizerWidget.ui +++ b/src/SeerMemoryVisualizerWidget.ui @@ -24,6 +24,9 @@ variable name + + false + @@ -50,6 +53,9 @@ # Bytes + + false + @@ -132,7 +138,8 @@ ... - + + ..