mirror of
https://github.com/rizinorg/cutter.git
synced 2025-01-19 02:48:49 +00:00
Added load, delete and export features in the Types Widget (#1158)
* Added export types feature in types widget * Added load new types option * Added delete types option * Removed redundant files * Fix for translation * Added popups in TypesWidget and related dialogs * Added addTypes() in CutterCore and changed code to use it. * Update r2 submodule for r_parse_c_string() * Added override keyword for overridden functions * Added comments and removed popup question. * Added comments and confirmation dialog for delete
This commit is contained in:
parent
75ce42f1ef
commit
c9ce15f6dd
2
radare2
2
radare2
@ -1 +1 @@
|
||||
Subproject commit b4178702c88ed361fcb98e1b87cd74f0af4b2f44
|
||||
Subproject commit e1f23fab43c665b29029cd65af208c1d5eff13b5
|
@ -2208,7 +2208,7 @@ QList<TypeDescription> CutterCore::getAllUnions()
|
||||
TypeDescription exp;
|
||||
exp.type = value.toString();
|
||||
exp.size = 0;
|
||||
exp.category = tr("Union");
|
||||
exp.category = "Union";
|
||||
ret << exp;
|
||||
}
|
||||
|
||||
@ -2225,7 +2225,7 @@ QList<TypeDescription> CutterCore::getAllStructs()
|
||||
TypeDescription exp;
|
||||
exp.type = value.toString();
|
||||
exp.size = 0;
|
||||
exp.category = tr("Struct");
|
||||
exp.category = "Struct";
|
||||
ret << exp;
|
||||
}
|
||||
|
||||
@ -2242,7 +2242,7 @@ QList<TypeDescription> CutterCore::getAllEnums()
|
||||
TypeDescription exp;
|
||||
exp.type = key;
|
||||
exp.size = 0;
|
||||
exp.category = tr("Enum");
|
||||
exp.category = "Enum";
|
||||
ret << exp;
|
||||
}
|
||||
|
||||
@ -2259,13 +2259,26 @@ QList<TypeDescription> CutterCore::getAllTypedefs()
|
||||
TypeDescription exp;
|
||||
exp.type = key;
|
||||
exp.size = 0;
|
||||
exp.category = tr("Typedef");
|
||||
exp.category = "Typedef";
|
||||
ret << exp;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
QString CutterCore::addTypes(const char *str)
|
||||
{
|
||||
char *error_msg = nullptr;
|
||||
char *parsed = r_parse_c_string(core_->anal, str, &error_msg);
|
||||
|
||||
if (!parsed && error_msg) {
|
||||
return QString(error_msg);
|
||||
}
|
||||
|
||||
sdb_query_lines(core_->anal->sdb_types, parsed);
|
||||
return QString();
|
||||
}
|
||||
|
||||
QList<SearchDescription> CutterCore::getAllSearch(QString search_for, QString space)
|
||||
{
|
||||
CORE_LOCK();
|
||||
|
12
src/Cutter.h
12
src/Cutter.h
@ -685,6 +685,18 @@ public:
|
||||
*/
|
||||
QList<TypeDescription> getAllTypedefs();
|
||||
|
||||
/*!
|
||||
* \brief Adds new types
|
||||
* It first uses the r_parse_c_string() function from radare2 API to parse the
|
||||
* supplied C file (in the form of a string). If there were errors, they are displayed.
|
||||
* If there were no errors, it uses sdb_query_lines() function from radare2 API
|
||||
* to save the parsed types returned by r_parse_c_string()
|
||||
* \param str Contains the definition of the data types
|
||||
* \return returns an empty QString if there was no error, else returns the error
|
||||
*/
|
||||
QString addTypes(const char *str);
|
||||
QString addTypes(const QString &str) { return addTypes(str.toUtf8().constData()); }
|
||||
|
||||
QList<MemoryMapDescription> getMemoryMap();
|
||||
QList<SearchDescription> getAllSearch(QString search_for, QString space);
|
||||
BlockStatistics getBlockStatistics(unsigned int blocksCount);
|
||||
|
@ -227,7 +227,8 @@ SOURCES += \
|
||||
common/RefreshDeferrer.cpp \
|
||||
dialogs/WelcomeDialog.cpp \
|
||||
RunScriptTask.cpp \
|
||||
dialogs/EditMethodDialog.cpp
|
||||
dialogs/EditMethodDialog.cpp \
|
||||
dialogs/LoadNewTypesDialog.cpp
|
||||
|
||||
HEADERS += \
|
||||
Cutter.h \
|
||||
@ -337,7 +338,8 @@ HEADERS += \
|
||||
dialogs/WelcomeDialog.h \
|
||||
RunScriptTask.h \
|
||||
common/Json.h \
|
||||
dialogs/EditMethodDialog.h
|
||||
dialogs/EditMethodDialog.h \
|
||||
dialogs/LoadNewTypesDialog.h
|
||||
|
||||
FORMS += \
|
||||
dialogs/AboutDialog.ui \
|
||||
@ -397,7 +399,8 @@ FORMS += \
|
||||
widgets/ComboQuickFilterView.ui \
|
||||
dialogs/HexdumpRangeDialog.ui \
|
||||
dialogs/WelcomeDialog.ui \
|
||||
dialogs/EditMethodDialog.ui
|
||||
dialogs/EditMethodDialog.ui \
|
||||
dialogs/LoadNewTypesDialog.ui
|
||||
|
||||
RESOURCES += \
|
||||
resources.qrc \
|
||||
|
71
src/dialogs/LoadNewTypesDialog.cpp
Normal file
71
src/dialogs/LoadNewTypesDialog.cpp
Normal file
@ -0,0 +1,71 @@
|
||||
#include "dialogs/LoadNewTypesDialog.h"
|
||||
#include "ui_LoadNewTypesDialog.h"
|
||||
|
||||
#include "Cutter.h"
|
||||
#include "common/Configuration.h"
|
||||
#include "widgets/TypesWidget.h"
|
||||
|
||||
#include <QFileDialog>
|
||||
#include <QTemporaryFile>
|
||||
|
||||
LoadNewTypesDialog::LoadNewTypesDialog(QWidget *parent) :
|
||||
QDialog(parent),
|
||||
ui(new Ui::LoadNewTypesDialog)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
ui->plainTextEdit->setPlainText("");
|
||||
ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false);
|
||||
}
|
||||
|
||||
LoadNewTypesDialog::~LoadNewTypesDialog() {}
|
||||
|
||||
void LoadNewTypesDialog::on_selectFileButton_clicked()
|
||||
{
|
||||
QString filename = QFileDialog::getOpenFileName(this, tr("Select file"), Config()->getRecentFolder(), "Header files (*.h *.hpp);;All files (*)");
|
||||
if (filename.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
Config()->setRecentFolder(QFileInfo(filename).absolutePath());
|
||||
QFile file(filename);
|
||||
if (!file.open(QIODevice::ReadOnly)) {
|
||||
QMessageBox popup(this);
|
||||
popup.setWindowTitle(tr("Error"));
|
||||
popup.setText(file.errorString());
|
||||
popup.setStandardButtons(QMessageBox::Ok);
|
||||
popup.exec();
|
||||
on_selectFileButton_clicked();
|
||||
return;
|
||||
}
|
||||
ui->filenameLineEdit->setText(filename);
|
||||
ui->plainTextEdit->setPlainText(file.readAll());
|
||||
}
|
||||
|
||||
void LoadNewTypesDialog::on_plainTextEdit_textChanged()
|
||||
{
|
||||
if (ui->plainTextEdit->toPlainText().isEmpty()) {
|
||||
ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false);
|
||||
} else {
|
||||
ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(true);
|
||||
}
|
||||
}
|
||||
|
||||
void LoadNewTypesDialog::done(int r)
|
||||
{
|
||||
if (r == QDialog::Accepted) {
|
||||
QString error = Core()->addTypes(ui->plainTextEdit->toPlainText());
|
||||
if (error.isEmpty()) {
|
||||
emit newTypesLoaded();
|
||||
QDialog::done(r);
|
||||
return;
|
||||
}
|
||||
|
||||
QMessageBox popup(this);
|
||||
popup.setWindowTitle(tr("Error"));
|
||||
popup.setText(tr("There was some error while loading new types"));
|
||||
popup.setDetailedText(error);
|
||||
popup.setStandardButtons(QMessageBox::Ok);
|
||||
popup.exec();
|
||||
} else {
|
||||
QDialog::done(r);
|
||||
}
|
||||
}
|
49
src/dialogs/LoadNewTypesDialog.h
Normal file
49
src/dialogs/LoadNewTypesDialog.h
Normal file
@ -0,0 +1,49 @@
|
||||
#ifndef LOADNEWTYPESDIALOG_H
|
||||
#define LOADNEWTYPESDIALOG_H
|
||||
|
||||
#include <QDialog>
|
||||
#include <memory>
|
||||
|
||||
namespace Ui {
|
||||
class LoadNewTypesDialog;
|
||||
}
|
||||
|
||||
class LoadNewTypesDialog : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit LoadNewTypesDialog(QWidget *parent = nullptr);
|
||||
~LoadNewTypesDialog();
|
||||
|
||||
private slots:
|
||||
/*!
|
||||
* \brief Executed when the user clicks the selectFileButton
|
||||
* Opens a File Dialog from where the user can select a file from where
|
||||
* the types will be loaded.
|
||||
*/
|
||||
void on_selectFileButton_clicked();
|
||||
|
||||
/*!
|
||||
* \brief Executed whenever the text inside the textbox changes
|
||||
* When the text box is empty, the OK button is disabled.
|
||||
*/
|
||||
void on_plainTextEdit_textChanged();
|
||||
|
||||
/*!
|
||||
* \brief done Closes the dialog and sets its result code to r
|
||||
* \param r The value which will be returned by exec()
|
||||
*/
|
||||
void done(int r) override;
|
||||
|
||||
private:
|
||||
std::unique_ptr<Ui::LoadNewTypesDialog> ui;
|
||||
|
||||
signals:
|
||||
/*!
|
||||
* \brief Emitted when new types are loaded
|
||||
*/
|
||||
void newTypesLoaded();
|
||||
};
|
||||
|
||||
#endif // LOADNEWTYPESDIALOG_H
|
99
src/dialogs/LoadNewTypesDialog.ui
Normal file
99
src/dialogs/LoadNewTypesDialog.ui
Normal file
@ -0,0 +1,99 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>LoadNewTypesDialog</class>
|
||||
<widget class="QDialog" name="LoadNewTypesDialog">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>400</width>
|
||||
<height>300</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Dialog</string>
|
||||
</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>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPlainTextEdit" name="plainTextEdit">
|
||||
<property name="plainText">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="placeholderText">
|
||||
<string>Enter Types Manually</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QDialogButtonBox" name="buttonBox">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="standardButtons">
|
||||
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections>
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>accepted()</signal>
|
||||
<receiver>LoadNewTypesDialog</receiver>
|
||||
<slot>accept()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>248</x>
|
||||
<y>254</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>157</x>
|
||||
<y>274</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>rejected()</signal>
|
||||
<receiver>LoadNewTypesDialog</receiver>
|
||||
<slot>reject()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>316</x>
|
||||
<y>260</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>286</x>
|
||||
<y>274</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
</connections>
|
||||
</ui>
|
@ -3,6 +3,11 @@
|
||||
#include "MainWindow.h"
|
||||
#include "common/Helpers.h"
|
||||
|
||||
#include "dialogs/LoadNewTypesDialog.h"
|
||||
|
||||
#include <QMenu>
|
||||
#include <QFileDialog>
|
||||
|
||||
TypesModel::TypesModel(QList<TypeDescription> *types, QObject *parent)
|
||||
: QAbstractListModel(parent),
|
||||
types(types)
|
||||
@ -68,6 +73,17 @@ QVariant TypesModel::headerData(int section, Qt::Orientation, int role) const
|
||||
}
|
||||
}
|
||||
|
||||
bool TypesModel::removeRows(int row, int count, const QModelIndex &parent)
|
||||
{
|
||||
Core()->cmdRaw("t-" + types->at(row).type);
|
||||
beginRemoveRows(parent, row, row + count - 1);
|
||||
while (count--) {
|
||||
types->removeAt(row);
|
||||
}
|
||||
endRemoveRows();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
TypesSortFilterProxyModel::TypesSortFilterProxyModel(TypesModel *source_model, QObject *parent)
|
||||
: QSortFilterProxyModel(parent)
|
||||
@ -120,6 +136,10 @@ TypesWidget::TypesWidget(MainWindow *main, QAction *action) :
|
||||
// Add status bar which displays the count
|
||||
tree->addStatusBar(ui->verticalLayout);
|
||||
|
||||
// Set single select mode
|
||||
ui->typesTreeView->setSelectionMode(QAbstractItemView::SingleSelection);
|
||||
|
||||
// Setup up the model and the proxy model
|
||||
types_model = new TypesModel(&types, this);
|
||||
types_proxy_model = new TypesSortFilterProxyModel(types_model, this);
|
||||
ui->typesTreeView->setModel(types_proxy_model);
|
||||
@ -127,6 +147,13 @@ TypesWidget::TypesWidget(MainWindow *main, QAction *action) :
|
||||
|
||||
setScrollMode();
|
||||
|
||||
// Setup custom context menu
|
||||
connect(ui->typesTreeView, SIGNAL(customContextMenuRequested(const QPoint &)),
|
||||
this, SLOT(showTypesContextMenu(const QPoint &)));
|
||||
|
||||
ui->typesTreeView->setContextMenuPolicy(Qt::CustomContextMenu);
|
||||
|
||||
|
||||
connect(ui->quickFilterView, SIGNAL(filterTextChanged(const QString &)), types_proxy_model,
|
||||
SLOT(setFilterWildcard(const QString &)));
|
||||
|
||||
@ -169,7 +196,7 @@ void TypesWidget::refreshTypes()
|
||||
categories.removeDuplicates();
|
||||
refreshCategoryCombo(categories);
|
||||
|
||||
qhelpers::adjustColumns(ui->typesTreeView, 3, 0);
|
||||
qhelpers::adjustColumns(ui->typesTreeView, 4, 0);
|
||||
}
|
||||
|
||||
void TypesWidget::refreshCategoryCombo(const QStringList &categories)
|
||||
@ -190,3 +217,67 @@ void TypesWidget::setScrollMode()
|
||||
{
|
||||
qhelpers::setVerticalScrollMode(ui->typesTreeView);
|
||||
}
|
||||
|
||||
void TypesWidget::showTypesContextMenu(const QPoint &pt)
|
||||
{
|
||||
QMenu menu(ui->typesTreeView);
|
||||
menu.addAction(ui->actionLoad_New_Types);
|
||||
menu.addAction(ui->actionExport_Types);
|
||||
|
||||
QModelIndex index = ui->typesTreeView->indexAt(pt);
|
||||
if (index.isValid()) {
|
||||
TypeDescription t = index.data(TypesModel::TypeDescriptionRole).value<TypeDescription>();
|
||||
if (t.category != "Typedef") {
|
||||
menu.addSeparator();
|
||||
menu.addAction(ui->actionDelete_Type);
|
||||
}
|
||||
}
|
||||
|
||||
menu.exec(ui->typesTreeView->mapToGlobal(pt));
|
||||
}
|
||||
|
||||
void TypesWidget::on_actionExport_Types_triggered()
|
||||
{
|
||||
QString filename = QFileDialog::getSaveFileName(this, tr("Save File"), Config()->getRecentFolder());
|
||||
if (filename.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
Config()->setRecentFolder(QFileInfo(filename).absolutePath());
|
||||
QFile file(filename);
|
||||
if (!file.open(QIODevice::WriteOnly)) {
|
||||
QMessageBox popup(this);
|
||||
popup.setWindowTitle(tr("Error"));
|
||||
popup.setText(file.errorString());
|
||||
popup.setStandardButtons(QMessageBox::Ok);
|
||||
popup.exec();
|
||||
on_actionExport_Types_triggered();
|
||||
return;
|
||||
}
|
||||
QTextStream fileOut(&file);
|
||||
fileOut << Core()->cmd("tc");
|
||||
file.close();
|
||||
}
|
||||
|
||||
void TypesWidget::on_actionLoad_New_Types_triggered()
|
||||
{
|
||||
LoadNewTypesDialog *dialog = new LoadNewTypesDialog(this);
|
||||
connect(dialog, SIGNAL(newTypesLoaded()), this, SLOT(refreshTypes()));
|
||||
dialog->setWindowTitle(tr("Load New Types"));
|
||||
dialog->exec();
|
||||
}
|
||||
|
||||
void TypesWidget::on_actionDelete_Type_triggered()
|
||||
{
|
||||
QModelIndex proxyIndex = ui->typesTreeView->currentIndex();
|
||||
QModelIndex index = types_proxy_model->mapToSource(proxyIndex);
|
||||
|
||||
TypeDescription exp = index.data(TypesModel::TypeDescriptionRole).value<TypeDescription>();
|
||||
QMessageBox popup(this);
|
||||
popup.setIcon(QMessageBox::Question);
|
||||
popup.setText(tr("Are you sure you want to delete \"%1\"?").arg(exp.type));
|
||||
popup.setStandardButtons(QMessageBox::No | QMessageBox::Yes);
|
||||
popup.setDefaultButton(QMessageBox::Yes);
|
||||
if (popup.exec() == QMessageBox::Yes) {
|
||||
types_model->removeRow(index.row());
|
||||
}
|
||||
}
|
||||
|
@ -38,11 +38,13 @@ public:
|
||||
|
||||
TypesModel(QList<TypeDescription> *types, QObject *parent = nullptr);
|
||||
|
||||
int rowCount(const QModelIndex &parent = QModelIndex()) const;
|
||||
int columnCount(const QModelIndex &parent = QModelIndex()) const;
|
||||
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
|
||||
int columnCount(const QModelIndex &parent = QModelIndex()) const override;
|
||||
|
||||
QVariant data(const QModelIndex &index, int role) const;
|
||||
QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const;
|
||||
QVariant data(const QModelIndex &index, int role) const override;
|
||||
QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override;
|
||||
|
||||
bool removeRows(int row, int count, const QModelIndex &parent = QModelIndex()) override;
|
||||
};
|
||||
|
||||
|
||||
@ -76,6 +78,32 @@ public:
|
||||
private slots:
|
||||
void refreshTypes();
|
||||
|
||||
/*!
|
||||
* \brief Show custom context menu
|
||||
* \param pt Position of the place where the right mouse button was clicked
|
||||
*/
|
||||
void showTypesContextMenu(const QPoint &pt);
|
||||
|
||||
/*!
|
||||
* \brief Executed on clicking the Export Types option in the context menu
|
||||
* It shows the user a file dialog box to select a file where the types
|
||||
* will be exported. It uses the "tc" command of radare2 to export the types.
|
||||
*/
|
||||
void on_actionExport_Types_triggered();
|
||||
|
||||
/*!
|
||||
* \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
|
||||
* types manually, or can select a file from where the types will be loaded
|
||||
*/
|
||||
void on_actionLoad_New_Types_triggered();
|
||||
|
||||
/*!
|
||||
* \brief Executed on clicking the Delete Type option in the context menu
|
||||
* Upon confirmation from the user, it will delete the selected type.
|
||||
*/
|
||||
void on_actionDelete_Type_triggered();
|
||||
|
||||
private:
|
||||
std::unique_ptr<Ui::TypesWidget> ui;
|
||||
|
||||
|
@ -71,6 +71,30 @@
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<action name="actionExport_Types">
|
||||
<property name="text">
|
||||
<string>Export Types</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Export Types</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionLoad_New_Types">
|
||||
<property name="text">
|
||||
<string>Load New Types</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Load New Types</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionDelete_Type">
|
||||
<property name="text">
|
||||
<string>Delete Type</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Delete Type</string>
|
||||
</property>
|
||||
</action>
|
||||
</widget>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
|
Loading…
Reference in New Issue
Block a user