mirror of
https://github.com/epasveer/seer.git
synced 2026-08-31 01:20:45 +08:00
1089 lines
38 KiB
C++
1089 lines
38 KiB
C++
// SPDX-FileCopyrightText: 2021 Ernie Pasveer <epasveer@att.net>
|
|
//
|
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
|
|
#include "SeerVarVisualizerWidget.h"
|
|
#include "SeerHelpPageDialog.h"
|
|
#include "SeerUtl.h"
|
|
#include "QEditDelegate.h"
|
|
#include <QtWidgets/QTreeWidget>
|
|
#include <QtWidgets/QTreeWidgetItemIterator>
|
|
#include <QtWidgets/QMenu>
|
|
#include <QAction>
|
|
#include <QtWidgets/QMessageBox>
|
|
#include <QtGui/QIcon>
|
|
#include <QtCore/QRegularExpression>
|
|
#include <QtCore/QTimer>
|
|
#include <QtCore/QSettings>
|
|
#include <QtCore/QDebug>
|
|
|
|
SeerVarVisualizerWidget::SeerVarVisualizerWidget (QWidget* parent) : QWidget(parent) {
|
|
|
|
// Init variables.
|
|
_variableId = Seer::createID(); // Create id for queries.
|
|
|
|
// Set up UI.
|
|
setupUi(this);
|
|
|
|
// Setup the widgets
|
|
setWindowIcon(QIcon(":/seer/resources/seergdb_64x64.png"));
|
|
setWindowTitle("Seer Struct Visualizer");
|
|
setAttribute(Qt::WA_DeleteOnClose);
|
|
|
|
variableNameLineEdit->setFocus();
|
|
variableTreeWidget->setMouseTracking(true);
|
|
variableTreeWidget->setSortingEnabled(false);
|
|
variableTreeWidget->setContextMenuPolicy(Qt::CustomContextMenu);
|
|
variableTreeWidget->setRootIsDecorated(true);
|
|
variableTreeWidget->setItemsExpandable(true);
|
|
variableTreeWidget->resizeColumnToContents(0); // variable
|
|
variableTreeWidget->resizeColumnToContents(1); // value
|
|
variableTreeWidget->resizeColumnToContents(2); // type
|
|
variableTreeWidget->resizeColumnToContents(3); // varobj name
|
|
variableTreeWidget->resizeColumnToContents(4); // varobj id
|
|
variableTreeWidget->resizeColumnToContents(5); // exp
|
|
variableTreeWidget->resizeColumnToContents(6); // numchild
|
|
variableTreeWidget->resizeColumnToContents(7); // thread-id
|
|
variableTreeWidget->resizeColumnToContents(8); // has_more
|
|
variableTreeWidget->resizeColumnToContents(9); // editable
|
|
variableTreeWidget->clear();
|
|
|
|
// Show debug columns.
|
|
debugCheckBox->setChecked(false);
|
|
debugCheckBox->hide();
|
|
|
|
// Create edit delegate.
|
|
// The value column will allow editing of the cell. However, some cells can then
|
|
// individually disable it again. ie: If the cell is for a node, not a value.
|
|
// See 'new QTreeWidgetItem'
|
|
QAllowEditDelegate* editDelegate = new QAllowEditDelegate(this);
|
|
|
|
variableTreeWidget->setItemDelegateForColumn(0, new QNoEditDelegate(this));
|
|
variableTreeWidget->setItemDelegateForColumn(1, editDelegate);
|
|
variableTreeWidget->setItemDelegateForColumn(2, new QNoEditDelegate(this));
|
|
variableTreeWidget->setItemDelegateForColumn(3, new QNoEditDelegate(this));
|
|
variableTreeWidget->setItemDelegateForColumn(4, new QNoEditDelegate(this));
|
|
variableTreeWidget->setItemDelegateForColumn(5, new QNoEditDelegate(this));
|
|
variableTreeWidget->setItemDelegateForColumn(6, new QNoEditDelegate(this));
|
|
variableTreeWidget->setItemDelegateForColumn(7, new QNoEditDelegate(this));
|
|
variableTreeWidget->setItemDelegateForColumn(8, new QNoEditDelegate(this));
|
|
variableTreeWidget->setItemDelegateForColumn(9, new QNoEditDelegate(this));
|
|
|
|
|
|
// Connect things.
|
|
QObject::connect(refreshToolButton, &QToolButton::clicked, this, &SeerVarVisualizerWidget::handleRefreshButton);
|
|
QObject::connect(helpToolButton, &QToolButton::clicked, this, &SeerVarVisualizerWidget::handleHelpButton);
|
|
QObject::connect(debugCheckBox, &QCheckBox::clicked, this, &SeerVarVisualizerWidget::handleDebugCheckBox);
|
|
QObject::connect(variableNameLineEdit, &QLineEdit::returnPressed, this, &SeerVarVisualizerWidget::handleVariableNameLineEdit);
|
|
QObject::connect(variableTreeWidget, &QTreeWidget::customContextMenuRequested, this, &SeerVarVisualizerWidget::handleContextMenu);
|
|
QObject::connect(variableTreeWidget, &QTreeWidget::itemEntered, this, &SeerVarVisualizerWidget::handleItemEntered);
|
|
QObject::connect(variableTreeWidget, &QTreeWidget::itemExpanded, this, &SeerVarVisualizerWidget::handleItemExpanded);
|
|
QObject::connect(variableTreeWidget, &QTreeWidget::itemCollapsed, this, &SeerVarVisualizerWidget::handleItemCollapsed);
|
|
QObject::connect(expandSelectedToolButton, &QToolButton::clicked, this, &SeerVarVisualizerWidget::handleExpandSelected);
|
|
QObject::connect(collapseSelectedToolButton, &QToolButton::clicked, this, &SeerVarVisualizerWidget::handleCollapseSelected);
|
|
QObject::connect(editDelegate, &QAllowEditDelegate::editingStarted, this, &SeerVarVisualizerWidget::handleIndexEditingStarted);
|
|
QObject::connect(editDelegate, &QAllowEditDelegate::editingFinished, this, &SeerVarVisualizerWidget::handleIndexEditingFinished);
|
|
|
|
// Show/hide columns.
|
|
handleDebugCheckBox();
|
|
|
|
// Restore window settings.
|
|
readSettings();
|
|
}
|
|
|
|
SeerVarVisualizerWidget::~SeerVarVisualizerWidget () {
|
|
|
|
// Send signal to delete variable.
|
|
if (_variableName != "") {
|
|
emit varObjDelete(_variableId, _variableName);
|
|
}
|
|
}
|
|
|
|
void SeerVarVisualizerWidget::setVariableName (const QString& name) {
|
|
|
|
setWindowTitle("Seer Struct Visualizer - '" + name + "'");
|
|
|
|
variableNameLineEdit->setText(name);
|
|
|
|
// Send signal to delete old variable.
|
|
if (_variableName != "") {
|
|
|
|
emit varObjDelete(_variableId, _variableName);
|
|
|
|
_variableName = "";
|
|
}
|
|
|
|
// Create the initial variable in the tree.
|
|
variableTreeWidget->clear();
|
|
|
|
if (variableNameLineEdit->text() != "") {
|
|
|
|
QTreeWidgetItem* item = new QTreeWidgetItem;
|
|
|
|
item->setText(0, name);
|
|
item->setText(1, "");
|
|
item->setText(2, "");
|
|
item->setText(3, "");
|
|
item->setText(4, "");
|
|
item->setText(5, "");
|
|
item->setText(6, "");
|
|
item->setText(7, "");
|
|
item->setText(8, "");
|
|
item->setText(9, "");
|
|
|
|
variableTreeWidget->addTopLevelItem(item);
|
|
}
|
|
|
|
// Resize columns.
|
|
handleResizeColumns();
|
|
|
|
// Send signal to get variable result.
|
|
if (variableNameLineEdit->text() != "") {
|
|
emit varObjCreate(_variableId, variableNameLineEdit->text());
|
|
}
|
|
}
|
|
|
|
QString SeerVarVisualizerWidget::variableName () const {
|
|
return variableNameLineEdit->text();
|
|
}
|
|
|
|
void SeerVarVisualizerWidget::handleText (const QString& text) {
|
|
|
|
if (text.contains(QRegularExpression("^([0-9]+)\\^done,name="))) {
|
|
|
|
//
|
|
// "-var-create x2112 "*" me"
|
|
//
|
|
// "4^done,name=\"seer4\",numchild=\"1\",value=\"{...}\",type=\"Person\",thread-id=\"1\",has_more=\"0\""
|
|
// --------------
|
|
//
|
|
|
|
QString id_text = text.section('^', 0,0);
|
|
|
|
if (id_text.toInt() == _variableId) {
|
|
|
|
//qDebug() << "var-create" << text;
|
|
|
|
QString name_text = Seer::parseFirst(text, "name=", '"', '"', false);
|
|
QString exp_text = Seer::parseFirst(text, "exp=", '"', '"', false);
|
|
QString numchild_text = Seer::parseFirst(text, "numchild=", '"', '"', false);
|
|
QString value_text = Seer::parseFirst(text, "value=", '"', '"', false);
|
|
QString type_text = Seer::parseFirst(text, "type=", '"', '"', false);
|
|
QString threadid_text = Seer::parseFirst(text, "thread-id=", '"', '"', false);
|
|
QString hasmore_text = Seer::parseFirst(text, "has_more=", '"', '"', false);
|
|
|
|
QTreeWidgetItem* topItem = variableTreeWidget->topLevelItem(0);
|
|
if (topItem == 0) {
|
|
return;
|
|
}
|
|
|
|
int varObjID = Seer::createID(); // Create id for queries.
|
|
|
|
topItem->setText(1, Seer::filterEscapes(value_text));
|
|
topItem->setText(2, type_text);
|
|
topItem->setText(3, name_text);
|
|
topItem->setText(4, QString::number(varObjID));
|
|
topItem->setText(5, exp_text);
|
|
topItem->setText(6, numchild_text);
|
|
topItem->setText(7, threadid_text);
|
|
topItem->setText(8, hasmore_text);
|
|
topItem->setText(9, "");
|
|
|
|
if (exp_text != "") {
|
|
topItem->setText(0, exp_text);
|
|
}
|
|
|
|
//XXX Ask for its editable attributes.
|
|
//XXX emit varObjAttributes (varObjID, name_text);
|
|
|
|
// If there are children, add a placeholder.
|
|
if (numchild_text != "0") {
|
|
if (type_text.endsWith('*')) {
|
|
if (Seer::filterEscapes(value_text) != "0x0") {
|
|
// If it is a pointer that is not null, add a placeholder. How universal is this for other languages?
|
|
QTreeWidgetItem* child = new QTreeWidgetItem;
|
|
child->setText(0, "{...}");
|
|
child->setText(3, name_text);
|
|
|
|
topItem->addChild(child);
|
|
|
|
}else{
|
|
deleteItems(topItem->takeChildren());
|
|
}
|
|
|
|
}else{
|
|
// A non-pointer child, add a placeholder.
|
|
QTreeWidgetItem* child = new QTreeWidgetItem;
|
|
child->setText(0, "{...}");
|
|
child->setText(3, name_text);
|
|
|
|
topItem->addChild(child);
|
|
}
|
|
|
|
}
|
|
|
|
// Save the VarObj name.
|
|
_variableName = name_text;
|
|
}
|
|
|
|
|
|
}else if (text.contains(QRegularExpression("^([0-9]+)\\^done,numchild="))) {
|
|
|
|
//
|
|
// "-var-list-children --all-values x2112.public"
|
|
//
|
|
// "4^done,numchild="1", children=[
|
|
// ------------
|
|
// child={name="x2112.public",exp="public",numchild="4",thread-id="1"}
|
|
// ],
|
|
// has_more="0"
|
|
//
|
|
|
|
QString id_text = text.section('^', 0,0);
|
|
|
|
if (id_text.toInt() == _variableId) {
|
|
|
|
//qDebug() << "var-list-children" << text;
|
|
|
|
QString children_text = Seer::parseFirst(text, "children=", '[', ']', false);
|
|
QStringList child_list = Seer::parse(children_text, "child=", '{', '}', false);
|
|
QString hasmore_text = Seer::parseFirst(text, "has_more=", '"', '"', false);
|
|
|
|
for (const auto& child_text : child_list) {
|
|
|
|
QString name_text = Seer::parseFirst(child_text, "name=", '"', '"', false);
|
|
QString exp_text = Seer::parseFirst(child_text, "exp=", '"', '"', false);
|
|
QString numchild_text = Seer::parseFirst(child_text, "numchild=", '"', '"', false);
|
|
QString value_text = Seer::parseFirst(child_text, "value=", '"', '"', false);
|
|
QString type_text = Seer::parseFirst(child_text, "type=", '"', '"', false);
|
|
QString threadid_text = Seer::parseFirst(child_text, "thread-id=", '"', '"', false);
|
|
|
|
// Do we have an existing item to add to?
|
|
QTreeWidgetItem* matchItem = findItem(Seer::varObjParent(name_text), Qt::MatchExactly|Qt::MatchRecursive, 3);
|
|
|
|
// No, just add to the top level.
|
|
if (matchItem == 0) {
|
|
|
|
// This shouldn't really happen.
|
|
qDebug() << name_text << "from the listchildren does not exist in the tree.";
|
|
|
|
// Yes, add to it.
|
|
}else{
|
|
|
|
// See if item has a "{...}" child. If so, remove it and any other siblings.
|
|
if (matchItem->childCount() > 0) {
|
|
QTreeWidgetItem* childItem = matchItem->child(0);
|
|
if (childItem->text(0) == "{...}") {
|
|
deleteItems(matchItem->takeChildren());
|
|
}
|
|
}
|
|
|
|
// Create the item.
|
|
QTreeWidgetItem* item = new QTreeWidgetItem;
|
|
|
|
int varObjID = Seer::createID(); // Create id for queries.
|
|
|
|
item->setText(0, "");
|
|
item->setText(1, Seer::filterEscapes(value_text));
|
|
item->setText(2, type_text);
|
|
item->setText(3, name_text);
|
|
item->setText(4, QString::number(varObjID));
|
|
item->setText(5, exp_text);
|
|
item->setText(6, numchild_text);
|
|
item->setText(7, threadid_text);
|
|
item->setText(8, hasmore_text);
|
|
item->setText(9, "");
|
|
item->setFlags(item->flags() | Qt::ItemIsEditable); //XXX Set item editable if it can.
|
|
|
|
if (exp_text != "") {
|
|
item->setText(0, exp_text);
|
|
}
|
|
|
|
//XXX Ask for its editable attributes.
|
|
//XXX emit varObjAttributes (varObjID, name_text);
|
|
|
|
// If there are children, add a placeholder.
|
|
bool expandItem = false;
|
|
|
|
if (numchild_text != "0") {
|
|
if (type_text.endsWith('*')) {
|
|
if (Seer::filterEscapes(value_text) != "0x0") {
|
|
// If it is a pointer that is not null, add a placeholder. How universal is this for other languages?
|
|
QTreeWidgetItem* child = new QTreeWidgetItem;
|
|
child->setText(0, "{...}");
|
|
child->setText(3, name_text);
|
|
|
|
item->addChild(child);
|
|
|
|
}else{
|
|
deleteItems(item->takeChildren());
|
|
}
|
|
|
|
}else{
|
|
// A non-pointer child, add a placeholder.
|
|
QTreeWidgetItem* child = new QTreeWidgetItem;
|
|
child->setText(0, "{...}");
|
|
child->setText(3, name_text);
|
|
|
|
item->addChild(child);
|
|
|
|
expandItem = true;
|
|
}
|
|
}
|
|
|
|
matchItem->addChild(item);
|
|
|
|
if (expandItem == true && expandRecursiveCheckBox->isChecked()) {
|
|
item->setExpanded(true);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
}else if (text.contains(QRegularExpression("^([0-9]+)\\^done,changelist="))) {
|
|
|
|
//qDebug() << "var-update-children" << text;
|
|
|
|
//
|
|
// "4^done,changelist=[
|
|
// {name=\"seer4.public.age\", value=\"60\", in_scope=\"true\", type_changed=\"false\", has_more=\"0\"},
|
|
// {name=\"seer4.public.salary\", value=\"0.25\", in_scope=\"true\", type_changed=\"false\", has_more=\"0\"},
|
|
// {name=\"seer4.public.location.public.city\", value=\"\\\"Houston\\\"\", in_scope=\"true\", type_changed=\"false\", displayhint=\"string\", dynamic=\"1\", has_more=\"0\"},
|
|
// {name=\"seer4.public.location.public.state\", value=\"\\\"Texas\\\"\", in_scope=\"true\", type_changed=\"false\", displayhint=\"string\", dynamic=\"1\", has_more=\"0\"},
|
|
// {name=\"seer4.public.location.public.zip\", value=\"77063\", in_scope=\"true\", type_changed=\"false\", has_more=\"0\"}
|
|
// ]"
|
|
|
|
QString id_text = text.section('^', 0,0);
|
|
|
|
if (id_text.toInt() == _variableId) {
|
|
|
|
QString changelist_text = Seer::parseFirst(text, "changelist=", '[', ']', false);
|
|
QStringList child_list = Seer::parse(changelist_text, "", '{', '}', false);
|
|
|
|
for (const auto& child_text : child_list) {
|
|
|
|
QString name_text = Seer::parseFirst(child_text, "name=", '"', '"', false);
|
|
QString value_text = Seer::parseFirst(child_text, "value=", '"', '"', false);
|
|
QString inscope_text = Seer::parseFirst(child_text, "in_scope=", '"', '"', false);
|
|
QString typechanged_text = Seer::parseFirst(child_text, "type_changed=", '"', '"', false);
|
|
QString displayhint_text = Seer::parseFirst(child_text, "displayhint=", '"', '"', false);
|
|
QString dynamic_text = Seer::parseFirst(child_text, "dynamic=", '"', '"', false);
|
|
QString hasmore_text = Seer::parseFirst(child_text, "has_more=", '"', '"', false);
|
|
|
|
// Do we have an existing item to add to?
|
|
QTreeWidgetItem* matchItem = findItem(name_text, Qt::MatchExactly|Qt::MatchRecursive, 3);
|
|
|
|
// No, just add to the top level.
|
|
if (matchItem == 0) {
|
|
|
|
qDebug() << name_text << "from the changelist does not exist in the tree.";
|
|
|
|
// Yes, add to it.
|
|
}else{
|
|
|
|
matchItem->setText(1, Seer::filterEscapes(value_text));
|
|
matchItem->setText(7, hasmore_text);
|
|
|
|
// If there are children, add a placeholder.
|
|
if (matchItem->text(5) != "0") {
|
|
if (matchItem->text(2).endsWith('*')) {
|
|
if (Seer::filterEscapes(matchItem->text(1)) != "0x0") {
|
|
// If it is a pointer that is not null, add a placeholder. How universal is this for other languages?
|
|
QTreeWidgetItem* child = new QTreeWidgetItem;
|
|
child->setText(0, "{...}");
|
|
child->setText(3, matchItem->text(3));
|
|
|
|
matchItem->addChild(child);
|
|
|
|
}else{
|
|
deleteItems(matchItem->takeChildren());
|
|
}
|
|
|
|
}else{
|
|
// A non-pointer child, add a placeholder.
|
|
QTreeWidgetItem* child = new QTreeWidgetItem;
|
|
child->setText(0, "{...}");
|
|
child->setText(3, matchItem->text(3));
|
|
|
|
matchItem->addChild(child);
|
|
}
|
|
}
|
|
|
|
// Need to refresh?
|
|
if (typechanged_text == "true") {
|
|
handleRefreshButton();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
}else if (text.contains(QRegularExpression("^([0-9]+)\\^done,attr="))) {
|
|
|
|
//qDebug() << "var-show-attributes" << text;
|
|
|
|
// 4^done,attr="noneditable"
|
|
// 4^done,attr="editable"
|
|
|
|
QString id_text = text.section('^', 0,0);
|
|
QString editable_text = Seer::parseFirst(text, "attr=", '"', '"', false);
|
|
|
|
// Do we have any items that match the varobjid?
|
|
QTreeWidgetItem* matchItem = findItem(id_text, Qt::MatchExactly|Qt::MatchRecursive, 4);
|
|
|
|
if (matchItem) {
|
|
matchItem->setText(9, editable_text);
|
|
|
|
if (editable_text == "editable") {
|
|
matchItem->setFlags(matchItem->flags() | Qt::ItemIsEditable);
|
|
}else{
|
|
matchItem->setFlags(matchItem->flags() & ~Qt::ItemIsEditable);
|
|
}
|
|
}
|
|
|
|
|
|
}else if (text.contains(QRegularExpression("^([0-9]+)\\^error,msg="))) {
|
|
|
|
QString id_text = text.section('^', 0,0);
|
|
QString msg_text = Seer::parseFirst(text, "msg=", '"', '"', false);
|
|
|
|
if (id_text.toInt() == _variableId) {
|
|
|
|
QMessageBox::warning(this, "Seer",
|
|
QString("Visualizer Error: '%1'\n").arg(Seer::filterEscapes(msg_text)),
|
|
QMessageBox::Ok);
|
|
|
|
}
|
|
|
|
if (msg_text.contains("Variable object is not editable")) {
|
|
|
|
if (_previousEditName != "") {
|
|
|
|
QTreeWidgetItem* matchItem = findItem(_previousEditName, Qt::MatchExactly|Qt::MatchRecursive, 3);
|
|
if (matchItem) {
|
|
matchItem->setText(1, _previousEditValue);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
}else if (text.startsWith("^error,msg=\"No registers.\"")) {
|
|
variableTreeWidget->clear();
|
|
|
|
// At a stopping point, refresh.
|
|
}else if (text.startsWith("*stopped,reason=\"")) {
|
|
|
|
if (autoRefreshCheckBox->isChecked()) {
|
|
handleRefreshButton();
|
|
}
|
|
|
|
}else{
|
|
// Ignore anything else.
|
|
}
|
|
|
|
// Resize columns.
|
|
handleResizeColumns();
|
|
}
|
|
|
|
void SeerVarVisualizerWidget::handleContextMenu (const QPoint& pos) {
|
|
|
|
QTreeWidgetItem* item = variableTreeWidget->itemAt(pos);
|
|
|
|
if (item == 0) {
|
|
return;
|
|
}
|
|
|
|
// Create the variable name.
|
|
// If it's a struct, include its parent names.
|
|
QString variable = fullVariableName(item);
|
|
|
|
// Create the menus.
|
|
QAction* expandItemAction = new QAction(QString("Expand item"));
|
|
QAction* collapseItemAction = new QAction(QString("Collapse item"));
|
|
QAction* addMemoryVisualizerAction = new QAction(QString("\"%1\"").arg(variable));
|
|
QAction* addMemoryAsteriskVisualizerAction = new QAction(QString("\"*%1\"").arg(variable));
|
|
QAction* addMemoryAmpersandVisualizerAction = new QAction(QString("\"&&%1\"").arg(variable));
|
|
QAction* addArrayVisualizerAction = new QAction(QString("\"%1\"").arg(variable));
|
|
QAction* addArrayAsteriskVisualizerAction = new QAction(QString("\"*%1\"").arg(variable));
|
|
QAction* addArrayAmpersandVisualizerAction = new QAction(QString("\"&&%1\"").arg(variable));
|
|
QAction* addMatrixVisualizerAction = new QAction(QString("\"%1\"").arg(variable));
|
|
QAction* addMatrixAsteriskVisualizerAction = new QAction(QString("\"*%1\"").arg(variable));
|
|
QAction* addMatrixAmpersandVisualizerAction = new QAction(QString("\"&&%1\"").arg(variable));
|
|
QAction* addVarVisualizerAction = new QAction(QString("\"%1\"").arg(variable));
|
|
QAction* addVarAsteriskVisualizerAction = new QAction(QString("\"*%1\"").arg(variable));
|
|
QAction* addVarAmpersandVisualizerAction = new QAction(QString("\"&&%1\"").arg(variable));
|
|
|
|
expandItemAction->setIcon(QIcon(":/seer/resources/RelaxLightIcons/list-add.svg"));
|
|
collapseItemAction->setIcon(QIcon(":/seer/resources/RelaxLightIcons/list-remove.svg"));
|
|
|
|
// Are we on an item that has children?
|
|
expandItemAction->setEnabled(false);
|
|
collapseItemAction->setEnabled(false);
|
|
|
|
expandItemAction->setText(QString("Expand item '%1'").arg(item->text(0)));
|
|
collapseItemAction->setText(QString("Collapse item '%1'").arg(item->text(0)));
|
|
|
|
if (item->childCount() > 0) {
|
|
if (item->isExpanded() == false) {
|
|
expandItemAction->setEnabled(true);
|
|
}else{
|
|
collapseItemAction->setEnabled(true);
|
|
}
|
|
}
|
|
|
|
// Populate the menu.
|
|
QMenu menu("Visualizers", this);
|
|
menu.setTitle("Visualizers");
|
|
|
|
menu.addAction(expandItemAction);
|
|
menu.addAction(collapseItemAction);
|
|
|
|
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 an Matrix Visualizer");
|
|
matrixVisualizerMenu.addAction(addMatrixVisualizerAction);
|
|
matrixVisualizerMenu.addAction(addMatrixAsteriskVisualizerAction);
|
|
matrixVisualizerMenu.addAction(addMatrixAmpersandVisualizerAction);
|
|
menu.addMenu(&matrixVisualizerMenu);
|
|
|
|
QMenu structVisualizerMenu("Add variable to a Struct Visualizer");
|
|
structVisualizerMenu.addAction(addVarVisualizerAction);
|
|
structVisualizerMenu.addAction(addVarAsteriskVisualizerAction);
|
|
structVisualizerMenu.addAction(addVarAmpersandVisualizerAction);
|
|
menu.addMenu(&structVisualizerMenu);
|
|
|
|
// Launch the menu. Get the response.
|
|
QAction* action = menu.exec(variableTreeWidget->viewport()->mapToGlobal(pos));
|
|
|
|
// Do nothing.
|
|
if (action == 0) {
|
|
return;
|
|
}
|
|
|
|
// Handle expanding the current item.
|
|
if (action == expandItemAction) {
|
|
|
|
expandItem(item);
|
|
|
|
return;
|
|
}
|
|
|
|
// Handle collapsing the current item.
|
|
if (action == collapseItemAction) {
|
|
|
|
collapseItem(item);
|
|
|
|
return;
|
|
}
|
|
|
|
// Handle adding memory to visualize.
|
|
if (action == addMemoryVisualizerAction) {
|
|
|
|
//qDebug() << "addMemoryVisualizer" << variable;
|
|
|
|
// Emit the signals.
|
|
if (variable != "") {
|
|
emit addMemoryVisualizer(variable);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
// Handle adding memory to visualize.
|
|
if (action == addMemoryAsteriskVisualizerAction) {
|
|
|
|
//qDebug() << "addMemoryAsteriskVisualizer" << variable;
|
|
|
|
// Emit the signals.
|
|
if (variable != "") {
|
|
emit addMemoryVisualizer(QString("*") + variable);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
// Handle adding memory to visualize.
|
|
if (action == addMemoryAmpersandVisualizerAction) {
|
|
|
|
//qDebug() << "addMemoryAmpersandVisualizer" << variable;
|
|
|
|
// Emit the signals.
|
|
if (variable != "") {
|
|
emit addMemoryVisualizer(QString("&") + variable);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
// Handle adding array to visualize.
|
|
if (action == addArrayVisualizerAction) {
|
|
|
|
//qDebug() << "addArrayVisualizer" << variable;
|
|
|
|
// Emit the signals.
|
|
if (variable != "") {
|
|
emit addArrayVisualizer(variable);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
// Handle adding array to visualize.
|
|
if (action == addArrayAsteriskVisualizerAction) {
|
|
|
|
//qDebug() << "addArrayAsteriskVisualizer" << variable;
|
|
|
|
// Emit the signals.
|
|
if (variable != "") {
|
|
emit addArrayVisualizer(QString("*") + variable);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
// Handle adding array to visualize.
|
|
if (action == addArrayAmpersandVisualizerAction) {
|
|
|
|
//qDebug() << "addArrayAmpersandVisualizer" << variable;
|
|
|
|
// Emit the signals.
|
|
if (variable != "") {
|
|
emit addArrayVisualizer(QString("&") + variable);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
// Handle adding array to visualize.
|
|
if (action == addMatrixVisualizerAction) {
|
|
|
|
//qDebug() << "addMatrixVisualizer" << variable;
|
|
|
|
// Emit the signals.
|
|
if (variable != "") {
|
|
emit addMatrixVisualizer(variable);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
// Handle adding array to visualize.
|
|
if (action == addMatrixAsteriskVisualizerAction) {
|
|
|
|
//qDebug() << "addMatrixAsteriskVisualizer" << variable;
|
|
|
|
// Emit the signals.
|
|
if (variable != "") {
|
|
emit addMatrixVisualizer(QString("*") + variable);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
// Handle adding array to visualize.
|
|
if (action == addMatrixAmpersandVisualizerAction) {
|
|
|
|
//qDebug() << "addMatrixAmpersandVisualizer" << variable;
|
|
|
|
// Emit the signals.
|
|
if (variable != "") {
|
|
emit addMatrixVisualizer(QString("&") + variable);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
// Handle adding struct to visualize.
|
|
if (action == addVarVisualizerAction) {
|
|
|
|
//qDebug() << "addVarVisualizer" << variable;
|
|
|
|
// Emit the signals.
|
|
if (variable != "") {
|
|
emit addVarVisualizer(variable);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
// Handle adding struct to visualize.
|
|
if (action == addVarAsteriskVisualizerAction) {
|
|
|
|
//qDebug() << "addVarAsteriskVisualizer" << variable;
|
|
|
|
// Emit the signals.
|
|
if (variable != "") {
|
|
emit addVarVisualizer(QString("*") + variable);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
// Handle adding struct to visualize.
|
|
if (action == addVarAmpersandVisualizerAction) {
|
|
|
|
//qDebug() << "addVarAmpersandVisualizer" << variable;
|
|
|
|
// Emit the signals.
|
|
if (variable != "") {
|
|
emit addVarVisualizer(QString("&") + variable);
|
|
}
|
|
|
|
return;
|
|
}
|
|
}
|
|
|
|
void SeerVarVisualizerWidget::handleItemEntered (QTreeWidgetItem* item, int column) {
|
|
|
|
Q_UNUSED(column);
|
|
|
|
QString text = toolTipText(item);
|
|
|
|
if (text != "") {
|
|
for (int i=0; i<variableTreeWidget->columnCount(); i++) { // Set tooltip to all columns.
|
|
item->setToolTip(i, text);
|
|
}
|
|
|
|
// Ask for its editable attributes.
|
|
if (item->text(4) != "") {
|
|
//XXX emit varObjAttributes (item->text(4).toInt(), item->text(3));
|
|
}
|
|
}
|
|
}
|
|
|
|
void SeerVarVisualizerWidget::handleItemExpanded (QTreeWidgetItem* item) {
|
|
|
|
// Ask for the children.
|
|
emit varObjListChildren(_variableId, item->text(3));
|
|
|
|
// Resize columns in a sec.
|
|
// Have to schedule the resize later. Doing it immediatedly messes up the
|
|
// tree display. Must be a Qt bug.
|
|
QTimer::singleShot(100, this, &SeerVarVisualizerWidget::handleResizeColumns);
|
|
}
|
|
|
|
void SeerVarVisualizerWidget::handleItemCollapsed (QTreeWidgetItem* item) {
|
|
|
|
// Delete all children items.
|
|
deleteItems(item->takeChildren());
|
|
|
|
// If there are children, add a placeholder.
|
|
if (item->text(5) != "0") {
|
|
if (item->text(2).endsWith('*')) {
|
|
if (Seer::filterEscapes(item->text(1)) != "0x0") {
|
|
// If it is a pointer that is not null, add a placeholder. How universal is this for other languages?
|
|
QTreeWidgetItem* child = new QTreeWidgetItem;
|
|
child->setText(0, "{...}");
|
|
child->setText(3, item->text(3));
|
|
|
|
item->addChild(child);
|
|
}
|
|
|
|
}else{
|
|
// A non-pointer child, add a placeholder.
|
|
QTreeWidgetItem* child = new QTreeWidgetItem;
|
|
child->setText(0, "{...}");
|
|
child->setText(3, item->text(3));
|
|
|
|
item->addChild(child);
|
|
}
|
|
}
|
|
|
|
// Resize columns in a sec.
|
|
// Have to schedule the resize later. Doing it immediatedly messes up the
|
|
// tree display. Must be a Qt bug.
|
|
QTimer::singleShot(100, this, &SeerVarVisualizerWidget::handleResizeColumns);
|
|
}
|
|
|
|
void SeerVarVisualizerWidget::handleExpandSelected () {
|
|
|
|
// Expand the current item, if selected.
|
|
if (variableTreeWidget->currentItem()) {
|
|
expandItem(variableTreeWidget->currentItem());
|
|
return;
|
|
}
|
|
|
|
// Or the entire tree.
|
|
if (variableTreeWidget->topLevelItemCount() > 0) {
|
|
expandItem(variableTreeWidget->topLevelItem(0));
|
|
}
|
|
}
|
|
|
|
void SeerVarVisualizerWidget::handleCollapseSelected () {
|
|
|
|
// Collapse the current item, if selected.
|
|
if (variableTreeWidget->currentItem()) {
|
|
collapseItem(variableTreeWidget->currentItem());
|
|
return;
|
|
}
|
|
|
|
// Or the entire tree.
|
|
if (variableTreeWidget->topLevelItemCount() > 0) {
|
|
collapseItem(variableTreeWidget->topLevelItem(0));
|
|
}
|
|
}
|
|
|
|
void SeerVarVisualizerWidget::handleResizeColumns () {
|
|
|
|
// Resize columns.
|
|
variableTreeWidget->resizeColumnToContents(0);
|
|
variableTreeWidget->resizeColumnToContents(1);
|
|
variableTreeWidget->resizeColumnToContents(2);
|
|
variableTreeWidget->resizeColumnToContents(3);
|
|
variableTreeWidget->resizeColumnToContents(4);
|
|
variableTreeWidget->resizeColumnToContents(5);
|
|
variableTreeWidget->resizeColumnToContents(6);
|
|
variableTreeWidget->resizeColumnToContents(7);
|
|
variableTreeWidget->resizeColumnToContents(8);
|
|
variableTreeWidget->resizeColumnToContents(9);
|
|
}
|
|
|
|
void SeerVarVisualizerWidget::handleHideDebugColumns (bool flag) {
|
|
|
|
variableTreeWidget->setColumnHidden(0, false);
|
|
variableTreeWidget->setColumnHidden(1, false);
|
|
variableTreeWidget->setColumnHidden(2, false);
|
|
variableTreeWidget->setColumnHidden(3, flag);
|
|
variableTreeWidget->setColumnHidden(4, flag);
|
|
variableTreeWidget->setColumnHidden(5, flag);
|
|
variableTreeWidget->setColumnHidden(6, flag);
|
|
variableTreeWidget->setColumnHidden(7, flag);
|
|
variableTreeWidget->setColumnHidden(8, flag);
|
|
variableTreeWidget->setColumnHidden(9, flag);
|
|
|
|
handleResizeColumns();
|
|
}
|
|
|
|
void SeerVarVisualizerWidget::handleRefreshButton () {
|
|
|
|
// Send signal to get variable result.
|
|
if (_variableName != "") {
|
|
emit varObjUpdate(_variableId, _variableName);
|
|
}
|
|
}
|
|
|
|
void SeerVarVisualizerWidget::handleHelpButton () {
|
|
|
|
SeerHelpPageDialog* help = new SeerHelpPageDialog;
|
|
help->loadFile(":/seer/resources/help/StructVisualizer.md");
|
|
help->show();
|
|
help->raise();
|
|
}
|
|
|
|
void SeerVarVisualizerWidget::handleDebugCheckBox () {
|
|
|
|
handleHideDebugColumns(!debugCheckBox->isChecked());
|
|
}
|
|
|
|
void SeerVarVisualizerWidget::handleVariableNameLineEdit () {
|
|
|
|
if (variableNameLineEdit->text() == "") {
|
|
return;
|
|
}
|
|
|
|
setVariableName (variableNameLineEdit->text());
|
|
}
|
|
|
|
void SeerVarVisualizerWidget::handleIndexEditingStarted (const QModelIndex& index) {
|
|
|
|
_previousEditName = "";
|
|
_previousEditValue = "";
|
|
|
|
QTreeWidgetItem* item = variableTreeWidget->getItemFromIndex(index);
|
|
|
|
if (item == 0) {
|
|
return;
|
|
}
|
|
|
|
// Get the old value;
|
|
_previousEditName = item->text(3);
|
|
_previousEditValue = item->text(1);
|
|
}
|
|
|
|
void SeerVarVisualizerWidget::handleIndexEditingFinished (const QModelIndex& index) {
|
|
|
|
QTreeWidgetItem* item = variableTreeWidget->getItemFromIndex(index);
|
|
|
|
if (item == 0) {
|
|
return;
|
|
}
|
|
|
|
// Get the new value;
|
|
QString value = item->text(1);
|
|
|
|
// If it hasn't changed, we do need to bother.
|
|
if (value == _previousEditValue) {
|
|
|
|
_previousEditName = "";
|
|
_previousEditValue = "";
|
|
|
|
return;
|
|
}
|
|
|
|
// Emit the signal to change the varobj to the new value.
|
|
emit varObjAssign(_variableId, item->text(3), value);
|
|
}
|
|
|
|
QTreeWidgetItem* SeerVarVisualizerWidget::findItem (const QString& text, Qt::MatchFlags flags, int column) {
|
|
|
|
QList<QTreeWidgetItem*> matches = variableTreeWidget->findItems(text, flags, column);
|
|
|
|
if (matches.size() == 0) {
|
|
return nullptr;
|
|
}
|
|
|
|
return matches[0];
|
|
}
|
|
|
|
void SeerVarVisualizerWidget::writeSettings () {
|
|
|
|
QSettings settings;
|
|
|
|
settings.beginGroup("varvisualizerwindow");
|
|
settings.setValue("size", size());
|
|
settings.endGroup();
|
|
}
|
|
|
|
void SeerVarVisualizerWidget::readSettings () {
|
|
|
|
QSettings settings;
|
|
|
|
settings.beginGroup("varvisualizerwindow");
|
|
resize(settings.value("size", QSize(800, 400)).toSize());
|
|
settings.endGroup();
|
|
}
|
|
|
|
void SeerVarVisualizerWidget::resizeEvent (QResizeEvent* event) {
|
|
|
|
writeSettings();
|
|
|
|
QWidget::resizeEvent(event);
|
|
}
|
|
|
|
void SeerVarVisualizerWidget::expandItem (QTreeWidgetItem* item) {
|
|
|
|
if (item->childCount() == 0) {
|
|
return;
|
|
}
|
|
|
|
if (item->isExpanded() == false) {
|
|
item->setExpanded(true);
|
|
}
|
|
}
|
|
|
|
void SeerVarVisualizerWidget::collapseItem (QTreeWidgetItem* item) {
|
|
|
|
if (item->childCount() == 0) {
|
|
return;
|
|
}
|
|
|
|
if (item->isExpanded() == true) {
|
|
item->setExpanded(false);
|
|
}
|
|
}
|
|
|
|
QString SeerVarVisualizerWidget::fullVariableName (QTreeWidgetItem* item) {
|
|
|
|
// If the item has no value or type, then return no variable.
|
|
if (item->text(1) == "" || item->text(2) == "") {
|
|
return "";
|
|
}
|
|
|
|
// Otherwise traverse up the tree to build up a variable name string, which includes '.' or '->'
|
|
// depending if the type ends with a '*'.
|
|
QString name;
|
|
QTreeWidgetItem* tmp = item;
|
|
|
|
while (tmp) {
|
|
|
|
// Skip items that have no value or type. 'public', 'private', 'protected'.
|
|
if (tmp->text(1) == "" || tmp->text(2) == "" || (tmp->text(0) == tmp->text(2))) {
|
|
tmp = tmp->parent();
|
|
continue;
|
|
}
|
|
|
|
// Add the variable part to the variable name.
|
|
if (tmp->childCount() > 0) {
|
|
if (tmp->text(2).endsWith('*')) {
|
|
if (name == "") {
|
|
name.prepend(tmp->text(0));
|
|
}else{
|
|
name.prepend(tmp->text(0) + "->");
|
|
}
|
|
}else{
|
|
if (name == "") {
|
|
name.prepend(tmp->text(0));
|
|
}else{
|
|
name.prepend(tmp->text(0) + ".");
|
|
}
|
|
}
|
|
}else{
|
|
name.prepend(tmp->text(0));
|
|
}
|
|
|
|
// Move to the parent to get the type, if we can.
|
|
tmp = tmp->parent();
|
|
}
|
|
|
|
return name;
|
|
}
|
|
|
|
QString SeerVarVisualizerWidget::toolTipText (QTreeWidgetItem* item) {
|
|
|
|
// If the item has no value or type, then return no tooltip.
|
|
if (item->text(1) == "" || item->text(2) == "") {
|
|
return "";
|
|
}
|
|
|
|
// Get the full variable name.
|
|
QString name = fullVariableName(item);
|
|
|
|
if (name == "") {
|
|
return name;
|
|
}
|
|
|
|
// Tack on the variable value and type.
|
|
QString text = name + " : " + item->text(1) + " : " + item->text(2);
|
|
|
|
return text;
|
|
}
|
|
|
|
void SeerVarVisualizerWidget::debug (QString message, QTreeWidgetItem* item) {
|
|
|
|
// 0 variable
|
|
// 1 value
|
|
// 2 type
|
|
// 3 varobj name
|
|
// 4 varobj id
|
|
// 5 exp
|
|
// 6 numchild
|
|
// 7 thread-id
|
|
// 8 has_more
|
|
// 8 editable
|
|
|
|
if (item == 0) {
|
|
qDebug() << message << "NULL";
|
|
}else{
|
|
qDebug() << message << item->text(0) << item->text(1) << item->text(2) << item->text(3) << item->text(4) << item->text(5) << item->text(6) << item->text(7) << item->text(8) << item->text(9);
|
|
}
|
|
}
|
|
|
|
void SeerVarVisualizerWidget::deleteItems (QList<QTreeWidgetItem*> items) {
|
|
|
|
// Loop through the list and delete the items.
|
|
// Note, the list will still contain pointers, but they will be invalid.
|
|
foreach (auto item, items) delete item;
|
|
}
|
|
|