mirror of
https://github.com/rizinorg/cutter.git
synced 2025-02-20 13:46:06 +00:00
Implement Editing and Viewing of Types in the Types Widget (#1597)
* Implemet Edit and View for types * Rename LoadNewTypes to TypesInteraction
This commit is contained in:
parent
228d3a454a
commit
5fb2c8ac6f
@ -331,7 +331,7 @@ SOURCES += \
|
||||
dialogs/WelcomeDialog.cpp \
|
||||
common/RunScriptTask.cpp \
|
||||
dialogs/EditMethodDialog.cpp \
|
||||
dialogs/LoadNewTypesDialog.cpp \
|
||||
dialogs/TypesInteractionDialog.cpp \
|
||||
widgets/SdbWidget.cpp \
|
||||
common/PythonManager.cpp \
|
||||
plugins/PluginManager.cpp \
|
||||
@ -457,7 +457,7 @@ HEADERS += \
|
||||
common/Json.h \
|
||||
dialogs/EditMethodDialog.h \
|
||||
common/CrashHandler.h \
|
||||
dialogs/LoadNewTypesDialog.h \
|
||||
dialogs/TypesInteractionDialog.h \
|
||||
widgets/SdbWidget.h \
|
||||
common/PythonManager.h \
|
||||
plugins/PluginManager.h \
|
||||
@ -532,7 +532,7 @@ FORMS += \
|
||||
dialogs/HexdumpRangeDialog.ui \
|
||||
dialogs/WelcomeDialog.ui \
|
||||
dialogs/EditMethodDialog.ui \
|
||||
dialogs/LoadNewTypesDialog.ui \
|
||||
dialogs/TypesInteractionDialog.ui \
|
||||
widgets/SdbWidget.ui \
|
||||
dialogs/LinkTypeDialog.ui \
|
||||
widgets/ColorPicker.ui \
|
||||
|
@ -2349,6 +2349,7 @@ QList<TypeDescription> CutterCore::getAllTypedefs()
|
||||
|
||||
QString CutterCore::addTypes(const char *str)
|
||||
{
|
||||
CORE_LOCK();
|
||||
char *error_msg = nullptr;
|
||||
char *parsed = r_parse_c_string(core_->anal, str, &error_msg);
|
||||
QString error;
|
||||
@ -2372,6 +2373,26 @@ QString CutterCore::addTypes(const char *str)
|
||||
return error;
|
||||
}
|
||||
|
||||
QString CutterCore::getTypeAsC(QString name, QString category)
|
||||
{
|
||||
CORE_LOCK();
|
||||
QString output = "Failed to fetch the output.";
|
||||
if (name.isEmpty() || category.isEmpty()) {
|
||||
return output;
|
||||
}
|
||||
QString typeName = sanitizeStringForCommand(name);
|
||||
if (category == "Struct") {
|
||||
output = cmd (QString("tsc %1").arg(typeName));
|
||||
} else if (category == "Union") {
|
||||
output = cmd (QString("tuc %1").arg(typeName));
|
||||
} else if(category == "Enum") {
|
||||
output = cmd (QString("tec %1").arg(typeName));
|
||||
} else if(category == "Typedef") {
|
||||
output = cmd (QString("ttc %1").arg(typeName));
|
||||
}
|
||||
return output;
|
||||
}
|
||||
|
||||
bool CutterCore::isAddressMapped(RVA addr)
|
||||
{
|
||||
// If value returned by "om. @ addr" is empty means that address is not mapped
|
||||
|
@ -335,6 +335,15 @@ public:
|
||||
*/
|
||||
QList<TypeDescription> getAllTypedefs();
|
||||
|
||||
/**
|
||||
* @brief Fetching the C representation of a given Type
|
||||
* @param name - the name or the type of the given Type / Struct
|
||||
* @param category - the category of the given Type (Struct, Union, Enum, ...)
|
||||
* @return The type decleration as C output
|
||||
*/
|
||||
QString getTypeAsC(QString name, QString category);
|
||||
|
||||
|
||||
/**
|
||||
* @brief Adds new types
|
||||
* It first uses the r_parse_c_string() function from radare2 API to parse the
|
||||
|
@ -1,5 +1,5 @@
|
||||
#include "dialogs/LoadNewTypesDialog.h"
|
||||
#include "ui_LoadNewTypesDialog.h"
|
||||
#include "dialogs/TypesInteractionDialog.h"
|
||||
#include "ui_TypesInteractionDialog.h"
|
||||
|
||||
#include "core/Cutter.h"
|
||||
#include "common/Configuration.h"
|
||||
@ -9,20 +9,20 @@
|
||||
#include <QFileDialog>
|
||||
#include <QTemporaryFile>
|
||||
|
||||
LoadNewTypesDialog::LoadNewTypesDialog(QWidget *parent) :
|
||||
TypesInteractionDialog::TypesInteractionDialog(QWidget *parent, bool readOnly) :
|
||||
QDialog(parent),
|
||||
ui(new Ui::LoadNewTypesDialog)
|
||||
ui(new Ui::TypesInteractionDialog)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
ui->plainTextEdit->setPlainText("");
|
||||
syntaxHighLighter = new SyntaxHighlighter(ui->plainTextEdit->document());
|
||||
|
||||
ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false);
|
||||
ui->plainTextEdit->setReadOnly(readOnly);
|
||||
}
|
||||
|
||||
LoadNewTypesDialog::~LoadNewTypesDialog() {}
|
||||
TypesInteractionDialog::~TypesInteractionDialog() {}
|
||||
|
||||
void LoadNewTypesDialog::on_selectFileButton_clicked()
|
||||
void TypesInteractionDialog::on_selectFileButton_clicked()
|
||||
{
|
||||
QString filename = QFileDialog::getOpenFileName(this, tr("Select file"), Config()->getRecentFolder(), "Header files (*.h *.hpp);;All files (*)");
|
||||
if (filename.isEmpty()) {
|
||||
@ -39,7 +39,7 @@ void LoadNewTypesDialog::on_selectFileButton_clicked()
|
||||
ui->plainTextEdit->setPlainText(file.readAll());
|
||||
}
|
||||
|
||||
void LoadNewTypesDialog::on_plainTextEdit_textChanged()
|
||||
void TypesInteractionDialog::on_plainTextEdit_textChanged()
|
||||
{
|
||||
if (ui->plainTextEdit->toPlainText().isEmpty()) {
|
||||
ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false);
|
||||
@ -48,7 +48,7 @@ void LoadNewTypesDialog::on_plainTextEdit_textChanged()
|
||||
}
|
||||
}
|
||||
|
||||
void LoadNewTypesDialog::done(int r)
|
||||
void TypesInteractionDialog::done(int r)
|
||||
{
|
||||
if (r == QDialog::Accepted) {
|
||||
QString error = Core()->addTypes(ui->plainTextEdit->toPlainText());
|
||||
@ -68,3 +68,8 @@ void LoadNewTypesDialog::done(int r)
|
||||
QDialog::done(r);
|
||||
}
|
||||
}
|
||||
|
||||
void TypesInteractionDialog::fillTextArea(QString content) {
|
||||
ui->layoutWidget->hide();
|
||||
ui->plainTextEdit->setPlainText(content);
|
||||
}
|
@ -1,21 +1,30 @@
|
||||
#ifndef LOADNEWTYPESDIALOG_H
|
||||
#define LOADNEWTYPESDIALOG_H
|
||||
#ifndef TYPESINTERACTIONDIALOG_H
|
||||
#define TYPESINTERACTIONDIALOG_H
|
||||
|
||||
#include <QDialog>
|
||||
#include <memory>
|
||||
|
||||
namespace Ui {
|
||||
class LoadNewTypesDialog;
|
||||
class TypesInteractionDialog;
|
||||
}
|
||||
class SyntaxHighlighter;
|
||||
|
||||
class LoadNewTypesDialog : public QDialog
|
||||
class TypesInteractionDialog : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit LoadNewTypesDialog(QWidget *parent = nullptr);
|
||||
~LoadNewTypesDialog();
|
||||
explicit TypesInteractionDialog(QWidget *parent = nullptr, bool readOnly = false);
|
||||
~TypesInteractionDialog();
|
||||
/**
|
||||
* @brief Fill the Dialog's TextEdit object with the content
|
||||
* passed to the function. The content will most likely be a C
|
||||
* representation of a Type.
|
||||
* @param content - The content which should be in the TextEdit object.
|
||||
* most likely will be a C representation of a Type.
|
||||
* @param readonly - Will be set as "true" for viewing mode
|
||||
*/
|
||||
void fillTextArea(QString content);
|
||||
|
||||
private slots:
|
||||
/**
|
||||
@ -38,7 +47,7 @@ private slots:
|
||||
void done(int r) override;
|
||||
|
||||
private:
|
||||
std::unique_ptr<Ui::LoadNewTypesDialog> ui;
|
||||
std::unique_ptr<Ui::TypesInteractionDialog> ui;
|
||||
SyntaxHighlighter *syntaxHighLighter;
|
||||
|
||||
signals:
|
||||
@ -48,4 +57,4 @@ signals:
|
||||
void newTypesLoaded();
|
||||
};
|
||||
|
||||
#endif // LOADNEWTYPESDIALOG_H
|
||||
#endif // TYPESINTERACTIONDIALOG_H
|
@ -1,7 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>LoadNewTypesDialog</class>
|
||||
<widget class="QDialog" name="LoadNewTypesDialog">
|
||||
<class>TypesInteractionDialog</class>
|
||||
<widget class="QDialog" name="TypesInteractionDialog">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
@ -15,29 +15,31 @@
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="filenameLabel">
|
||||
<property name="text">
|
||||
<string>Load From File:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLineEdit" name="filenameLineEdit">
|
||||
<property name="readOnly">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="selectFileButton">
|
||||
<property name="text">
|
||||
<string>Select File</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
<widget class="QWidget" name="layoutWidget" native="true">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="filenameLabel">
|
||||
<property name="text">
|
||||
<string>Load From File:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLineEdit" name="filenameLineEdit">
|
||||
<property name="readOnly">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="selectFileButton">
|
||||
<property name="text">
|
||||
<string>Select File</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPlainTextEdit" name="plainTextEdit">
|
||||
@ -66,7 +68,7 @@
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>accepted()</signal>
|
||||
<receiver>LoadNewTypesDialog</receiver>
|
||||
<receiver>TypesInteractionDialog</receiver>
|
||||
<slot>accept()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
@ -82,7 +84,7 @@
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>rejected()</signal>
|
||||
<receiver>LoadNewTypesDialog</receiver>
|
||||
<receiver>TypesInteractionDialog</receiver>
|
||||
<slot>reject()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
@ -2,7 +2,7 @@
|
||||
#include "ui_TypesWidget.h"
|
||||
#include "core/MainWindow.h"
|
||||
#include "common/Helpers.h"
|
||||
#include "dialogs/LoadNewTypesDialog.h"
|
||||
#include "dialogs/TypesInteractionDialog.h"
|
||||
#include "dialogs/LinkTypeDialog.h"
|
||||
|
||||
#include <QMenu>
|
||||
@ -60,7 +60,7 @@ QVariant TypesModel::headerData(int section, Qt::Orientation, int role) const
|
||||
case Qt::DisplayRole:
|
||||
switch (section) {
|
||||
case TYPE:
|
||||
return tr("Type");
|
||||
return tr("Type / Name");
|
||||
case SIZE:
|
||||
return tr("Size");
|
||||
case FORMAT:
|
||||
@ -181,6 +181,13 @@ TypesWidget::TypesWidget(MainWindow *main, QAction *action) :
|
||||
tree->showItemsNumber(types_proxy_model->rowCount());
|
||||
}
|
||||
);
|
||||
|
||||
actionViewType = new QAction(tr("View Type"), this);
|
||||
actionEditType = new QAction(tr("Edit Type"), this);
|
||||
|
||||
connect (actionViewType, &QAction::triggered, [this]() { viewType(true) ;});
|
||||
connect (actionEditType, &QAction::triggered, [this]() { viewType(false) ;});
|
||||
connect (ui->typesTreeView, &QTreeView::doubleClicked, this, &TypesWidget::typeItemDoubleClicked);
|
||||
}
|
||||
|
||||
TypesWidget::~TypesWidget() {}
|
||||
@ -229,9 +236,13 @@ void TypesWidget::showTypesContextMenu(const QPoint &pt)
|
||||
|
||||
if (index.isValid()) {
|
||||
TypeDescription t = index.data(TypesModel::TypeDescriptionRole).value<TypeDescription>();
|
||||
if (t.category == "Struct") {
|
||||
if (t.category != "Primitive") {
|
||||
// Add "Link To Address" option
|
||||
menu.addAction(ui->actionLink_Type_To_Address);
|
||||
menu.addAction(actionViewType);
|
||||
menu.addAction(actionEditType);
|
||||
if (t.category == "Struct") {
|
||||
menu.addAction(ui->actionLink_Type_To_Address);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -268,12 +279,34 @@ void TypesWidget::on_actionExport_Types_triggered()
|
||||
|
||||
void TypesWidget::on_actionLoad_New_Types_triggered()
|
||||
{
|
||||
LoadNewTypesDialog dialog(this);
|
||||
connect(&dialog, SIGNAL(newTypesLoaded()), this, SLOT(refreshTypes()));
|
||||
TypesInteractionDialog dialog(this);
|
||||
connect(&dialog, &TypesInteractionDialog::newTypesLoaded, this, &TypesWidget::refreshTypes);
|
||||
dialog.setWindowTitle(tr("Load New Types"));
|
||||
dialog.exec();
|
||||
}
|
||||
|
||||
void TypesWidget::viewType(bool readOnly)
|
||||
{
|
||||
|
||||
QModelIndex index = ui->typesTreeView->currentIndex();
|
||||
|
||||
if (!index.isValid()) {
|
||||
return;
|
||||
}
|
||||
|
||||
TypesInteractionDialog dialog(this, readOnly);
|
||||
TypeDescription t = index.data(TypesModel::TypeDescriptionRole).value<TypeDescription>();
|
||||
if (!readOnly) {
|
||||
dialog.setWindowTitle(tr("Edit Type: ") + t.type);
|
||||
connect(&dialog, &TypesInteractionDialog::newTypesLoaded, this, &TypesWidget::refreshTypes);
|
||||
} else {
|
||||
dialog.setWindowTitle(tr("View Type: ") + t.type + tr(" (Read Only)"));
|
||||
}
|
||||
dialog.fillTextArea(Core()->getTypeAsC(t.type, t.category));
|
||||
dialog.exec();
|
||||
}
|
||||
|
||||
|
||||
void TypesWidget::on_actionDelete_Type_triggered()
|
||||
{
|
||||
QModelIndex proxyIndex = ui->typesTreeView->currentIndex();
|
||||
@ -301,7 +334,21 @@ void TypesWidget::on_actionLink_Type_To_Address_triggered()
|
||||
TypeDescription t = index.data(TypesModel::TypeDescriptionRole).value<TypeDescription>();
|
||||
dialog.setDefaultType(t.type);
|
||||
dialog.setDefaultAddress(RAddressString(Core()->getOffset()));
|
||||
dialog.exec();
|
||||
}
|
||||
}
|
||||
|
||||
void TypesWidget::typeItemDoubleClicked(const QModelIndex &index) {
|
||||
if (!index.isValid()) {
|
||||
return;
|
||||
}
|
||||
|
||||
TypesInteractionDialog dialog(this, true);
|
||||
TypeDescription t = index.data(TypesModel::TypeDescriptionRole).value<TypeDescription>();
|
||||
if (t.category == "Primitive") {
|
||||
return;
|
||||
}
|
||||
dialog.fillTextArea(Core()->getTypeAsC(t.type, t.category));
|
||||
dialog.setWindowTitle(tr("View Type: ") + t.type + tr(" (Read Only)"));
|
||||
dialog.exec();
|
||||
}
|
||||
|
@ -93,11 +93,19 @@ private slots:
|
||||
|
||||
/**
|
||||
* @brief Executed on clicking the Load New types option in the context menu
|
||||
* It will open the LoadNewTypesDialog where the user can either enter the
|
||||
* It will open the TypesInteractionDialog where the user can either enter the
|
||||
* types manually, or can select a file from where the types will be loaded
|
||||
*/
|
||||
void on_actionLoad_New_Types_triggered();
|
||||
|
||||
/**
|
||||
* @brief Executed on clicking either the Edit Type or View Type options in the context menu
|
||||
* It will open the TypesInteractionDialog filled with the selected type. Depends on Edit or View mode
|
||||
* the text view would be read-only or not.
|
||||
*/
|
||||
void viewType(bool readOnly=true);
|
||||
|
||||
|
||||
/**
|
||||
* @brief Executed on clicking the Delete Type option in the context menu
|
||||
* Upon confirmation from the user, it will delete the selected type.
|
||||
@ -110,6 +118,12 @@ private slots:
|
||||
*/
|
||||
void on_actionLink_Type_To_Address_triggered();
|
||||
|
||||
/**
|
||||
* @brief triggers when the user double-clicks an item. This will open
|
||||
* a dialog that shows the Type's content
|
||||
*/
|
||||
void typeItemDoubleClicked(const QModelIndex &index);
|
||||
|
||||
private:
|
||||
std::unique_ptr<Ui::TypesWidget> ui;
|
||||
|
||||
@ -117,6 +131,8 @@ private:
|
||||
TypesSortFilterProxyModel *types_proxy_model;
|
||||
QList<TypeDescription> types;
|
||||
CutterTreeWidget *tree;
|
||||
QAction *actionViewType;
|
||||
QAction *actionEditType;
|
||||
|
||||
void setScrollMode();
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user