From 5fb2c8ac6f285fc945cba25eb87eb60bb8704f94 Mon Sep 17 00:00:00 2001 From: Itay Cohen Date: Thu, 13 Jun 2019 09:22:20 +0300 Subject: [PATCH] Implement Editing and Viewing of Types in the Types Widget (#1597) * Implemet Edit and View for types * Rename LoadNewTypes to TypesInteraction --- src/Cutter.pro | 6 +- src/core/Cutter.cpp | 21 +++++++ src/core/Cutter.h | 9 +++ ...sDialog.cpp => TypesInteractionDialog.cpp} | 23 +++++--- ...TypesDialog.h => TypesInteractionDialog.h} | 25 +++++--- ...pesDialog.ui => TypesInteractionDialog.ui} | 56 +++++++++--------- src/widgets/TypesWidget.cpp | 59 +++++++++++++++++-- src/widgets/TypesWidget.h | 18 +++++- 8 files changed, 163 insertions(+), 54 deletions(-) rename src/dialogs/{LoadNewTypesDialog.cpp => TypesInteractionDialog.cpp} (74%) rename src/dialogs/{LoadNewTypesDialog.h => TypesInteractionDialog.h} (53%) rename src/dialogs/{LoadNewTypesDialog.ui => TypesInteractionDialog.ui} (63%) diff --git a/src/Cutter.pro b/src/Cutter.pro index 6cd7d0b3..3235459a 100644 --- a/src/Cutter.pro +++ b/src/Cutter.pro @@ -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 \ diff --git a/src/core/Cutter.cpp b/src/core/Cutter.cpp index db013ba5..20ca13f5 100644 --- a/src/core/Cutter.cpp +++ b/src/core/Cutter.cpp @@ -2349,6 +2349,7 @@ QList 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 diff --git a/src/core/Cutter.h b/src/core/Cutter.h index 522fb898..6380df0d 100644 --- a/src/core/Cutter.h +++ b/src/core/Cutter.h @@ -335,6 +335,15 @@ public: */ QList 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 diff --git a/src/dialogs/LoadNewTypesDialog.cpp b/src/dialogs/TypesInteractionDialog.cpp similarity index 74% rename from src/dialogs/LoadNewTypesDialog.cpp rename to src/dialogs/TypesInteractionDialog.cpp index 8f618998..4ae27de5 100644 --- a/src/dialogs/LoadNewTypesDialog.cpp +++ b/src/dialogs/TypesInteractionDialog.cpp @@ -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 #include -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); +} \ No newline at end of file diff --git a/src/dialogs/LoadNewTypesDialog.h b/src/dialogs/TypesInteractionDialog.h similarity index 53% rename from src/dialogs/LoadNewTypesDialog.h rename to src/dialogs/TypesInteractionDialog.h index 32b36141..08b1910a 100644 --- a/src/dialogs/LoadNewTypesDialog.h +++ b/src/dialogs/TypesInteractionDialog.h @@ -1,21 +1,30 @@ -#ifndef LOADNEWTYPESDIALOG_H -#define LOADNEWTYPESDIALOG_H +#ifndef TYPESINTERACTIONDIALOG_H +#define TYPESINTERACTIONDIALOG_H #include #include 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; + std::unique_ptr ui; SyntaxHighlighter *syntaxHighLighter; signals: @@ -48,4 +57,4 @@ signals: void newTypesLoaded(); }; -#endif // LOADNEWTYPESDIALOG_H +#endif // TYPESINTERACTIONDIALOG_H diff --git a/src/dialogs/LoadNewTypesDialog.ui b/src/dialogs/TypesInteractionDialog.ui similarity index 63% rename from src/dialogs/LoadNewTypesDialog.ui rename to src/dialogs/TypesInteractionDialog.ui index dab4e10c..2dc8af6d 100644 --- a/src/dialogs/LoadNewTypesDialog.ui +++ b/src/dialogs/TypesInteractionDialog.ui @@ -1,7 +1,7 @@ - LoadNewTypesDialog - + TypesInteractionDialog + 0 @@ -15,29 +15,31 @@ - - - - - Load From File: - - - - - - - true - - - - - - - Select File - - - - + + + + + + Load From File: + + + + + + + true + + + + + + + Select File + + + + + @@ -66,7 +68,7 @@ buttonBox accepted() - LoadNewTypesDialog + TypesInteractionDialog accept() @@ -82,7 +84,7 @@ buttonBox rejected() - LoadNewTypesDialog + TypesInteractionDialog reject() diff --git a/src/widgets/TypesWidget.cpp b/src/widgets/TypesWidget.cpp index 97a7fd34..11f2c7ad 100644 --- a/src/widgets/TypesWidget.cpp +++ b/src/widgets/TypesWidget.cpp @@ -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 @@ -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(); - 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(); + 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(); 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(); + 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(); } diff --git a/src/widgets/TypesWidget.h b/src/widgets/TypesWidget.h index ee4729b3..ef02f965 100644 --- a/src/widgets/TypesWidget.h +++ b/src/widgets/TypesWidget.h @@ -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; @@ -117,6 +131,8 @@ private: TypesSortFilterProxyModel *types_proxy_model; QList types; CutterTreeWidget *tree; + QAction *actionViewType; + QAction *actionEditType; void setScrollMode();