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 \ dialogs/WelcomeDialog.cpp \
common/RunScriptTask.cpp \ common/RunScriptTask.cpp \
dialogs/EditMethodDialog.cpp \ dialogs/EditMethodDialog.cpp \
dialogs/LoadNewTypesDialog.cpp \ dialogs/TypesInteractionDialog.cpp \
widgets/SdbWidget.cpp \ widgets/SdbWidget.cpp \
common/PythonManager.cpp \ common/PythonManager.cpp \
plugins/PluginManager.cpp \ plugins/PluginManager.cpp \
@ -457,7 +457,7 @@ HEADERS += \
common/Json.h \ common/Json.h \
dialogs/EditMethodDialog.h \ dialogs/EditMethodDialog.h \
common/CrashHandler.h \ common/CrashHandler.h \
dialogs/LoadNewTypesDialog.h \ dialogs/TypesInteractionDialog.h \
widgets/SdbWidget.h \ widgets/SdbWidget.h \
common/PythonManager.h \ common/PythonManager.h \
plugins/PluginManager.h \ plugins/PluginManager.h \
@ -532,7 +532,7 @@ FORMS += \
dialogs/HexdumpRangeDialog.ui \ dialogs/HexdumpRangeDialog.ui \
dialogs/WelcomeDialog.ui \ dialogs/WelcomeDialog.ui \
dialogs/EditMethodDialog.ui \ dialogs/EditMethodDialog.ui \
dialogs/LoadNewTypesDialog.ui \ dialogs/TypesInteractionDialog.ui \
widgets/SdbWidget.ui \ widgets/SdbWidget.ui \
dialogs/LinkTypeDialog.ui \ dialogs/LinkTypeDialog.ui \
widgets/ColorPicker.ui \ widgets/ColorPicker.ui \

View File

@ -2349,6 +2349,7 @@ QList<TypeDescription> CutterCore::getAllTypedefs()
QString CutterCore::addTypes(const char *str) QString CutterCore::addTypes(const char *str)
{ {
CORE_LOCK();
char *error_msg = nullptr; char *error_msg = nullptr;
char *parsed = r_parse_c_string(core_->anal, str, &error_msg); char *parsed = r_parse_c_string(core_->anal, str, &error_msg);
QString error; QString error;
@ -2372,6 +2373,26 @@ QString CutterCore::addTypes(const char *str)
return error; 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) bool CutterCore::isAddressMapped(RVA addr)
{ {
// If value returned by "om. @ addr" is empty means that address is not mapped // If value returned by "om. @ addr" is empty means that address is not mapped

View File

@ -335,6 +335,15 @@ public:
*/ */
QList<TypeDescription> getAllTypedefs(); 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 * @brief Adds new types
* It first uses the r_parse_c_string() function from radare2 API to parse the * 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 "dialogs/TypesInteractionDialog.h"
#include "ui_LoadNewTypesDialog.h" #include "ui_TypesInteractionDialog.h"
#include "core/Cutter.h" #include "core/Cutter.h"
#include "common/Configuration.h" #include "common/Configuration.h"
@ -9,20 +9,20 @@
#include <QFileDialog> #include <QFileDialog>
#include <QTemporaryFile> #include <QTemporaryFile>
LoadNewTypesDialog::LoadNewTypesDialog(QWidget *parent) : TypesInteractionDialog::TypesInteractionDialog(QWidget *parent, bool readOnly) :
QDialog(parent), QDialog(parent),
ui(new Ui::LoadNewTypesDialog) ui(new Ui::TypesInteractionDialog)
{ {
ui->setupUi(this); ui->setupUi(this);
ui->plainTextEdit->setPlainText(""); ui->plainTextEdit->setPlainText("");
syntaxHighLighter = new SyntaxHighlighter(ui->plainTextEdit->document()); syntaxHighLighter = new SyntaxHighlighter(ui->plainTextEdit->document());
ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false); 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 (*)"); QString filename = QFileDialog::getOpenFileName(this, tr("Select file"), Config()->getRecentFolder(), "Header files (*.h *.hpp);;All files (*)");
if (filename.isEmpty()) { if (filename.isEmpty()) {
@ -39,7 +39,7 @@ void LoadNewTypesDialog::on_selectFileButton_clicked()
ui->plainTextEdit->setPlainText(file.readAll()); ui->plainTextEdit->setPlainText(file.readAll());
} }
void LoadNewTypesDialog::on_plainTextEdit_textChanged() void TypesInteractionDialog::on_plainTextEdit_textChanged()
{ {
if (ui->plainTextEdit->toPlainText().isEmpty()) { if (ui->plainTextEdit->toPlainText().isEmpty()) {
ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false); 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) { if (r == QDialog::Accepted) {
QString error = Core()->addTypes(ui->plainTextEdit->toPlainText()); QString error = Core()->addTypes(ui->plainTextEdit->toPlainText());
@ -68,3 +68,8 @@ void LoadNewTypesDialog::done(int r)
QDialog::done(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 #ifndef TYPESINTERACTIONDIALOG_H
#define LOADNEWTYPESDIALOG_H #define TYPESINTERACTIONDIALOG_H
#include <QDialog> #include <QDialog>
#include <memory> #include <memory>
namespace Ui { namespace Ui {
class LoadNewTypesDialog; class TypesInteractionDialog;
} }
class SyntaxHighlighter; class SyntaxHighlighter;
class LoadNewTypesDialog : public QDialog class TypesInteractionDialog : public QDialog
{ {
Q_OBJECT Q_OBJECT
public: public:
explicit LoadNewTypesDialog(QWidget *parent = nullptr); explicit TypesInteractionDialog(QWidget *parent = nullptr, bool readOnly = false);
~LoadNewTypesDialog(); ~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: private slots:
/** /**
@ -38,7 +47,7 @@ private slots:
void done(int r) override; void done(int r) override;
private: private:
std::unique_ptr<Ui::LoadNewTypesDialog> ui; std::unique_ptr<Ui::TypesInteractionDialog> ui;
SyntaxHighlighter *syntaxHighLighter; SyntaxHighlighter *syntaxHighLighter;
signals: signals:
@ -48,4 +57,4 @@ signals:
void newTypesLoaded(); void newTypesLoaded();
}; };
#endif // LOADNEWTYPESDIALOG_H #endif // TYPESINTERACTIONDIALOG_H

View File

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0"> <ui version="4.0">
<class>LoadNewTypesDialog</class> <class>TypesInteractionDialog</class>
<widget class="QDialog" name="LoadNewTypesDialog"> <widget class="QDialog" name="TypesInteractionDialog">
<property name="geometry"> <property name="geometry">
<rect> <rect>
<x>0</x> <x>0</x>
@ -15,29 +15,31 @@
</property> </property>
<layout class="QVBoxLayout" name="verticalLayout"> <layout class="QVBoxLayout" name="verticalLayout">
<item> <item>
<layout class="QHBoxLayout" name="horizontalLayout"> <widget class="QWidget" name="layoutWidget" native="true">
<item> <layout class="QHBoxLayout" name="horizontalLayout">
<widget class="QLabel" name="filenameLabel"> <item>
<property name="text"> <widget class="QLabel" name="filenameLabel">
<string>Load From File:</string> <property name="text">
</property> <string>Load From File:</string>
</widget> </property>
</item> </widget>
<item> </item>
<widget class="QLineEdit" name="filenameLineEdit"> <item>
<property name="readOnly"> <widget class="QLineEdit" name="filenameLineEdit">
<bool>true</bool> <property name="readOnly">
</property> <bool>true</bool>
</widget> </property>
</item> </widget>
<item> </item>
<widget class="QPushButton" name="selectFileButton"> <item>
<property name="text"> <widget class="QPushButton" name="selectFileButton">
<string>Select File</string> <property name="text">
</property> <string>Select File</string>
</widget> </property>
</item> </widget>
</layout> </item>
</layout>
</widget>
</item> </item>
<item> <item>
<widget class="QPlainTextEdit" name="plainTextEdit"> <widget class="QPlainTextEdit" name="plainTextEdit">
@ -66,7 +68,7 @@
<connection> <connection>
<sender>buttonBox</sender> <sender>buttonBox</sender>
<signal>accepted()</signal> <signal>accepted()</signal>
<receiver>LoadNewTypesDialog</receiver> <receiver>TypesInteractionDialog</receiver>
<slot>accept()</slot> <slot>accept()</slot>
<hints> <hints>
<hint type="sourcelabel"> <hint type="sourcelabel">
@ -82,7 +84,7 @@
<connection> <connection>
<sender>buttonBox</sender> <sender>buttonBox</sender>
<signal>rejected()</signal> <signal>rejected()</signal>
<receiver>LoadNewTypesDialog</receiver> <receiver>TypesInteractionDialog</receiver>
<slot>reject()</slot> <slot>reject()</slot>
<hints> <hints>
<hint type="sourcelabel"> <hint type="sourcelabel">

View File

@ -2,7 +2,7 @@
#include "ui_TypesWidget.h" #include "ui_TypesWidget.h"
#include "core/MainWindow.h" #include "core/MainWindow.h"
#include "common/Helpers.h" #include "common/Helpers.h"
#include "dialogs/LoadNewTypesDialog.h" #include "dialogs/TypesInteractionDialog.h"
#include "dialogs/LinkTypeDialog.h" #include "dialogs/LinkTypeDialog.h"
#include <QMenu> #include <QMenu>
@ -60,7 +60,7 @@ QVariant TypesModel::headerData(int section, Qt::Orientation, int role) const
case Qt::DisplayRole: case Qt::DisplayRole:
switch (section) { switch (section) {
case TYPE: case TYPE:
return tr("Type"); return tr("Type / Name");
case SIZE: case SIZE:
return tr("Size"); return tr("Size");
case FORMAT: case FORMAT:
@ -181,6 +181,13 @@ TypesWidget::TypesWidget(MainWindow *main, QAction *action) :
tree->showItemsNumber(types_proxy_model->rowCount()); 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() {} TypesWidget::~TypesWidget() {}
@ -229,9 +236,13 @@ void TypesWidget::showTypesContextMenu(const QPoint &pt)
if (index.isValid()) { if (index.isValid()) {
TypeDescription t = index.data(TypesModel::TypeDescriptionRole).value<TypeDescription>(); TypeDescription t = index.data(TypesModel::TypeDescriptionRole).value<TypeDescription>();
if (t.category == "Struct") { if (t.category != "Primitive") {
// Add "Link To Address" option // 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() void TypesWidget::on_actionLoad_New_Types_triggered()
{ {
LoadNewTypesDialog dialog(this); TypesInteractionDialog dialog(this);
connect(&dialog, SIGNAL(newTypesLoaded()), this, SLOT(refreshTypes())); connect(&dialog, &TypesInteractionDialog::newTypesLoaded, this, &TypesWidget::refreshTypes);
dialog.setWindowTitle(tr("Load New Types")); dialog.setWindowTitle(tr("Load New Types"));
dialog.exec(); 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() void TypesWidget::on_actionDelete_Type_triggered()
{ {
QModelIndex proxyIndex = ui->typesTreeView->currentIndex(); 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>(); TypeDescription t = index.data(TypesModel::TypeDescriptionRole).value<TypeDescription>();
dialog.setDefaultType(t.type); dialog.setDefaultType(t.type);
dialog.setDefaultAddress(RAddressString(Core()->getOffset())); 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(); dialog.exec();
} }

View File

@ -93,11 +93,19 @@ private slots:
/** /**
* @brief Executed on clicking the Load New types option in the context menu * @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 * types manually, or can select a file from where the types will be loaded
*/ */
void on_actionLoad_New_Types_triggered(); 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 * @brief Executed on clicking the Delete Type option in the context menu
* Upon confirmation from the user, it will delete the selected type. * Upon confirmation from the user, it will delete the selected type.
@ -110,6 +118,12 @@ private slots:
*/ */
void on_actionLink_Type_To_Address_triggered(); 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: private:
std::unique_ptr<Ui::TypesWidget> ui; std::unique_ptr<Ui::TypesWidget> ui;
@ -117,6 +131,8 @@ private:
TypesSortFilterProxyModel *types_proxy_model; TypesSortFilterProxyModel *types_proxy_model;
QList<TypeDescription> types; QList<TypeDescription> types;
CutterTreeWidget *tree; CutterTreeWidget *tree;
QAction *actionViewType;
QAction *actionEditType;
void setScrollMode(); void setScrollMode();