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:
Itay Cohen 2019-06-13 09:22:20 +03:00 committed by GitHub
parent 228d3a454a
commit 5fb2c8ac6f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 163 additions and 54 deletions

View File

@ -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 \

View File

@ -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

View File

@ -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

View File

@ -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);
}

View File

@ -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

View File

@ -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">

View File

@ -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();
}

View File

@ -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();