2019-02-22 16:50:45 +00:00
|
|
|
|
#include "core/MainWindow.h"
|
2017-10-01 19:09:42 +00:00
|
|
|
|
#include "ui_MainWindow.h"
|
2017-05-14 16:21:54 +00:00
|
|
|
|
|
2018-11-17 10:10:44 +00:00
|
|
|
|
// Common Headers
|
2020-08-10 16:12:38 +00:00
|
|
|
|
#include "common/AnalTask.h"
|
2019-04-09 07:44:44 +00:00
|
|
|
|
#include "common/BugReporting.h"
|
2018-10-17 07:55:53 +00:00
|
|
|
|
#include "common/Highlighter.h"
|
|
|
|
|
#include "common/Helpers.h"
|
|
|
|
|
#include "common/SvgIconEngine.h"
|
|
|
|
|
#include "common/ProgressIndicator.h"
|
|
|
|
|
#include "common/TempConfig.h"
|
2019-03-14 09:28:42 +00:00
|
|
|
|
#include "common/RunScriptTask.h"
|
|
|
|
|
#include "common/PythonManager.h"
|
2020-08-14 18:30:51 +00:00
|
|
|
|
#include "plugins/CutterPlugin.h"
|
2019-03-14 09:28:42 +00:00
|
|
|
|
#include "plugins/PluginManager.h"
|
|
|
|
|
#include "CutterConfig.h"
|
2020-04-20 21:22:10 +00:00
|
|
|
|
#include "CutterApplication.h"
|
2017-04-13 15:36:20 +00:00
|
|
|
|
|
2018-11-17 10:10:44 +00:00
|
|
|
|
// Dialogs
|
2019-01-19 20:54:02 +00:00
|
|
|
|
#include "dialogs/WelcomeDialog.h"
|
2017-12-23 16:42:42 +00:00
|
|
|
|
#include "dialogs/NewFileDialog.h"
|
2018-08-18 10:51:11 +00:00
|
|
|
|
#include "dialogs/InitialOptionsDialog.h"
|
2018-06-20 09:24:28 +00:00
|
|
|
|
#include "dialogs/CommentsDialog.h"
|
|
|
|
|
#include "dialogs/AboutDialog.h"
|
|
|
|
|
#include "dialogs/preferences/PreferencesDialog.h"
|
2020-04-07 13:20:52 +00:00
|
|
|
|
#include "dialogs/MapFileDialog.h"
|
2019-01-28 13:37:20 +00:00
|
|
|
|
#include "dialogs/AsyncTaskDialog.h"
|
2020-05-23 22:12:32 +00:00
|
|
|
|
#include "dialogs/LayoutManager.h"
|
2018-06-20 09:24:28 +00:00
|
|
|
|
|
2018-11-17 10:10:44 +00:00
|
|
|
|
// Widgets Headers
|
2017-12-13 22:38:46 +00:00
|
|
|
|
#include "widgets/DisassemblerGraphView.h"
|
2019-01-24 17:13:04 +00:00
|
|
|
|
#include "widgets/GraphView.h"
|
2018-03-16 21:46:57 +00:00
|
|
|
|
#include "widgets/GraphWidget.h"
|
2019-01-24 17:13:04 +00:00
|
|
|
|
#include "widgets/OverviewWidget.h"
|
2019-02-19 15:56:50 +00:00
|
|
|
|
#include "widgets/OverviewView.h"
|
2017-10-02 16:18:40 +00:00
|
|
|
|
#include "widgets/FunctionsWidget.h"
|
|
|
|
|
#include "widgets/SectionsWidget.h"
|
2018-10-20 18:20:06 +00:00
|
|
|
|
#include "widgets/SegmentsWidget.h"
|
2017-10-02 16:18:40 +00:00
|
|
|
|
#include "widgets/CommentsWidget.h"
|
|
|
|
|
#include "widgets/ImportsWidget.h"
|
|
|
|
|
#include "widgets/ExportsWidget.h"
|
2018-03-06 17:21:48 +00:00
|
|
|
|
#include "widgets/TypesWidget.h"
|
2018-03-08 12:24:15 +00:00
|
|
|
|
#include "widgets/SearchWidget.h"
|
2017-10-02 16:18:40 +00:00
|
|
|
|
#include "widgets/SymbolsWidget.h"
|
|
|
|
|
#include "widgets/StringsWidget.h"
|
|
|
|
|
#include "widgets/RelocsWidget.h"
|
|
|
|
|
#include "widgets/FlagsWidget.h"
|
2017-12-08 15:00:52 +00:00
|
|
|
|
#include "widgets/VisualNavbar.h"
|
2017-10-02 16:18:40 +00:00
|
|
|
|
#include "widgets/Dashboard.h"
|
2019-02-13 06:38:47 +00:00
|
|
|
|
#include "widgets/SdbWidget.h"
|
2017-10-02 16:18:40 +00:00
|
|
|
|
#include "widgets/Omnibar.h"
|
|
|
|
|
#include "widgets/ConsoleWidget.h"
|
|
|
|
|
#include "widgets/EntrypointWidget.h"
|
2017-12-23 16:42:42 +00:00
|
|
|
|
#include "widgets/ClassesWidget.h"
|
2018-02-04 14:32:18 +00:00
|
|
|
|
#include "widgets/ResourcesWidget.h"
|
2018-02-26 22:26:18 +00:00
|
|
|
|
#include "widgets/VTablesWidget.h"
|
2018-05-21 17:34:41 +00:00
|
|
|
|
#include "widgets/HeadersWidget.h"
|
2018-05-24 15:37:37 +00:00
|
|
|
|
#include "widgets/ZignaturesWidget.h"
|
2018-12-21 20:36:40 +00:00
|
|
|
|
#include "widgets/DebugActions.h"
|
2018-06-12 08:43:14 +00:00
|
|
|
|
#include "widgets/MemoryMapWidget.h"
|
2018-06-22 08:45:00 +00:00
|
|
|
|
#include "widgets/BreakpointWidget.h"
|
2018-07-19 14:35:46 +00:00
|
|
|
|
#include "widgets/RegisterRefsWidget.h"
|
2019-03-14 09:28:42 +00:00
|
|
|
|
#include "widgets/DisassemblyWidget.h"
|
|
|
|
|
#include "widgets/StackWidget.h"
|
2019-10-22 15:51:56 +00:00
|
|
|
|
#include "widgets/ThreadsWidget.h"
|
2019-12-09 18:43:42 +00:00
|
|
|
|
#include "widgets/ProcessesWidget.h"
|
2019-03-14 09:28:42 +00:00
|
|
|
|
#include "widgets/RegistersWidget.h"
|
|
|
|
|
#include "widgets/BacktraceWidget.h"
|
|
|
|
|
#include "widgets/HexdumpWidget.h"
|
2019-09-06 05:40:20 +00:00
|
|
|
|
#include "widgets/DecompilerWidget.h"
|
2019-05-16 16:03:48 +00:00
|
|
|
|
#include "widgets/HexWidget.h"
|
2020-12-16 10:59:23 +00:00
|
|
|
|
#include "widgets/RizinGraphWidget.h"
|
2020-07-16 08:05:10 +00:00
|
|
|
|
#include "widgets/CallGraph.h"
|
2017-04-13 15:36:20 +00:00
|
|
|
|
|
2019-03-14 09:28:42 +00:00
|
|
|
|
// Qt Headers
|
2020-11-30 07:42:13 +00:00
|
|
|
|
#include <QActionGroup>
|
2019-03-14 09:28:42 +00:00
|
|
|
|
#include <QApplication>
|
|
|
|
|
#include <QComboBox>
|
|
|
|
|
#include <QCompleter>
|
|
|
|
|
#include <QDebug>
|
|
|
|
|
#include <QDesktopServices>
|
|
|
|
|
#include <QDir>
|
|
|
|
|
#include <QDockWidget>
|
|
|
|
|
#include <QFile>
|
|
|
|
|
#include <QFileDialog>
|
|
|
|
|
#include <QFont>
|
|
|
|
|
#include <QFontDialog>
|
|
|
|
|
#include <QLabel>
|
|
|
|
|
#include <QLineEdit>
|
|
|
|
|
#include <QList>
|
|
|
|
|
#include <QMessageBox>
|
|
|
|
|
#include <QProcess>
|
|
|
|
|
#include <QPropertyAnimation>
|
|
|
|
|
#include <QSysInfo>
|
|
|
|
|
#include <QJsonObject>
|
2019-12-10 07:34:21 +00:00
|
|
|
|
#include <QJsonArray>
|
2020-05-22 11:49:34 +00:00
|
|
|
|
#include <QInputDialog>
|
2019-03-14 09:28:42 +00:00
|
|
|
|
|
|
|
|
|
#include <QScrollBar>
|
|
|
|
|
#include <QSettings>
|
|
|
|
|
#include <QShortcut>
|
|
|
|
|
#include <QStringListModel>
|
|
|
|
|
#include <QStyledItemDelegate>
|
|
|
|
|
#include <QStyleFactory>
|
|
|
|
|
#include <QTextCursor>
|
|
|
|
|
#include <QtGlobal>
|
|
|
|
|
#include <QToolButton>
|
|
|
|
|
#include <QToolTip>
|
|
|
|
|
#include <QTreeWidgetItem>
|
|
|
|
|
#include <QSvgRenderer>
|
2019-01-28 13:37:20 +00:00
|
|
|
|
|
2018-07-06 21:23:51 +00:00
|
|
|
|
// Graphics
|
2017-03-29 10:18:37 +00:00
|
|
|
|
#include <QGraphicsEllipseItem>
|
|
|
|
|
#include <QGraphicsScene>
|
|
|
|
|
#include <QGraphicsView>
|
|
|
|
|
|
2020-12-01 11:45:26 +00:00
|
|
|
|
#define PROJECT_FILE_FILTER tr("Rizin Project (*.rzdb)")
|
|
|
|
|
|
2019-06-18 13:02:41 +00:00
|
|
|
|
template<class T>
|
2020-05-22 11:49:34 +00:00
|
|
|
|
T *getNewInstance(MainWindow *m) { return new T(m); }
|
|
|
|
|
|
2020-05-23 22:12:32 +00:00
|
|
|
|
using namespace Cutter;
|
2019-06-18 13:02:41 +00:00
|
|
|
|
|
2017-05-13 18:09:36 +00:00
|
|
|
|
MainWindow::MainWindow(QWidget *parent) :
|
2017-03-29 10:18:37 +00:00
|
|
|
|
QMainWindow(parent),
|
2018-02-26 22:25:23 +00:00
|
|
|
|
core(Core()),
|
2018-02-04 14:32:18 +00:00
|
|
|
|
ui(new Ui::MainWindow)
|
2017-03-29 10:18:37 +00:00
|
|
|
|
{
|
2017-12-17 13:49:03 +00:00
|
|
|
|
tabsOnTop = false;
|
2018-02-26 22:25:23 +00:00
|
|
|
|
configuration = Config();
|
2019-11-04 16:40:54 +00:00
|
|
|
|
|
|
|
|
|
initUI();
|
2017-05-13 18:09:36 +00:00
|
|
|
|
}
|
2017-03-29 10:18:37 +00:00
|
|
|
|
|
2017-05-13 18:09:36 +00:00
|
|
|
|
MainWindow::~MainWindow()
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void MainWindow::initUI()
|
|
|
|
|
{
|
|
|
|
|
ui->setupUi(this);
|
2017-04-05 08:56:59 +00:00
|
|
|
|
|
2020-01-31 10:13:28 +00:00
|
|
|
|
// Initialize context menu extensions for plugins
|
|
|
|
|
disassemblyContextMenuExtensions = new QMenu(tr("Plugins"), this);
|
|
|
|
|
addressableContextMenuExtensions = new QMenu(tr("Plugins"), this);
|
|
|
|
|
|
2020-08-29 05:15:47 +00:00
|
|
|
|
connect(ui->actionExtraDecompiler, &QAction::triggered, this, &MainWindow::addExtraDecompiler);
|
2019-06-18 13:02:41 +00:00
|
|
|
|
connect(ui->actionExtraGraph, &QAction::triggered, this, &MainWindow::addExtraGraph);
|
|
|
|
|
connect(ui->actionExtraDisassembly, &QAction::triggered, this, &MainWindow::addExtraDisassembly);
|
|
|
|
|
connect(ui->actionExtraHexdump, &QAction::triggered, this, &MainWindow::addExtraHexdump);
|
2020-03-19 09:36:36 +00:00
|
|
|
|
connect(ui->actionCommitChanges, &QAction::triggered, this, [this]() {
|
|
|
|
|
Core()->commitWriteCache();
|
|
|
|
|
});
|
|
|
|
|
ui->actionCommitChanges->setEnabled(false);
|
|
|
|
|
connect(Core(), &CutterCore::ioCacheChanged, ui->actionCommitChanges, &QAction::setEnabled);
|
2019-06-18 13:02:41 +00:00
|
|
|
|
|
2019-07-04 18:51:44 +00:00
|
|
|
|
widgetTypeToConstructorMap.insert(GraphWidget::getWidgetType(), getNewInstance<GraphWidget>);
|
2020-05-22 11:49:34 +00:00
|
|
|
|
widgetTypeToConstructorMap.insert(DisassemblyWidget::getWidgetType(),
|
|
|
|
|
getNewInstance<DisassemblyWidget>);
|
2019-07-04 18:51:44 +00:00
|
|
|
|
widgetTypeToConstructorMap.insert(HexdumpWidget::getWidgetType(), getNewInstance<HexdumpWidget>);
|
2020-08-30 11:26:56 +00:00
|
|
|
|
widgetTypeToConstructorMap.insert(DecompilerWidget::getWidgetType(),
|
|
|
|
|
getNewInstance<DecompilerWidget>);
|
2019-06-18 13:02:41 +00:00
|
|
|
|
|
2019-03-18 06:44:14 +00:00
|
|
|
|
initToolBar();
|
|
|
|
|
initDocks();
|
|
|
|
|
|
2019-06-18 13:02:41 +00:00
|
|
|
|
emptyState = saveState();
|
2017-03-29 10:18:37 +00:00
|
|
|
|
/*
|
2019-03-18 06:44:14 +00:00
|
|
|
|
* Some global shortcuts
|
2018-11-01 22:23:01 +00:00
|
|
|
|
*/
|
2019-04-27 17:58:44 +00:00
|
|
|
|
|
2019-03-18 06:44:14 +00:00
|
|
|
|
// Period goes to command entry
|
|
|
|
|
QShortcut *cmd_shortcut = new QShortcut(QKeySequence(Qt::Key_Period), this);
|
2020-08-03 09:13:39 +00:00
|
|
|
|
connect(cmd_shortcut, &QShortcut::activated, consoleDock, &ConsoleWidget::focusInputLineEdit);
|
2019-03-18 06:44:14 +00:00
|
|
|
|
|
|
|
|
|
// G and S goes to goto entry
|
|
|
|
|
QShortcut *goto_shortcut = new QShortcut(QKeySequence(Qt::Key_G), this);
|
2020-08-07 14:18:42 +00:00
|
|
|
|
connect(goto_shortcut, &QShortcut::activated, this->omnibar, [this](){ this->omnibar->setFocus(); });
|
2019-03-18 06:44:14 +00:00
|
|
|
|
QShortcut *seek_shortcut = new QShortcut(QKeySequence(Qt::Key_S), this);
|
2020-08-07 14:18:42 +00:00
|
|
|
|
connect(seek_shortcut, &QShortcut::activated, this->omnibar, [this](){ this->omnibar->setFocus(); });
|
2019-05-30 08:41:14 +00:00
|
|
|
|
QShortcut *seek_to_func_end_shortcut = new QShortcut(QKeySequence(Qt::Key_Dollar), this);
|
2020-08-03 09:13:39 +00:00
|
|
|
|
connect(seek_to_func_end_shortcut, &QShortcut::activated, this, &MainWindow::seekToFunctionLastInstruction);
|
2019-05-30 08:41:14 +00:00
|
|
|
|
QShortcut *seek_to_func_start_shortcut = new QShortcut(QKeySequence(Qt::Key_AsciiCircum), this);
|
2020-08-03 09:13:39 +00:00
|
|
|
|
connect(seek_to_func_start_shortcut, &QShortcut::activated, this, &MainWindow::seekToFunctionStart);
|
2019-03-18 06:44:14 +00:00
|
|
|
|
|
|
|
|
|
QShortcut *refresh_shortcut = new QShortcut(QKeySequence(QKeySequence::Refresh), this);
|
2020-08-03 09:13:39 +00:00
|
|
|
|
connect(refresh_shortcut, &QShortcut::activated, this, &MainWindow::refreshAll);
|
2019-03-18 06:44:14 +00:00
|
|
|
|
|
2020-08-03 09:13:39 +00:00
|
|
|
|
connect(ui->actionZoomIn, &QAction::triggered, this, &MainWindow::onZoomIn);
|
|
|
|
|
connect(ui->actionZoomOut, &QAction::triggered, this, &MainWindow::onZoomOut);
|
|
|
|
|
connect(ui->actionZoomReset, &QAction::triggered, this, &MainWindow::onZoomReset);
|
2019-10-12 05:50:10 +00:00
|
|
|
|
|
2019-12-13 17:30:55 +00:00
|
|
|
|
connect(core, &CutterCore::toggleDebugView, this, &MainWindow::toggleDebugView);
|
2019-03-18 06:44:14 +00:00
|
|
|
|
|
2020-08-03 09:13:39 +00:00
|
|
|
|
connect(core, &CutterCore::newMessage,
|
|
|
|
|
this->consoleDock, &ConsoleWidget::addOutput);
|
|
|
|
|
connect(core, &CutterCore::newDebugMessage,
|
|
|
|
|
this->consoleDock, &ConsoleWidget::addDebugOutput);
|
2019-03-18 06:44:14 +00:00
|
|
|
|
|
2019-07-19 19:21:12 +00:00
|
|
|
|
connect(core, &CutterCore::showMemoryWidgetRequested,
|
|
|
|
|
this, static_cast<void(MainWindow::*)()>(&MainWindow::showMemoryWidget));
|
|
|
|
|
|
2019-03-18 06:44:14 +00:00
|
|
|
|
updateTasksIndicator();
|
|
|
|
|
connect(core->getAsyncTaskManager(), &AsyncTaskManager::tasksChanged, this,
|
|
|
|
|
&MainWindow::updateTasksIndicator);
|
|
|
|
|
|
2020-05-23 11:41:12 +00:00
|
|
|
|
// Undo and redo seek
|
2019-05-10 18:22:54 +00:00
|
|
|
|
ui->actionBackward->setShortcut(QKeySequence::Back);
|
|
|
|
|
ui->actionForward->setShortcut(QKeySequence::Forward);
|
2019-07-19 19:21:12 +00:00
|
|
|
|
|
2019-12-10 07:34:21 +00:00
|
|
|
|
initBackForwardMenu();
|
|
|
|
|
|
2020-05-23 11:41:12 +00:00
|
|
|
|
connect(core, &CutterCore::ioModeChanged, this, &MainWindow::setAvailableIOModeOptions);
|
|
|
|
|
|
|
|
|
|
QActionGroup *ioModeActionGroup = new QActionGroup(this);
|
|
|
|
|
|
|
|
|
|
ioModeActionGroup->addAction(ui->actionCacheMode);
|
|
|
|
|
ioModeActionGroup->addAction(ui->actionWriteMode);
|
|
|
|
|
ioModeActionGroup->addAction(ui->actionReadOnly);
|
|
|
|
|
|
|
|
|
|
connect(ui->actionCacheMode, &QAction::triggered, this, [this]() {
|
|
|
|
|
ioModesController.setIOMode(IOModesController::Mode::CACHE);
|
|
|
|
|
setAvailableIOModeOptions();
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
connect(ui->actionWriteMode, &QAction::triggered, this, [this]() {
|
|
|
|
|
ioModesController.setIOMode(IOModesController::Mode::WRITE);
|
|
|
|
|
setAvailableIOModeOptions();
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
connect(ui->actionReadOnly, &QAction::triggered, this, [this]() {
|
|
|
|
|
ioModesController.setIOMode(IOModesController::Mode::READ_ONLY);
|
|
|
|
|
setAvailableIOModeOptions();
|
|
|
|
|
});
|
2020-11-30 07:42:13 +00:00
|
|
|
|
|
2020-05-22 11:49:34 +00:00
|
|
|
|
connect(ui->actionSaveLayout, &QAction::triggered, this, &MainWindow::saveNamedLayout);
|
2020-05-23 22:12:32 +00:00
|
|
|
|
connect(ui->actionManageLayouts, &QAction::triggered, this, &MainWindow::manageLayouts);
|
2020-08-11 12:52:52 +00:00
|
|
|
|
connect(ui->actionDocumentation, &QAction::triggered, this, &MainWindow::documentationClicked);
|
2020-05-22 11:49:34 +00:00
|
|
|
|
|
2019-03-18 06:44:14 +00:00
|
|
|
|
/* Setup plugins interfaces */
|
2020-08-28 18:17:31 +00:00
|
|
|
|
const auto &plugins = Plugins()->getPlugins();
|
|
|
|
|
for (auto &plugin : plugins) {
|
2019-03-18 06:44:14 +00:00
|
|
|
|
plugin->setupInterface(this);
|
|
|
|
|
}
|
2019-03-28 09:17:01 +00:00
|
|
|
|
|
2020-08-28 18:17:31 +00:00
|
|
|
|
// Check if plugins are loaded and display tooltips accordingly
|
|
|
|
|
ui->menuWindows->setToolTipsVisible(true);
|
|
|
|
|
if (plugins.empty()) {
|
|
|
|
|
ui->menuPlugins->menuAction()->setToolTip(
|
|
|
|
|
tr("No plugins are installed. Check the plugins section on Cutter documentation to learn more."));
|
|
|
|
|
ui->menuPlugins->setEnabled(false);
|
|
|
|
|
} else if (ui->menuPlugins->isEmpty()) {
|
|
|
|
|
ui->menuPlugins->menuAction()->setToolTip(
|
|
|
|
|
tr("The installed plugins didn't add entries to this menu."));
|
|
|
|
|
ui->menuPlugins->setEnabled(false);
|
|
|
|
|
}
|
|
|
|
|
|
2020-11-30 07:42:13 +00:00
|
|
|
|
connect(ui->actionUnlock, &QAction::toggled, this, [this](bool unlock){ lockDocks(!unlock); });
|
|
|
|
|
|
2019-10-06 17:35:44 +00:00
|
|
|
|
#if QT_VERSION < QT_VERSION_CHECK(5, 7, 0)
|
|
|
|
|
ui->actionGrouped_dock_dragging->setVisible(false);
|
|
|
|
|
#endif
|
|
|
|
|
|
2020-05-22 11:49:34 +00:00
|
|
|
|
enableDebugWidgetsMenu(false);
|
|
|
|
|
readSettings();
|
2020-08-10 16:12:38 +00:00
|
|
|
|
|
|
|
|
|
// Display tooltip for the Analyze Program action
|
2020-12-13 09:33:08 +00:00
|
|
|
|
ui->actionAnalyze->setToolTip("Analyze the program using Rizin's \"aaa\" command");
|
2020-08-10 16:12:38 +00:00
|
|
|
|
ui->menuFile->setToolTipsVisible(true);
|
2019-03-18 06:44:14 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void MainWindow::initToolBar()
|
|
|
|
|
{
|
2019-04-27 17:58:44 +00:00
|
|
|
|
chooseThemeIcons();
|
2019-07-19 19:21:12 +00:00
|
|
|
|
|
2017-03-29 10:18:37 +00:00
|
|
|
|
// Sepparator between undo/redo and goto lineEdit
|
2017-04-09 19:55:06 +00:00
|
|
|
|
QWidget *spacer3 = new QWidget();
|
2017-03-29 10:18:37 +00:00
|
|
|
|
spacer3->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
|
2019-04-27 17:58:44 +00:00
|
|
|
|
spacer3->setStyleSheet("background-color: rgba(0,0,0,0)");
|
2017-03-29 10:18:37 +00:00
|
|
|
|
spacer3->setMinimumSize(20, 20);
|
2018-06-12 08:43:14 +00:00
|
|
|
|
spacer3->setMaximumWidth(100);
|
2017-11-18 14:57:00 +00:00
|
|
|
|
ui->mainToolBar->addWidget(spacer3);
|
2017-03-29 10:18:37 +00:00
|
|
|
|
|
2018-12-21 20:36:40 +00:00
|
|
|
|
DebugActions *debugActions = new DebugActions(ui->mainToolBar, this);
|
2018-07-17 07:26:20 +00:00
|
|
|
|
// Debug menu
|
2019-12-19 18:03:44 +00:00
|
|
|
|
auto debugViewAction = ui->menuDebug->addAction(tr("View"));
|
|
|
|
|
debugViewAction->setMenu(ui->menuAddDebugWidgets);
|
|
|
|
|
ui->menuDebug->addSeparator();
|
2019-01-12 19:21:45 +00:00
|
|
|
|
ui->menuDebug->addAction(debugActions->actionStart);
|
2018-12-21 20:36:40 +00:00
|
|
|
|
ui->menuDebug->addAction(debugActions->actionStartEmul);
|
2019-01-12 19:21:45 +00:00
|
|
|
|
ui->menuDebug->addAction(debugActions->actionAttach);
|
2019-12-13 18:33:13 +00:00
|
|
|
|
ui->menuDebug->addAction(debugActions->actionStartRemote);
|
2018-07-17 07:26:20 +00:00
|
|
|
|
ui->menuDebug->addSeparator();
|
2018-12-21 20:36:40 +00:00
|
|
|
|
ui->menuDebug->addAction(debugActions->actionStep);
|
|
|
|
|
ui->menuDebug->addAction(debugActions->actionStepOver);
|
|
|
|
|
ui->menuDebug->addAction(debugActions->actionStepOut);
|
2018-07-17 07:26:20 +00:00
|
|
|
|
ui->menuDebug->addSeparator();
|
2018-12-21 20:36:40 +00:00
|
|
|
|
ui->menuDebug->addAction(debugActions->actionContinue);
|
|
|
|
|
ui->menuDebug->addAction(debugActions->actionContinueUntilCall);
|
|
|
|
|
ui->menuDebug->addAction(debugActions->actionContinueUntilSyscall);
|
2018-06-12 08:43:14 +00:00
|
|
|
|
|
|
|
|
|
// Sepparator between undo/redo and goto lineEdit
|
|
|
|
|
QWidget *spacer4 = new QWidget();
|
|
|
|
|
spacer4->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
|
2019-04-27 17:58:44 +00:00
|
|
|
|
spacer4->setStyleSheet("background-color: rgba(0,0,0,0)");
|
|
|
|
|
spacer4->setMinimumSize(10, 10);
|
|
|
|
|
spacer4->setMaximumWidth(10);
|
2018-06-12 08:43:14 +00:00
|
|
|
|
ui->mainToolBar->addWidget(spacer4);
|
|
|
|
|
|
2017-03-29 10:18:37 +00:00
|
|
|
|
// Omnibar LineEdit
|
|
|
|
|
this->omnibar = new Omnibar(this);
|
2017-11-18 14:57:00 +00:00
|
|
|
|
ui->mainToolBar->addWidget(this->omnibar);
|
2017-03-29 10:18:37 +00:00
|
|
|
|
|
2017-09-01 13:03:35 +00:00
|
|
|
|
// Add special separators to the toolbar that expand to separate groups of elements
|
2017-04-09 19:55:06 +00:00
|
|
|
|
QWidget *spacer2 = new QWidget();
|
2017-03-29 10:18:37 +00:00
|
|
|
|
spacer2->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
|
2019-04-27 17:58:44 +00:00
|
|
|
|
spacer2->setStyleSheet("background-color: rgba(0,0,0,0)");
|
2017-03-29 10:18:37 +00:00
|
|
|
|
spacer2->setMinimumSize(10, 10);
|
|
|
|
|
spacer2->setMaximumWidth(300);
|
2017-11-18 14:57:00 +00:00
|
|
|
|
ui->mainToolBar->addWidget(spacer2);
|
2017-03-29 10:18:37 +00:00
|
|
|
|
|
2017-09-01 13:03:35 +00:00
|
|
|
|
// Separator between back/forward and undo/redo buttons
|
2017-04-09 19:55:06 +00:00
|
|
|
|
QWidget *spacer = new QWidget();
|
2017-03-29 10:18:37 +00:00
|
|
|
|
spacer->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Expanding);
|
2019-04-27 17:58:44 +00:00
|
|
|
|
spacer->setStyleSheet("background-color: rgba(0,0,0,0)");
|
2017-03-29 10:18:37 +00:00
|
|
|
|
spacer->setMinimumSize(20, 20);
|
|
|
|
|
ui->mainToolBar->addWidget(spacer);
|
|
|
|
|
|
2018-06-24 19:12:02 +00:00
|
|
|
|
tasksProgressIndicator = new ProgressIndicator();
|
2019-04-27 17:58:44 +00:00
|
|
|
|
tasksProgressIndicator->setStyleSheet("background-color: rgba(0,0,0,0)");
|
2018-06-24 19:12:02 +00:00
|
|
|
|
ui->mainToolBar->addWidget(tasksProgressIndicator);
|
|
|
|
|
|
|
|
|
|
QWidget *spacerEnd = new QWidget();
|
|
|
|
|
spacerEnd->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Expanding);
|
2019-04-27 17:58:44 +00:00
|
|
|
|
spacerEnd->setStyleSheet("background-color: rgba(0,0,0,0)");
|
2018-06-24 19:12:02 +00:00
|
|
|
|
spacerEnd->setMinimumSize(4, 0);
|
|
|
|
|
spacerEnd->setMaximumWidth(4);
|
|
|
|
|
ui->mainToolBar->addWidget(spacerEnd);
|
2018-06-22 18:34:25 +00:00
|
|
|
|
|
2017-12-08 15:00:52 +00:00
|
|
|
|
// Visual navigation tool bar
|
|
|
|
|
this->visualNavbar = new VisualNavbar(this);
|
|
|
|
|
this->visualNavbar->setMovable(false);
|
2017-03-29 10:18:37 +00:00
|
|
|
|
addToolBarBreak(Qt::TopToolBarArea);
|
2017-12-08 15:00:52 +00:00
|
|
|
|
addToolBar(visualNavbar);
|
2020-08-07 14:18:42 +00:00
|
|
|
|
QObject::connect(configuration, &Configuration::colorsUpdated, this, [this]() {
|
2018-10-10 09:37:24 +00:00
|
|
|
|
this->visualNavbar->updateGraphicsScene();
|
|
|
|
|
});
|
2019-05-01 16:15:33 +00:00
|
|
|
|
QObject::connect(configuration, &Configuration::interfaceThemeChanged, this, &MainWindow::chooseThemeIcons);
|
2019-03-18 06:44:14 +00:00
|
|
|
|
}
|
2017-03-29 10:18:37 +00:00
|
|
|
|
|
2019-03-18 06:44:14 +00:00
|
|
|
|
void MainWindow::initDocks()
|
|
|
|
|
{
|
2018-02-04 14:32:18 +00:00
|
|
|
|
dockWidgets.reserve(20);
|
2020-05-22 11:49:34 +00:00
|
|
|
|
consoleDock = new ConsoleWidget(this);
|
2019-04-14 12:18:24 +00:00
|
|
|
|
|
2020-05-22 11:49:34 +00:00
|
|
|
|
overviewDock = new OverviewWidget(this);
|
2019-02-19 15:56:50 +00:00
|
|
|
|
overviewDock->hide();
|
2020-05-22 11:49:34 +00:00
|
|
|
|
actionOverview = overviewDock->toggleViewAction();
|
2019-04-14 12:18:24 +00:00
|
|
|
|
connect(overviewDock, &OverviewWidget::isAvailableChanged, this, [this](bool isAvailable) {
|
2020-05-22 11:49:34 +00:00
|
|
|
|
actionOverview->setEnabled(isAvailable);
|
2019-03-08 18:57:57 +00:00
|
|
|
|
});
|
2020-05-22 11:49:34 +00:00
|
|
|
|
actionOverview->setEnabled(overviewDock->getIsAvailable());
|
|
|
|
|
actionOverview->setChecked(overviewDock->getUserOpened());
|
|
|
|
|
|
|
|
|
|
dashboardDock = new Dashboard(this);
|
|
|
|
|
functionsDock = new FunctionsWidget(this);
|
|
|
|
|
typesDock = new TypesWidget(this);
|
|
|
|
|
searchDock = new SearchWidget(this);
|
|
|
|
|
commentsDock = new CommentsWidget(this);
|
|
|
|
|
stringsDock = new StringsWidget(this);
|
|
|
|
|
|
|
|
|
|
QList<CutterDockWidget *> debugDocks = {
|
|
|
|
|
stackDock = new StackWidget(this),
|
|
|
|
|
threadsDock = new ThreadsWidget(this),
|
|
|
|
|
processesDock = new ProcessesWidget(this),
|
|
|
|
|
backtraceDock = new BacktraceWidget(this),
|
|
|
|
|
registersDock = new RegistersWidget(this),
|
|
|
|
|
memoryMapDock = new MemoryMapWidget(this),
|
|
|
|
|
breakpointDock = new BreakpointWidget(this),
|
|
|
|
|
registerRefsDock = new RegisterRefsWidget(this)
|
|
|
|
|
};
|
2019-04-14 12:18:24 +00:00
|
|
|
|
|
2020-05-22 11:49:34 +00:00
|
|
|
|
QList<CutterDockWidget *> infoDocks = {
|
|
|
|
|
classesDock = new ClassesWidget(this),
|
|
|
|
|
entrypointDock = new EntrypointWidget(this),
|
|
|
|
|
exportsDock = new ExportsWidget(this),
|
|
|
|
|
flagsDock = new FlagsWidget(this),
|
|
|
|
|
headersDock = new HeadersWidget(this),
|
|
|
|
|
importsDock = new ImportsWidget(this),
|
|
|
|
|
relocsDock = new RelocsWidget(this),
|
|
|
|
|
resourcesDock = new ResourcesWidget(this),
|
|
|
|
|
sdbDock = new SdbWidget(this),
|
|
|
|
|
sectionsDock = new SectionsWidget(this),
|
|
|
|
|
segmentsDock = new SegmentsWidget(this),
|
|
|
|
|
symbolsDock = new SymbolsWidget(this),
|
|
|
|
|
vTablesDock = new VTablesWidget(this),
|
2020-07-16 08:05:10 +00:00
|
|
|
|
zignaturesDock = new ZignaturesWidget(this),
|
2020-12-16 10:59:23 +00:00
|
|
|
|
rzGraphDock = new RizinGraphWidget(this),
|
2020-07-16 08:05:10 +00:00
|
|
|
|
callGraphDock = new CallGraphWidget(this, false),
|
|
|
|
|
globalCallGraphDock = new CallGraphWidget(this, true),
|
2020-05-22 11:49:34 +00:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
auto makeActionList = [this](QList<CutterDockWidget *> docks) {
|
|
|
|
|
QList<QAction *> result;
|
|
|
|
|
for (auto dock : docks) {
|
|
|
|
|
if (dock != nullptr) {
|
|
|
|
|
result.push_back(dock->toggleViewAction());
|
|
|
|
|
} else {
|
|
|
|
|
auto separator = new QAction(this);
|
|
|
|
|
separator->setSeparator(true);
|
|
|
|
|
result.push_back(separator);
|
2019-06-18 13:02:41 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
2020-05-22 11:49:34 +00:00
|
|
|
|
return result;
|
|
|
|
|
};
|
2019-06-18 13:02:41 +00:00
|
|
|
|
|
2020-05-22 11:49:34 +00:00
|
|
|
|
QList<CutterDockWidget *> windowDocks = {
|
|
|
|
|
dashboardDock,
|
|
|
|
|
nullptr,
|
|
|
|
|
functionsDock,
|
|
|
|
|
overviewDock,
|
|
|
|
|
nullptr,
|
|
|
|
|
searchDock,
|
|
|
|
|
stringsDock,
|
|
|
|
|
typesDock,
|
|
|
|
|
nullptr,
|
|
|
|
|
};
|
2020-08-29 05:15:47 +00:00
|
|
|
|
ui->menuWindows->insertActions(ui->actionExtraDecompiler, makeActionList(windowDocks));
|
2020-05-22 11:49:34 +00:00
|
|
|
|
QList<CutterDockWidget *> windowDocks2 = {
|
|
|
|
|
consoleDock,
|
|
|
|
|
commentsDock,
|
|
|
|
|
nullptr,
|
|
|
|
|
};
|
|
|
|
|
ui->menuWindows->addActions(makeActionList(windowDocks2));
|
|
|
|
|
ui->menuAddInfoWidgets->addActions(makeActionList(infoDocks));
|
|
|
|
|
ui->menuAddDebugWidgets->addActions(makeActionList(debugDocks));
|
|
|
|
|
|
|
|
|
|
auto uniqueDocks = windowDocks + windowDocks2 + infoDocks + debugDocks;
|
|
|
|
|
for (auto dock : uniqueDocks) {
|
|
|
|
|
if (dock) { // ignore nullptr used as separators
|
|
|
|
|
addWidget(dock);
|
|
|
|
|
}
|
|
|
|
|
}
|
2018-06-22 18:34:25 +00:00
|
|
|
|
}
|
|
|
|
|
|
2019-02-19 15:56:50 +00:00
|
|
|
|
void MainWindow::toggleOverview(bool visibility, GraphWidget *targetGraph)
|
|
|
|
|
{
|
|
|
|
|
if (!overviewDock) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
2019-04-14 12:18:24 +00:00
|
|
|
|
if (visibility) {
|
|
|
|
|
overviewDock->setTargetGraphWidget(targetGraph);
|
2019-03-12 07:37:10 +00:00
|
|
|
|
}
|
2019-02-19 15:56:50 +00:00
|
|
|
|
}
|
|
|
|
|
|
2018-06-22 18:34:25 +00:00
|
|
|
|
void MainWindow::updateTasksIndicator()
|
|
|
|
|
{
|
2018-08-27 11:16:48 +00:00
|
|
|
|
bool running = core->getAsyncTaskManager()->getTasksRunning();
|
2018-06-24 19:12:02 +00:00
|
|
|
|
tasksProgressIndicator->setProgressIndicatorVisible(running);
|
2017-05-13 18:09:36 +00:00
|
|
|
|
}
|
|
|
|
|
|
2019-06-18 13:02:41 +00:00
|
|
|
|
void MainWindow::addExtraGraph()
|
2018-05-25 14:30:59 +00:00
|
|
|
|
{
|
2020-05-22 11:49:34 +00:00
|
|
|
|
auto *extraDock = new GraphWidget(this);
|
2018-05-25 14:30:59 +00:00
|
|
|
|
addExtraWidget(extraDock);
|
|
|
|
|
}
|
|
|
|
|
|
2019-06-18 13:02:41 +00:00
|
|
|
|
void MainWindow::addExtraHexdump()
|
2018-05-25 14:30:59 +00:00
|
|
|
|
{
|
2020-05-22 11:49:34 +00:00
|
|
|
|
auto *extraDock = new HexdumpWidget(this);
|
2018-05-25 14:30:59 +00:00
|
|
|
|
addExtraWidget(extraDock);
|
|
|
|
|
}
|
|
|
|
|
|
2019-06-18 13:02:41 +00:00
|
|
|
|
void MainWindow::addExtraDisassembly()
|
2018-05-25 14:30:59 +00:00
|
|
|
|
{
|
2020-05-22 11:49:34 +00:00
|
|
|
|
auto *extraDock = new DisassemblyWidget(this);
|
2018-05-25 14:30:59 +00:00
|
|
|
|
addExtraWidget(extraDock);
|
|
|
|
|
}
|
|
|
|
|
|
2020-08-29 05:15:47 +00:00
|
|
|
|
void MainWindow::addExtraDecompiler()
|
|
|
|
|
{
|
|
|
|
|
auto *extraDock = new DecompilerWidget(this);
|
|
|
|
|
addExtraWidget(extraDock);
|
|
|
|
|
}
|
|
|
|
|
|
2019-04-27 09:46:29 +00:00
|
|
|
|
void MainWindow::addExtraWidget(CutterDockWidget *extraDock)
|
2018-05-25 14:30:59 +00:00
|
|
|
|
{
|
2019-04-27 09:46:29 +00:00
|
|
|
|
extraDock->setTransient(true);
|
2020-05-22 11:49:34 +00:00
|
|
|
|
dockOnMainArea(extraDock);
|
|
|
|
|
addWidget(extraDock);
|
|
|
|
|
extraDock->show();
|
|
|
|
|
extraDock->raise();
|
2018-05-25 14:30:59 +00:00
|
|
|
|
}
|
|
|
|
|
|
2019-04-03 07:50:25 +00:00
|
|
|
|
QMenu *MainWindow::getMenuByType(MenuType type)
|
|
|
|
|
{
|
|
|
|
|
switch (type) {
|
|
|
|
|
case MenuType::File:
|
|
|
|
|
return ui->menuFile;
|
|
|
|
|
case MenuType::Edit:
|
|
|
|
|
return ui->menuEdit;
|
|
|
|
|
case MenuType::View:
|
|
|
|
|
return ui->menuView;
|
|
|
|
|
case MenuType::Windows:
|
|
|
|
|
return ui->menuWindows;
|
|
|
|
|
case MenuType::Debug:
|
|
|
|
|
return ui->menuDebug;
|
|
|
|
|
case MenuType::Help:
|
|
|
|
|
return ui->menuHelp;
|
|
|
|
|
case MenuType::Plugins:
|
|
|
|
|
return ui->menuPlugins;
|
|
|
|
|
default:
|
|
|
|
|
return nullptr;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-05-22 11:49:34 +00:00
|
|
|
|
void MainWindow::addPluginDockWidget(CutterDockWidget *dockWidget)
|
2019-02-07 16:19:05 +00:00
|
|
|
|
{
|
2019-06-18 13:02:41 +00:00
|
|
|
|
addWidget(dockWidget);
|
2020-05-22 11:49:34 +00:00
|
|
|
|
ui->menuPlugins->addAction(dockWidget->toggleViewAction());
|
2019-03-29 17:02:27 +00:00
|
|
|
|
addDockWidget(Qt::DockWidgetArea::TopDockWidgetArea, dockWidget);
|
2020-05-22 11:49:34 +00:00
|
|
|
|
pluginDocks.push_back(dockWidget);
|
2019-02-07 16:19:05 +00:00
|
|
|
|
}
|
|
|
|
|
|
2019-02-11 19:05:53 +00:00
|
|
|
|
void MainWindow::addMenuFileAction(QAction *action)
|
|
|
|
|
{
|
|
|
|
|
ui->menuFile->addAction(action);
|
|
|
|
|
}
|
|
|
|
|
|
2019-05-17 11:40:50 +00:00
|
|
|
|
void MainWindow::openNewFile(InitialOptions &options, bool skipOptionsDialog)
|
2017-05-13 18:09:36 +00:00
|
|
|
|
{
|
2018-08-18 16:04:45 +00:00
|
|
|
|
setFilename(options.filename);
|
2017-05-13 18:09:36 +00:00
|
|
|
|
|
2020-12-16 10:59:23 +00:00
|
|
|
|
/* Prompt to load filename.rz script */
|
2018-08-18 16:04:45 +00:00
|
|
|
|
if (options.script.isEmpty()) {
|
2020-12-16 10:59:23 +00:00
|
|
|
|
QString script = QString("%1.rz").arg(this->filename);
|
2020-10-28 12:28:04 +00:00
|
|
|
|
if (rz_file_exists(script.toStdString().data())) {
|
2018-08-18 16:04:45 +00:00
|
|
|
|
QMessageBox mb;
|
|
|
|
|
mb.setWindowTitle(tr("Script loading"));
|
|
|
|
|
mb.setText(tr("Do you want to load the '%1' script?").arg(script));
|
|
|
|
|
mb.setStandardButtons(QMessageBox::Yes | QMessageBox::No);
|
|
|
|
|
if (mb.exec() == QMessageBox::Yes) {
|
2018-08-25 11:23:31 +00:00
|
|
|
|
options.script = script;
|
2018-08-18 16:04:45 +00:00
|
|
|
|
}
|
2017-12-14 12:45:03 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Show analysis options dialog */
|
2018-08-18 16:04:45 +00:00
|
|
|
|
displayInitialOptionsDialog(options, skipOptionsDialog);
|
2018-01-27 10:40:26 +00:00
|
|
|
|
}
|
|
|
|
|
|
2018-05-08 20:44:53 +00:00
|
|
|
|
void MainWindow::openNewFileFailed()
|
|
|
|
|
{
|
|
|
|
|
displayNewFileDialog();
|
|
|
|
|
QMessageBox mb(this);
|
|
|
|
|
mb.setIcon(QMessageBox::Critical);
|
|
|
|
|
mb.setStandardButtons(QMessageBox::Ok);
|
|
|
|
|
mb.setWindowTitle(tr("Cannot open file!"));
|
2018-07-18 10:15:10 +00:00
|
|
|
|
mb.setText(
|
|
|
|
|
tr("Could not open the file! Make sure the file exists and that you have the correct permissions."));
|
2018-05-08 20:44:53 +00:00
|
|
|
|
mb.exec();
|
|
|
|
|
}
|
|
|
|
|
|
2019-03-06 20:30:39 +00:00
|
|
|
|
/**
|
|
|
|
|
* @brief displays the WelocmeDialog
|
2019-01-19 20:54:02 +00:00
|
|
|
|
*
|
|
|
|
|
* Upon first execution of Cutter, the WelcomeDialog would be showed to the user.
|
|
|
|
|
* The Welcome dialog would be showed after a reset of Cutter's preferences by the user.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
void MainWindow::displayWelcomeDialog()
|
|
|
|
|
{
|
|
|
|
|
WelcomeDialog w;
|
|
|
|
|
w.exec();
|
|
|
|
|
}
|
|
|
|
|
|
2018-02-06 18:23:01 +00:00
|
|
|
|
void MainWindow::displayNewFileDialog()
|
|
|
|
|
{
|
2019-03-10 22:26:25 +00:00
|
|
|
|
NewFileDialog *n = new NewFileDialog(this);
|
2018-02-10 18:04:31 +00:00
|
|
|
|
newFileDialog = n;
|
2018-02-06 18:23:01 +00:00
|
|
|
|
n->setAttribute(Qt::WA_DeleteOnClose);
|
|
|
|
|
n->show();
|
|
|
|
|
}
|
|
|
|
|
|
2018-02-10 18:04:31 +00:00
|
|
|
|
void MainWindow::closeNewFileDialog()
|
|
|
|
|
{
|
|
|
|
|
if (newFileDialog) {
|
|
|
|
|
newFileDialog->close();
|
|
|
|
|
}
|
|
|
|
|
newFileDialog = nullptr;
|
|
|
|
|
}
|
|
|
|
|
|
2018-08-18 16:04:45 +00:00
|
|
|
|
void MainWindow::displayInitialOptionsDialog(const InitialOptions &options, bool skipOptionsDialog)
|
2018-01-27 10:40:26 +00:00
|
|
|
|
{
|
2018-08-18 16:04:45 +00:00
|
|
|
|
auto o = new InitialOptionsDialog(this);
|
2017-05-13 18:09:36 +00:00
|
|
|
|
o->setAttribute(Qt::WA_DeleteOnClose);
|
2018-10-12 10:49:29 +00:00
|
|
|
|
o->loadOptions(options);
|
2017-05-13 18:09:36 +00:00
|
|
|
|
|
2018-08-18 16:04:45 +00:00
|
|
|
|
if (skipOptionsDialog) {
|
|
|
|
|
o->setupAndStartAnalysis();
|
2018-10-12 10:49:29 +00:00
|
|
|
|
} else {
|
2018-10-11 13:04:57 +00:00
|
|
|
|
o->show();
|
|
|
|
|
}
|
2017-05-13 18:09:36 +00:00
|
|
|
|
}
|
|
|
|
|
|
2020-12-01 11:45:26 +00:00
|
|
|
|
bool MainWindow::openProject(const QString &file)
|
2017-05-13 18:09:36 +00:00
|
|
|
|
{
|
2020-12-01 11:45:26 +00:00
|
|
|
|
RzProjectErr err;
|
|
|
|
|
RzList *res = rz_list_new();
|
|
|
|
|
{
|
|
|
|
|
RzCoreLocked core(Core());
|
|
|
|
|
err = rz_project_load_file(core, file.toUtf8().constData(), true, res);
|
|
|
|
|
}
|
|
|
|
|
if (err != RZ_PROJECT_ERR_SUCCESS) {
|
|
|
|
|
const char *s = rz_project_err_message(err);
|
|
|
|
|
QString msg = tr("Failed to open project: %1").arg(QString::fromUtf8(s));
|
|
|
|
|
RzListIter *it;
|
|
|
|
|
CutterRListForeach(res, it, const char, s) {
|
|
|
|
|
msg += "\n" + QString::fromUtf8(s);
|
|
|
|
|
}
|
|
|
|
|
QMessageBox::critical(this, tr("Open Project"), msg);
|
|
|
|
|
rz_list_free(res);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2017-05-13 18:09:36 +00:00
|
|
|
|
|
2020-12-01 11:45:26 +00:00
|
|
|
|
Config()->addRecentProject(file);
|
2017-05-13 18:09:36 +00:00
|
|
|
|
|
2020-12-01 11:45:26 +00:00
|
|
|
|
rz_list_free(res);
|
|
|
|
|
setFilename(file.trimmed());
|
2017-05-13 18:09:36 +00:00
|
|
|
|
finalizeOpen();
|
2020-12-01 11:45:26 +00:00
|
|
|
|
return true;
|
2017-05-13 18:09:36 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void MainWindow::finalizeOpen()
|
|
|
|
|
{
|
|
|
|
|
core->getOpcodes();
|
2018-09-06 17:32:12 +00:00
|
|
|
|
core->updateSeek();
|
2017-11-19 12:56:10 +00:00
|
|
|
|
refreshAll();
|
2017-05-13 18:09:36 +00:00
|
|
|
|
// Add fortune message
|
2020-03-20 18:14:21 +00:00
|
|
|
|
core->message("\n" + core->cmdRaw("fo"));
|
2019-04-12 05:22:06 +00:00
|
|
|
|
|
2020-07-16 08:05:10 +00:00
|
|
|
|
// hide all docks before showing window to avoid false positive for refreshDeferrer
|
|
|
|
|
for (auto dockWidget : dockWidgets) {
|
|
|
|
|
dockWidget->hide();
|
|
|
|
|
}
|
|
|
|
|
|
2020-05-22 11:49:34 +00:00
|
|
|
|
QSettings settings;
|
|
|
|
|
auto geometry = settings.value("geometry").toByteArray();
|
|
|
|
|
if (!geometry.isEmpty()) {
|
|
|
|
|
restoreGeometry(geometry);
|
|
|
|
|
show();
|
|
|
|
|
} else {
|
|
|
|
|
showMaximized();
|
2019-06-18 13:02:41 +00:00
|
|
|
|
}
|
2019-04-12 05:22:06 +00:00
|
|
|
|
|
2020-05-22 11:49:34 +00:00
|
|
|
|
Config()->adjustColorThemeDarkness();
|
|
|
|
|
setViewLayout(getViewLayout(LAYOUT_DEFAULT));
|
|
|
|
|
|
2019-04-12 05:22:06 +00:00
|
|
|
|
|
2020-07-21 09:40:53 +00:00
|
|
|
|
// Set focus to disasm or graph widget
|
|
|
|
|
// Graph with function in it has focus priority over DisasmWidget.
|
2019-04-12 05:22:06 +00:00
|
|
|
|
// If there are no graph/disasm widgets focus on MainWindow
|
|
|
|
|
|
|
|
|
|
setFocus();
|
|
|
|
|
bool graphContainsFunc = false;
|
|
|
|
|
for (auto dockWidget : dockWidgets) {
|
2020-05-22 11:49:34 +00:00
|
|
|
|
auto graphWidget = qobject_cast<GraphWidget *>(dockWidget);
|
2020-07-21 09:40:53 +00:00
|
|
|
|
if (graphWidget && dockWidget->isVisibleToUser()) {
|
2019-06-18 18:22:26 +00:00
|
|
|
|
graphContainsFunc = !graphWidget->getGraphView()->getBlocks().empty();
|
2019-04-12 05:22:06 +00:00
|
|
|
|
if (graphContainsFunc) {
|
2020-07-21 09:40:53 +00:00
|
|
|
|
dockWidget->raiseMemoryWidget();
|
2019-04-12 05:22:06 +00:00
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-05-22 11:49:34 +00:00
|
|
|
|
auto disasmWidget = qobject_cast<DisassemblyWidget *>(dockWidget);
|
2020-07-21 09:40:53 +00:00
|
|
|
|
if (disasmWidget && dockWidget->isVisibleToUser()) {
|
|
|
|
|
disasmWidget->raiseMemoryWidget();
|
2020-08-30 11:26:56 +00:00
|
|
|
|
// continue looping in case there is a graph widget
|
|
|
|
|
}
|
|
|
|
|
auto decompilerWidget = qobject_cast<DecompilerWidget *>(dockWidget);
|
|
|
|
|
if (decompilerWidget && dockWidget->isVisibleToUser()) {
|
|
|
|
|
decompilerWidget->raiseMemoryWidget();
|
|
|
|
|
// continue looping in case there is a graph widget
|
2019-04-12 05:22:06 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
2017-05-13 18:09:36 +00:00
|
|
|
|
}
|
|
|
|
|
|
2020-12-01 11:45:26 +00:00
|
|
|
|
RzProjectErr MainWindow::saveProject(bool *canceled)
|
2017-05-13 18:09:36 +00:00
|
|
|
|
{
|
2020-12-01 11:45:26 +00:00
|
|
|
|
QString file = core->getConfig("prj.file");
|
|
|
|
|
if (file.isEmpty()) {
|
|
|
|
|
return saveProjectAs(canceled);
|
|
|
|
|
}
|
|
|
|
|
if (canceled) {
|
|
|
|
|
*canceled = false;
|
|
|
|
|
}
|
|
|
|
|
RzProjectErr err = rz_project_save_file(RzCoreLocked(core), file.toUtf8().constData());
|
|
|
|
|
if (err == RZ_PROJECT_ERR_SUCCESS) {
|
|
|
|
|
Config()->addRecentProject(file);
|
|
|
|
|
}
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
RzProjectErr MainWindow::saveProjectAs(bool *canceled)
|
|
|
|
|
{
|
|
|
|
|
QString dir = core->getConfig("prj.file");
|
|
|
|
|
if (dir.isEmpty()) {
|
|
|
|
|
dir = QDir(filename).dirName();
|
|
|
|
|
}
|
|
|
|
|
QString file = QFileDialog::getSaveFileName(this, tr("Save Project"), dir, PROJECT_FILE_FILTER);
|
|
|
|
|
if (file.isEmpty()) {
|
|
|
|
|
if (canceled) {
|
|
|
|
|
*canceled = true;
|
|
|
|
|
}
|
|
|
|
|
return RZ_PROJECT_ERR_SUCCESS;
|
|
|
|
|
}
|
|
|
|
|
if (canceled) {
|
|
|
|
|
*canceled = false;
|
2017-10-21 19:20:10 +00:00
|
|
|
|
}
|
2020-12-01 11:45:26 +00:00
|
|
|
|
RzProjectErr err = rz_project_save_file(RzCoreLocked(core), file.toUtf8().constData());
|
|
|
|
|
if (err == RZ_PROJECT_ERR_SUCCESS) {
|
|
|
|
|
Config()->addRecentProject(file);
|
|
|
|
|
}
|
|
|
|
|
return err;
|
2017-10-21 19:20:10 +00:00
|
|
|
|
}
|
|
|
|
|
|
2020-12-01 11:45:26 +00:00
|
|
|
|
void MainWindow::showProjectSaveError(RzProjectErr err)
|
2017-10-21 19:20:10 +00:00
|
|
|
|
{
|
2020-12-01 11:45:26 +00:00
|
|
|
|
if (err == RZ_PROJECT_ERR_SUCCESS) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
const char *s = rz_project_err_message(err);
|
|
|
|
|
QMessageBox::critical(this, tr("Save Project"), tr("Failed to save project: %1").arg(QString::fromUtf8(s)));
|
2017-03-29 10:18:37 +00:00
|
|
|
|
}
|
|
|
|
|
|
2017-04-27 18:59:27 +00:00
|
|
|
|
void MainWindow::refreshOmniBar(const QStringList &flags)
|
|
|
|
|
{
|
|
|
|
|
omnibar->refresh(flags);
|
|
|
|
|
}
|
|
|
|
|
|
2017-04-28 13:38:01 +00:00
|
|
|
|
void MainWindow::setFilename(const QString &fn)
|
2017-04-13 15:36:20 +00:00
|
|
|
|
{
|
2017-03-29 10:18:37 +00:00
|
|
|
|
// Add file name to window title
|
|
|
|
|
this->filename = fn;
|
2018-09-27 11:16:07 +00:00
|
|
|
|
this->setWindowTitle(APPNAME" – " + fn);
|
2017-03-29 10:18:37 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void MainWindow::closeEvent(QCloseEvent *event)
|
|
|
|
|
{
|
2020-03-19 09:36:36 +00:00
|
|
|
|
|
|
|
|
|
// Check if there are uncommitted changes
|
2020-05-23 11:41:12 +00:00
|
|
|
|
if (!ioModesController.askCommitUnsavedChanges()) {
|
|
|
|
|
// if false, Cancel was chosen
|
|
|
|
|
event->ignore();
|
|
|
|
|
return;
|
2020-03-19 09:36:36 +00:00
|
|
|
|
}
|
|
|
|
|
|
2017-09-25 12:55:41 +00:00
|
|
|
|
QMessageBox::StandardButton ret = QMessageBox::question(this, APPNAME,
|
2019-02-07 10:52:08 +00:00
|
|
|
|
tr("Do you really want to exit?\nSave your project before closing!"),
|
|
|
|
|
(QMessageBox::StandardButtons)(QMessageBox::Save | QMessageBox::Discard | QMessageBox::Cancel));
|
2018-10-12 07:08:04 +00:00
|
|
|
|
if (ret == QMessageBox::Cancel) {
|
|
|
|
|
event->ignore();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2020-12-01 11:45:26 +00:00
|
|
|
|
if (ret == QMessageBox::Save) {
|
|
|
|
|
bool canceled;
|
|
|
|
|
RzProjectErr save_err = saveProject(&canceled);
|
|
|
|
|
if (canceled) {
|
|
|
|
|
event->ignore();
|
|
|
|
|
return;
|
|
|
|
|
} else if (save_err != RZ_PROJECT_ERR_SUCCESS) {
|
|
|
|
|
event->ignore();
|
|
|
|
|
showProjectSaveError(save_err);
|
|
|
|
|
return;
|
|
|
|
|
}
|
2018-10-12 07:08:04 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!core->currentlyDebugging) {
|
|
|
|
|
saveSettings();
|
|
|
|
|
} else {
|
|
|
|
|
core->stopDebug();
|
2017-03-29 10:18:37 +00:00
|
|
|
|
}
|
2018-10-12 07:08:04 +00:00
|
|
|
|
QMainWindow::closeEvent(event);
|
2017-03-29 10:18:37 +00:00
|
|
|
|
}
|
|
|
|
|
|
2019-03-18 06:44:14 +00:00
|
|
|
|
void MainWindow::paintEvent(QPaintEvent *event)
|
|
|
|
|
{
|
|
|
|
|
QMainWindow::paintEvent(event);
|
|
|
|
|
/*
|
|
|
|
|
* Dirty hack
|
2020-05-22 11:49:34 +00:00
|
|
|
|
* Just to adjust the width of Functions Widget to fixed size.
|
|
|
|
|
* After everything is drawn, restore the max width limit.
|
2019-03-18 06:44:14 +00:00
|
|
|
|
*/
|
2020-05-22 11:49:34 +00:00
|
|
|
|
if (functionsDock && functionDockWidthToRestore) {
|
|
|
|
|
functionsDock->setMaximumWidth(functionDockWidthToRestore);
|
|
|
|
|
functionDockWidthToRestore = 0;
|
2019-03-18 06:44:14 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-05-22 11:49:34 +00:00
|
|
|
|
void MainWindow::readSettings()
|
2017-03-29 10:18:37 +00:00
|
|
|
|
{
|
2017-04-03 00:18:09 +00:00
|
|
|
|
QSettings settings;
|
2019-03-29 17:02:27 +00:00
|
|
|
|
|
2019-03-18 06:44:14 +00:00
|
|
|
|
responsive = settings.value("responsive").toBool();
|
2020-11-30 07:42:13 +00:00
|
|
|
|
lockDocks(settings.value("panelLock").toBool());
|
2017-12-17 13:49:03 +00:00
|
|
|
|
tabsOnTop = settings.value("tabsOnTop").toBool();
|
|
|
|
|
setTabLocation();
|
2019-05-08 11:38:25 +00:00
|
|
|
|
bool dockGroupedDragging = settings.value("docksGroupedDragging", false).toBool();
|
|
|
|
|
ui->actionGrouped_dock_dragging->setChecked(dockGroupedDragging);
|
|
|
|
|
on_actionGrouped_dock_dragging_triggered(dockGroupedDragging);
|
|
|
|
|
|
2020-05-22 11:49:34 +00:00
|
|
|
|
loadLayouts(settings);
|
2017-12-17 13:49:03 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void MainWindow::saveSettings()
|
|
|
|
|
{
|
|
|
|
|
QSettings settings;
|
2019-06-18 13:02:41 +00:00
|
|
|
|
|
2020-11-30 07:42:13 +00:00
|
|
|
|
settings.setValue("panelLock", !ui->actionUnlock->isChecked());
|
2017-12-17 13:49:03 +00:00
|
|
|
|
settings.setValue("tabsOnTop", tabsOnTop);
|
2019-05-08 11:38:25 +00:00
|
|
|
|
settings.setValue("docksGroupedDragging", ui->actionGrouped_dock_dragging->isChecked());
|
2020-05-22 11:49:34 +00:00
|
|
|
|
settings.setValue("geometry", saveGeometry());
|
2017-12-17 13:49:03 +00:00
|
|
|
|
|
2020-05-22 11:49:34 +00:00
|
|
|
|
layouts[Core()->currentlyDebugging ? LAYOUT_DEBUG : LAYOUT_DEFAULT] = getViewLayout();
|
|
|
|
|
saveLayouts(settings);
|
2018-07-23 23:12:36 +00:00
|
|
|
|
}
|
|
|
|
|
|
2017-12-17 13:49:03 +00:00
|
|
|
|
void MainWindow::setTabLocation()
|
|
|
|
|
{
|
2018-03-21 20:32:32 +00:00
|
|
|
|
if (tabsOnTop) {
|
2017-12-17 13:49:03 +00:00
|
|
|
|
this->setTabPosition(Qt::AllDockWidgetAreas, QTabWidget::North);
|
|
|
|
|
ui->actionTabs_on_Top->setChecked(true);
|
2018-03-21 20:32:32 +00:00
|
|
|
|
} else {
|
2017-12-17 13:49:03 +00:00
|
|
|
|
this->setTabPosition(Qt::AllDockWidgetAreas, QTabWidget::South);
|
|
|
|
|
ui->actionTabs_on_Top->setChecked(false);
|
|
|
|
|
}
|
2017-03-29 10:18:37 +00:00
|
|
|
|
}
|
|
|
|
|
|
2017-11-19 12:56:10 +00:00
|
|
|
|
void MainWindow::refreshAll()
|
2017-04-09 19:55:06 +00:00
|
|
|
|
{
|
2018-08-27 11:16:48 +00:00
|
|
|
|
core->triggerRefreshAll();
|
2017-03-29 10:18:37 +00:00
|
|
|
|
}
|
|
|
|
|
|
2020-11-30 07:42:13 +00:00
|
|
|
|
void MainWindow::lockDocks(bool lock)
|
2017-03-29 10:18:37 +00:00
|
|
|
|
{
|
2020-11-30 07:42:13 +00:00
|
|
|
|
if (ui->actionUnlock->isChecked() == lock) {
|
|
|
|
|
ui->actionUnlock->setChecked(!lock);
|
|
|
|
|
}
|
|
|
|
|
if (lock) {
|
2018-05-04 07:58:32 +00:00
|
|
|
|
for (QDockWidget *dockWidget : findChildren<QDockWidget *>()) {
|
2017-03-29 10:18:37 +00:00
|
|
|
|
dockWidget->setFeatures(QDockWidget::NoDockWidgetFeatures);
|
|
|
|
|
}
|
2018-03-21 20:32:32 +00:00
|
|
|
|
} else {
|
2018-05-04 07:58:32 +00:00
|
|
|
|
for (QDockWidget *dockWidget : findChildren<QDockWidget *>()) {
|
2020-11-30 07:42:13 +00:00
|
|
|
|
dockWidget->setFeatures(QDockWidget::DockWidgetClosable |
|
|
|
|
|
QDockWidget::DockWidgetMovable |
|
|
|
|
|
QDockWidget::DockWidgetFloatable);
|
2017-03-29 10:18:37 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void MainWindow::restoreDocks()
|
|
|
|
|
{
|
2020-05-22 11:49:34 +00:00
|
|
|
|
// Initial structure
|
|
|
|
|
// func | main area | debug
|
|
|
|
|
// |___________|
|
|
|
|
|
// | console |
|
|
|
|
|
addDockWidget(Qt::LeftDockWidgetArea, functionsDock);
|
2019-06-18 13:02:41 +00:00
|
|
|
|
splitDockWidget(functionsDock, dashboardDock, Qt::Horizontal);
|
2020-05-22 11:49:34 +00:00
|
|
|
|
splitDockWidget(dashboardDock, stackDock, Qt::Horizontal);
|
|
|
|
|
splitDockWidget(dashboardDock, consoleDock, Qt::Vertical);
|
|
|
|
|
|
|
|
|
|
// overview bellow func
|
|
|
|
|
splitDockWidget(functionsDock, overviewDock, Qt::Vertical);
|
|
|
|
|
|
|
|
|
|
// main area
|
2019-01-13 17:55:02 +00:00
|
|
|
|
tabifyDockWidget(dashboardDock, entrypointDock);
|
|
|
|
|
tabifyDockWidget(dashboardDock, flagsDock);
|
|
|
|
|
tabifyDockWidget(dashboardDock, stringsDock);
|
|
|
|
|
tabifyDockWidget(dashboardDock, relocsDock);
|
|
|
|
|
tabifyDockWidget(dashboardDock, importsDock);
|
|
|
|
|
tabifyDockWidget(dashboardDock, exportsDock);
|
|
|
|
|
tabifyDockWidget(dashboardDock, typesDock);
|
|
|
|
|
tabifyDockWidget(dashboardDock, searchDock);
|
|
|
|
|
tabifyDockWidget(dashboardDock, headersDock);
|
|
|
|
|
tabifyDockWidget(dashboardDock, zignaturesDock);
|
|
|
|
|
tabifyDockWidget(dashboardDock, symbolsDock);
|
|
|
|
|
tabifyDockWidget(dashboardDock, classesDock);
|
|
|
|
|
tabifyDockWidget(dashboardDock, resourcesDock);
|
|
|
|
|
tabifyDockWidget(dashboardDock, vTablesDock);
|
2019-02-13 06:38:47 +00:00
|
|
|
|
tabifyDockWidget(dashboardDock, sdbDock);
|
2019-06-18 13:02:41 +00:00
|
|
|
|
tabifyDockWidget(dashboardDock, memoryMapDock);
|
|
|
|
|
tabifyDockWidget(dashboardDock, breakpointDock);
|
|
|
|
|
tabifyDockWidget(dashboardDock, registerRefsDock);
|
2020-12-16 10:59:23 +00:00
|
|
|
|
tabifyDockWidget(dashboardDock, rzGraphDock);
|
2020-07-16 08:05:10 +00:00
|
|
|
|
tabifyDockWidget(dashboardDock, callGraphDock);
|
|
|
|
|
tabifyDockWidget(dashboardDock, globalCallGraphDock);
|
2019-06-18 13:02:41 +00:00
|
|
|
|
for (const auto &it : dockWidgets) {
|
|
|
|
|
// Check whether or not current widgets is graph, hexdump or disasm
|
2020-05-22 11:49:34 +00:00
|
|
|
|
if (isExtraMemoryWidget(it)) {
|
2019-06-18 13:02:41 +00:00
|
|
|
|
tabifyDockWidget(dashboardDock, it);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-05-22 11:49:34 +00:00
|
|
|
|
// Console | Sections/segments/comments
|
2019-06-18 13:02:41 +00:00
|
|
|
|
splitDockWidget(consoleDock, sectionsDock, Qt::Horizontal);
|
2020-05-22 11:49:34 +00:00
|
|
|
|
tabifyDockWidget(sectionsDock, segmentsDock);
|
2019-06-18 13:02:41 +00:00
|
|
|
|
tabifyDockWidget(sectionsDock, commentsDock);
|
2018-06-06 11:05:20 +00:00
|
|
|
|
|
2019-10-22 15:51:56 +00:00
|
|
|
|
// Add Stack, Registers, Threads and Backtrace vertically stacked
|
2019-01-13 18:11:59 +00:00
|
|
|
|
splitDockWidget(stackDock, registersDock, Qt::Vertical);
|
|
|
|
|
tabifyDockWidget(stackDock, backtraceDock);
|
2019-10-22 15:51:56 +00:00
|
|
|
|
tabifyDockWidget(backtraceDock, threadsDock);
|
2019-12-09 18:43:42 +00:00
|
|
|
|
tabifyDockWidget(threadsDock, processesDock);
|
2019-01-13 18:11:59 +00:00
|
|
|
|
|
2020-05-22 11:49:34 +00:00
|
|
|
|
for (auto dock : pluginDocks) {
|
|
|
|
|
dockOnMainArea(dock);
|
2019-06-18 13:02:41 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-12-20 06:19:44 +00:00
|
|
|
|
bool MainWindow::isDebugWidget(QDockWidget *dock) const
|
|
|
|
|
{
|
|
|
|
|
return dock == stackDock ||
|
|
|
|
|
dock == registersDock ||
|
|
|
|
|
dock == backtraceDock ||
|
|
|
|
|
dock == threadsDock ||
|
|
|
|
|
dock == memoryMapDock ||
|
|
|
|
|
dock == breakpointDock ||
|
|
|
|
|
dock == processesDock ||
|
|
|
|
|
dock == registerRefsDock;
|
|
|
|
|
}
|
|
|
|
|
|
2020-05-22 11:49:34 +00:00
|
|
|
|
bool MainWindow::isExtraMemoryWidget(QDockWidget *dock) const
|
|
|
|
|
{
|
|
|
|
|
return qobject_cast<GraphWidget*>(dock) ||
|
|
|
|
|
qobject_cast<HexdumpWidget*>(dock) ||
|
2020-08-30 11:26:56 +00:00
|
|
|
|
qobject_cast<DisassemblyWidget*>(dock) ||
|
|
|
|
|
qobject_cast<DecompilerWidget*>(dock);
|
2020-05-22 11:49:34 +00:00
|
|
|
|
}
|
|
|
|
|
|
2019-07-19 19:21:12 +00:00
|
|
|
|
MemoryWidgetType MainWindow::getMemoryWidgetTypeToRestore()
|
|
|
|
|
{
|
2019-09-02 07:34:41 +00:00
|
|
|
|
if (lastSyncMemoryWidget) {
|
|
|
|
|
return lastSyncMemoryWidget->getType();
|
2019-07-19 19:21:12 +00:00
|
|
|
|
}
|
|
|
|
|
return MemoryWidgetType::Disassembly;
|
|
|
|
|
}
|
|
|
|
|
|
2019-06-18 17:57:07 +00:00
|
|
|
|
QString MainWindow::getUniqueObjectName(const QString &widgetType) const
|
2019-06-18 13:02:41 +00:00
|
|
|
|
{
|
|
|
|
|
QStringList docks;
|
|
|
|
|
docks.reserve(dockWidgets.size());
|
|
|
|
|
QString name;
|
|
|
|
|
for (const auto &it : dockWidgets) {
|
|
|
|
|
name = it->objectName();
|
2019-06-18 17:57:07 +00:00
|
|
|
|
if (name.split(';').at(0) == widgetType) {
|
2019-06-18 13:02:41 +00:00
|
|
|
|
docks.push_back(name);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (docks.isEmpty()) {
|
|
|
|
|
return widgetType;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int id = 0;
|
|
|
|
|
while (docks.contains(widgetType + ";" + QString::number(id))) {
|
|
|
|
|
id++;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return widgetType + ";" + QString::number(id);
|
|
|
|
|
}
|
|
|
|
|
|
2019-07-19 19:21:12 +00:00
|
|
|
|
void MainWindow::showMemoryWidget()
|
|
|
|
|
{
|
2019-09-02 07:34:41 +00:00
|
|
|
|
if (lastSyncMemoryWidget) {
|
|
|
|
|
if (lastSyncMemoryWidget->tryRaiseMemoryWidget()) {
|
2019-07-19 19:21:12 +00:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
showMemoryWidget(MemoryWidgetType::Disassembly);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void MainWindow::showMemoryWidget(MemoryWidgetType type)
|
|
|
|
|
{
|
|
|
|
|
for (auto &dock : dockWidgets) {
|
|
|
|
|
if (auto memoryWidget = qobject_cast<MemoryDockWidget *>(dock)) {
|
|
|
|
|
if (memoryWidget->getType() == type && memoryWidget->getSeekable()->isSynchronized()) {
|
|
|
|
|
memoryWidget->tryRaiseMemoryWidget();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
auto memoryDockWidget = addNewMemoryWidget(type, Core()->getOffset());
|
|
|
|
|
memoryDockWidget->raiseMemoryWidget();
|
|
|
|
|
}
|
|
|
|
|
|
2020-07-30 08:41:23 +00:00
|
|
|
|
QMenu *MainWindow::createShowInMenu(QWidget *parent, RVA address, AddressTypeHint addressType)
|
2019-07-19 19:21:12 +00:00
|
|
|
|
{
|
|
|
|
|
QMenu *menu = new QMenu(parent);
|
2020-07-16 08:05:10 +00:00
|
|
|
|
// Memory dock widgets
|
2019-07-19 19:21:12 +00:00
|
|
|
|
for (auto &dock : dockWidgets) {
|
|
|
|
|
if (auto memoryWidget = qobject_cast<MemoryDockWidget *>(dock)) {
|
2020-07-30 08:41:23 +00:00
|
|
|
|
if (memoryWidget->getType() == MemoryWidgetType::Graph
|
|
|
|
|
|| memoryWidget->getType() == MemoryWidgetType::Decompiler)
|
|
|
|
|
{
|
|
|
|
|
if (addressType == AddressTypeHint::Data) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
}
|
2019-07-19 19:21:12 +00:00
|
|
|
|
QAction *action = new QAction(memoryWidget->windowTitle(), menu);
|
2020-07-16 08:05:10 +00:00
|
|
|
|
connect(action, &QAction::triggered, this, [memoryWidget, address]() {
|
|
|
|
|
memoryWidget->getSeekable()->seek(address);
|
|
|
|
|
memoryWidget->raiseMemoryWidget();
|
|
|
|
|
});
|
|
|
|
|
menu->addAction(action);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
menu->addSeparator();
|
|
|
|
|
// Rest of the AddressableDockWidgets that weren't added already
|
|
|
|
|
for (auto &dock : dockWidgets) {
|
|
|
|
|
if (auto memoryWidget = qobject_cast<AddressableDockWidget *>(dock)) {
|
|
|
|
|
if (qobject_cast<MemoryDockWidget *>(dock)) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
QAction *action = new QAction(memoryWidget->windowTitle(), menu);
|
|
|
|
|
connect(action, &QAction::triggered, this, [memoryWidget, address]() {
|
2019-07-19 19:21:12 +00:00
|
|
|
|
memoryWidget->getSeekable()->seek(address);
|
|
|
|
|
memoryWidget->raiseMemoryWidget();
|
|
|
|
|
});
|
|
|
|
|
menu->addAction(action);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
menu->addSeparator();
|
|
|
|
|
auto createAddNewWidgetAction = [this, menu, address](QString label, MemoryWidgetType type) {
|
|
|
|
|
QAction *action = new QAction(label, menu);
|
2020-05-22 11:49:34 +00:00
|
|
|
|
connect(action, &QAction::triggered, this, [this, address, type]() {
|
2019-08-14 18:47:30 +00:00
|
|
|
|
addNewMemoryWidget(type, address, false);
|
2019-07-19 19:21:12 +00:00
|
|
|
|
});
|
|
|
|
|
menu->addAction(action);
|
|
|
|
|
};
|
|
|
|
|
createAddNewWidgetAction(tr("New disassembly"), MemoryWidgetType::Disassembly);
|
2020-07-30 08:41:23 +00:00
|
|
|
|
if (addressType != AddressTypeHint::Data) {
|
|
|
|
|
createAddNewWidgetAction(tr("New graph"), MemoryWidgetType::Graph);
|
|
|
|
|
}
|
2019-07-19 19:21:12 +00:00
|
|
|
|
createAddNewWidgetAction(tr("New hexdump"), MemoryWidgetType::Hexdump);
|
2020-08-29 05:15:47 +00:00
|
|
|
|
createAddNewWidgetAction(tr("New Decompiler"), MemoryWidgetType::Decompiler);
|
2019-07-19 19:21:12 +00:00
|
|
|
|
return menu;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void MainWindow::setCurrentMemoryWidget(MemoryDockWidget *memoryWidget)
|
|
|
|
|
{
|
|
|
|
|
if (memoryWidget->getSeekable()->isSynchronized()) {
|
2019-09-02 07:34:41 +00:00
|
|
|
|
lastSyncMemoryWidget = memoryWidget;
|
2019-07-19 19:21:12 +00:00
|
|
|
|
}
|
2019-09-02 07:34:41 +00:00
|
|
|
|
lastMemoryWidget = memoryWidget;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
MemoryDockWidget *MainWindow::getLastMemoryWidget()
|
|
|
|
|
{
|
|
|
|
|
return lastMemoryWidget;
|
2019-07-19 19:21:12 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
MemoryDockWidget *MainWindow::addNewMemoryWidget(MemoryWidgetType type, RVA address,
|
|
|
|
|
bool synchronized)
|
|
|
|
|
{
|
|
|
|
|
MemoryDockWidget *memoryWidget = nullptr;
|
|
|
|
|
switch (type) {
|
|
|
|
|
case MemoryWidgetType::Graph:
|
|
|
|
|
memoryWidget = new GraphWidget(this);
|
|
|
|
|
break;
|
|
|
|
|
case MemoryWidgetType::Hexdump:
|
|
|
|
|
memoryWidget = new HexdumpWidget(this);
|
|
|
|
|
break;
|
|
|
|
|
case MemoryWidgetType::Disassembly:
|
|
|
|
|
memoryWidget = new DisassemblyWidget(this);
|
|
|
|
|
break;
|
2019-09-06 05:40:20 +00:00
|
|
|
|
case MemoryWidgetType::Decompiler:
|
|
|
|
|
memoryWidget = new DecompilerWidget(this);
|
2019-07-19 19:21:12 +00:00
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
auto seekable = memoryWidget->getSeekable();
|
|
|
|
|
seekable->setSynchronization(synchronized);
|
|
|
|
|
seekable->seek(address);
|
|
|
|
|
addExtraWidget(memoryWidget);
|
|
|
|
|
return memoryWidget;
|
|
|
|
|
}
|
|
|
|
|
|
2019-12-10 07:34:21 +00:00
|
|
|
|
void MainWindow::initBackForwardMenu()
|
|
|
|
|
{
|
|
|
|
|
auto prepareButtonMenu = [this](QAction *action) -> QMenu* {
|
|
|
|
|
QToolButton *button = qobject_cast<QToolButton *>(ui->mainToolBar->widgetForAction(action));
|
|
|
|
|
if (!button) {
|
|
|
|
|
return nullptr;
|
|
|
|
|
}
|
|
|
|
|
QMenu *menu = new QMenu(button);
|
|
|
|
|
button->setMenu(menu);
|
|
|
|
|
button->setPopupMode(QToolButton::DelayedPopup);
|
|
|
|
|
button->setContextMenuPolicy(Qt::CustomContextMenu);
|
|
|
|
|
connect(button, &QWidget::customContextMenuRequested, button,
|
|
|
|
|
[menu, button] (const QPoint &pos) {
|
|
|
|
|
menu->exec(button->mapToGlobal(pos));
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
QFontMetrics metrics(fontMetrics());
|
|
|
|
|
// Roughly 10-16 lines depending on padding size, no need to calculate more precisely
|
|
|
|
|
menu->setMaximumHeight(metrics.lineSpacing() * 20);
|
|
|
|
|
|
|
|
|
|
menu->setToolTipsVisible(true);
|
|
|
|
|
return menu;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
if (auto menu = prepareButtonMenu(ui->actionBackward)) {
|
|
|
|
|
menu->setObjectName("historyMenu");
|
|
|
|
|
connect(menu, &QMenu::aboutToShow, menu, [this, menu]() {
|
|
|
|
|
updateHistoryMenu(menu, false);
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
if (auto menu = prepareButtonMenu(ui->actionForward)) {
|
|
|
|
|
menu->setObjectName("forwardHistoryMenu");
|
|
|
|
|
connect(menu, &QMenu::aboutToShow, menu, [this, menu]() {
|
|
|
|
|
updateHistoryMenu(menu, true);
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void MainWindow::updateHistoryMenu(QMenu *menu, bool redo)
|
|
|
|
|
{
|
|
|
|
|
// Not too long so that whole screen doesn't get covered,
|
|
|
|
|
// not too short so that reasonable length c++ names can be seen most of the time
|
|
|
|
|
const int MAX_NAME_LENGTH = 64;
|
|
|
|
|
|
|
|
|
|
auto hist = Core()->cmdj("sj");
|
|
|
|
|
bool history = true;
|
|
|
|
|
QList<QAction *> actions;
|
|
|
|
|
for (auto item : Core()->cmdj("sj").array()) {
|
|
|
|
|
QJsonObject obj = item.toObject();
|
|
|
|
|
QString name = obj["name"].toString();
|
|
|
|
|
RVA offset = obj["offset"].toVariant().toULongLong();
|
|
|
|
|
bool current = obj["current"].toBool(false);
|
|
|
|
|
if (current) {
|
|
|
|
|
history = false;
|
|
|
|
|
}
|
|
|
|
|
if (history != redo || current) { // Include current in both directions
|
|
|
|
|
QString addressString = RAddressString(offset);
|
|
|
|
|
|
|
|
|
|
QString toolTip = QString("%1 %2").arg(addressString, name); // show non truncated name in tooltip
|
|
|
|
|
|
|
|
|
|
name.truncate(MAX_NAME_LENGTH); // TODO:#1904 use common name shortening function
|
|
|
|
|
QString label = QString("%1 (%2)").arg(name, addressString);
|
|
|
|
|
if (current) {
|
|
|
|
|
label = QString("current position (%1)").arg(addressString);
|
|
|
|
|
}
|
|
|
|
|
QAction *action = new QAction(label, menu);
|
|
|
|
|
action->setToolTip(toolTip);
|
|
|
|
|
actions.push_back(action);
|
|
|
|
|
if (current) {
|
|
|
|
|
QFont font;
|
|
|
|
|
font.setBold(true);
|
|
|
|
|
action->setFont(font);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (!redo) {
|
|
|
|
|
std::reverse(actions.begin(), actions.end());
|
|
|
|
|
}
|
|
|
|
|
menu->clear();
|
|
|
|
|
menu->addActions(actions);
|
|
|
|
|
int steps = 0;
|
|
|
|
|
for (QAction *item : menu->actions()) {
|
|
|
|
|
if (redo) {
|
|
|
|
|
connect(item, &QAction::triggered, item, [steps]() {
|
|
|
|
|
for (int i = 0; i < steps; i++) {
|
|
|
|
|
Core()->seekNext();
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
} else {
|
|
|
|
|
connect(item, &QAction::triggered, item, [steps]() {
|
|
|
|
|
for (int i = 0; i < steps; i++) {
|
|
|
|
|
Core()->seekPrev();
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
++steps;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
2020-05-22 11:49:34 +00:00
|
|
|
|
void MainWindow::updateLayoutsMenu()
|
2019-06-18 13:02:41 +00:00
|
|
|
|
{
|
2020-05-22 11:49:34 +00:00
|
|
|
|
ui->menuLayouts->clear();
|
|
|
|
|
for (auto it = layouts.begin(), end = layouts.end(); it != end; ++it) {
|
|
|
|
|
QString name = it.key();
|
|
|
|
|
if (isBuiltinLayoutName(name)) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
auto action = new QAction(it.key(), ui->menuLayouts);
|
|
|
|
|
connect(action, &QAction::triggered, this, [this, name]() {
|
|
|
|
|
setViewLayout(getViewLayout(name));
|
2019-06-18 13:02:41 +00:00
|
|
|
|
});
|
2020-05-22 11:49:34 +00:00
|
|
|
|
ui->menuLayouts->addAction(action);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void MainWindow::saveNamedLayout()
|
|
|
|
|
{
|
|
|
|
|
bool ok = false;
|
|
|
|
|
QString name;
|
2020-05-23 22:12:32 +00:00
|
|
|
|
QStringList names = layouts.keys();
|
|
|
|
|
names.removeAll(LAYOUT_DEBUG);
|
|
|
|
|
names.removeAll(LAYOUT_DEFAULT);
|
2020-05-22 11:49:34 +00:00
|
|
|
|
while (name.isEmpty() || isBuiltinLayoutName(name)) {
|
|
|
|
|
if (ok) {
|
|
|
|
|
QMessageBox::warning(this, tr("Save layout error"), tr("'%1' is not a valid name.").arg(name));
|
|
|
|
|
}
|
2020-05-23 22:12:32 +00:00
|
|
|
|
name = QInputDialog::getItem(this, tr("Save layout"), tr("Enter name"), names, -1, true, &ok);
|
2020-05-22 11:49:34 +00:00
|
|
|
|
if (!ok) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
layouts[name] = getViewLayout();
|
|
|
|
|
updateLayoutsMenu();
|
|
|
|
|
saveSettings();
|
|
|
|
|
}
|
|
|
|
|
|
2020-05-23 22:12:32 +00:00
|
|
|
|
void MainWindow::manageLayouts()
|
|
|
|
|
{
|
|
|
|
|
LayoutManager layoutManger(layouts, this);
|
|
|
|
|
layoutManger.exec();
|
|
|
|
|
updateLayoutsMenu();
|
|
|
|
|
}
|
|
|
|
|
|
2020-05-22 11:49:34 +00:00
|
|
|
|
void MainWindow::addWidget(CutterDockWidget *widget)
|
|
|
|
|
{
|
|
|
|
|
dockWidgets.push_back(widget);
|
2019-07-19 19:21:12 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void MainWindow::addMemoryDockWidget(MemoryDockWidget *widget)
|
|
|
|
|
{
|
|
|
|
|
connect(widget, &QDockWidget::visibilityChanged, this, [this, widget](bool visibility) {
|
|
|
|
|
if (visibility) {
|
|
|
|
|
setCurrentMemoryWidget(widget);
|
|
|
|
|
}
|
|
|
|
|
});
|
2017-03-29 10:18:37 +00:00
|
|
|
|
}
|
|
|
|
|
|
2020-05-22 11:49:34 +00:00
|
|
|
|
void MainWindow::removeWidget(CutterDockWidget *widget)
|
2019-06-18 18:22:26 +00:00
|
|
|
|
{
|
|
|
|
|
dockWidgets.removeAll(widget);
|
2020-05-22 11:49:34 +00:00
|
|
|
|
pluginDocks.removeAll(widget);
|
2019-09-02 07:34:41 +00:00
|
|
|
|
if (lastSyncMemoryWidget == widget) {
|
|
|
|
|
lastSyncMemoryWidget = nullptr;
|
|
|
|
|
}
|
2019-07-19 19:21:12 +00:00
|
|
|
|
if (lastMemoryWidget == widget) {
|
|
|
|
|
lastMemoryWidget = nullptr;
|
|
|
|
|
}
|
2019-06-18 18:22:26 +00:00
|
|
|
|
}
|
|
|
|
|
|
2018-05-25 14:30:59 +00:00
|
|
|
|
void MainWindow::showZenDocks()
|
|
|
|
|
{
|
|
|
|
|
const QList<QDockWidget *> zenDocks = { functionsDock,
|
2018-09-30 20:00:44 +00:00
|
|
|
|
dashboardDock,
|
2018-05-25 14:30:59 +00:00
|
|
|
|
stringsDock,
|
2019-05-15 18:45:16 +00:00
|
|
|
|
searchDock,
|
2019-06-18 13:02:41 +00:00
|
|
|
|
importsDock
|
2018-07-18 10:15:10 +00:00
|
|
|
|
};
|
2020-05-22 11:49:34 +00:00
|
|
|
|
functionDockWidthToRestore = functionsDock->maximumWidth();
|
2019-06-18 13:02:41 +00:00
|
|
|
|
functionsDock->setMaximumWidth(200);
|
2018-05-25 14:30:59 +00:00
|
|
|
|
for (auto w : dockWidgets) {
|
2019-06-18 13:02:41 +00:00
|
|
|
|
if (zenDocks.contains(w) ||
|
2020-05-22 11:49:34 +00:00
|
|
|
|
isExtraMemoryWidget(w)) {
|
2018-05-25 14:30:59 +00:00
|
|
|
|
w->show();
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-05-22 11:49:34 +00:00
|
|
|
|
dashboardDock->raise();
|
2018-05-25 14:30:59 +00:00
|
|
|
|
}
|
|
|
|
|
|
2018-06-12 15:42:38 +00:00
|
|
|
|
void MainWindow::showDebugDocks()
|
|
|
|
|
{
|
|
|
|
|
const QList<QDockWidget *> debugDocks = { functionsDock,
|
2018-07-18 10:15:10 +00:00
|
|
|
|
stringsDock,
|
|
|
|
|
searchDock,
|
|
|
|
|
stackDock,
|
|
|
|
|
registersDock,
|
|
|
|
|
backtraceDock,
|
2019-10-22 15:51:56 +00:00
|
|
|
|
threadsDock,
|
2018-07-18 10:15:10 +00:00
|
|
|
|
memoryMapDock,
|
|
|
|
|
breakpointDock
|
2018-06-12 15:42:38 +00:00
|
|
|
|
};
|
2020-05-22 11:49:34 +00:00
|
|
|
|
functionDockWidthToRestore = functionsDock->maximumWidth();
|
2019-06-18 13:02:41 +00:00
|
|
|
|
functionsDock->setMaximumWidth(200);
|
2020-05-22 11:49:34 +00:00
|
|
|
|
auto registerWidth = qhelpers::forceWidth(registersDock, std::min(500, this->width() / 4));
|
|
|
|
|
auto registerHeight = qhelpers::forceHeight(registersDock, std::max(100, height() / 2));
|
|
|
|
|
QDockWidget *widgetToFocus = nullptr;
|
2018-06-12 15:42:38 +00:00
|
|
|
|
for (auto w : dockWidgets) {
|
2019-06-18 13:02:41 +00:00
|
|
|
|
if (debugDocks.contains(w) ||
|
2020-05-22 11:49:34 +00:00
|
|
|
|
isExtraMemoryWidget(w)) {
|
2018-06-12 15:42:38 +00:00
|
|
|
|
w->show();
|
|
|
|
|
}
|
2020-05-22 11:49:34 +00:00
|
|
|
|
if (qobject_cast<DisassemblyWidget*>(w)) {
|
|
|
|
|
widgetToFocus = w;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
registerHeight.restoreHeight(registersDock);
|
|
|
|
|
registerWidth.restoreWidth(registersDock);
|
|
|
|
|
if (widgetToFocus) {
|
|
|
|
|
widgetToFocus->raise();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void MainWindow::dockOnMainArea(QDockWidget *widget)
|
|
|
|
|
{
|
|
|
|
|
QDockWidget* best = nullptr;
|
|
|
|
|
float bestScore = 1;
|
|
|
|
|
// choose best existing area for placing the new widget
|
|
|
|
|
for (auto dock : dockWidgets) {
|
|
|
|
|
if (dock->isHidden() || dock == widget ||
|
|
|
|
|
dock->isFloating() || // tabifying onto floating dock using code doesn't work well
|
|
|
|
|
dock->parentWidget() != this) { // floating group isn't considered floating
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
float newScore = 0;
|
|
|
|
|
if (isExtraMemoryWidget(dock)) {
|
|
|
|
|
newScore += 10000000; // prefer existing disssasembly and graph widgets
|
|
|
|
|
}
|
|
|
|
|
newScore += dock->width() * dock->height(); // the bigger the better
|
|
|
|
|
|
|
|
|
|
if (newScore > bestScore) {
|
|
|
|
|
bestScore = newScore;
|
|
|
|
|
best = dock;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (best) {
|
|
|
|
|
tabifyDockWidget(best, widget);
|
|
|
|
|
} else {
|
|
|
|
|
addDockWidget(Qt::TopDockWidgetArea, widget, Qt::Orientation::Horizontal);
|
2018-06-12 15:42:38 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-10-22 18:06:30 +00:00
|
|
|
|
void MainWindow::enableDebugWidgetsMenu(bool enable)
|
|
|
|
|
{
|
2020-01-29 16:58:05 +00:00
|
|
|
|
for (QAction *action : ui->menuAddDebugWidgets->actions()) {
|
|
|
|
|
// The breakpoints menu should be available outside of debug
|
|
|
|
|
if (!action->text().contains("Breakpoints")) {
|
|
|
|
|
action->setEnabled(enable);
|
|
|
|
|
}
|
|
|
|
|
}
|
2018-10-22 18:06:30 +00:00
|
|
|
|
}
|
|
|
|
|
|
2020-05-22 11:49:34 +00:00
|
|
|
|
CutterLayout MainWindow::getViewLayout()
|
2018-05-25 14:30:59 +00:00
|
|
|
|
{
|
2020-05-22 11:49:34 +00:00
|
|
|
|
CutterLayout layout;
|
|
|
|
|
layout.geometry = saveGeometry();
|
|
|
|
|
layout.state = saveState();
|
|
|
|
|
|
|
|
|
|
for (auto dock : dockWidgets) {
|
|
|
|
|
QVariantMap properties;
|
|
|
|
|
if (auto cutterDock = qobject_cast<CutterDockWidget *>(dock)) {
|
|
|
|
|
properties = cutterDock->serializeViewProprties();
|
|
|
|
|
}
|
|
|
|
|
layout.viewProperties.insert(dock->objectName(), std::move(properties));
|
|
|
|
|
}
|
|
|
|
|
return layout;
|
2018-05-25 14:30:59 +00:00
|
|
|
|
}
|
|
|
|
|
|
2020-05-22 11:49:34 +00:00
|
|
|
|
CutterLayout MainWindow::getViewLayout(const QString &name)
|
2019-01-14 08:17:10 +00:00
|
|
|
|
{
|
2020-05-22 11:49:34 +00:00
|
|
|
|
auto it = layouts.find(name);
|
|
|
|
|
if (it != layouts.end()) {
|
|
|
|
|
return *it;
|
|
|
|
|
}
|
|
|
|
|
return {};
|
2019-01-14 08:17:10 +00:00
|
|
|
|
}
|
|
|
|
|
|
2020-05-22 11:49:34 +00:00
|
|
|
|
void MainWindow::setViewLayout(const CutterLayout &layout)
|
2018-06-12 15:42:38 +00:00
|
|
|
|
{
|
2020-05-22 11:49:34 +00:00
|
|
|
|
bool isDefault = layout.state.isEmpty() || layout.geometry.isEmpty();
|
|
|
|
|
bool isDebug = Core()->currentlyDebugging;
|
|
|
|
|
|
|
|
|
|
// make a copy to avoid iterating over container from which items are being removed
|
|
|
|
|
auto widgetsToClose = dockWidgets;
|
|
|
|
|
|
|
|
|
|
for (auto dock : widgetsToClose) {
|
|
|
|
|
dock->hide();
|
|
|
|
|
dock->close();
|
|
|
|
|
dock->setFloating(false); // tabifyDockWidget doesn't work if dock is floating
|
|
|
|
|
removeDockWidget(dock);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QStringList docksToCreate;
|
|
|
|
|
if (isDefault) {
|
|
|
|
|
docksToCreate = QStringList {
|
|
|
|
|
DisassemblyWidget::getWidgetType(),
|
|
|
|
|
GraphWidget::getWidgetType(),
|
2020-08-30 11:26:56 +00:00
|
|
|
|
HexdumpWidget::getWidgetType(),
|
|
|
|
|
DecompilerWidget::getWidgetType()
|
2020-05-22 11:49:34 +00:00
|
|
|
|
};
|
|
|
|
|
} else {
|
|
|
|
|
docksToCreate = layout.viewProperties.keys();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (const auto &it : docksToCreate) {
|
|
|
|
|
if (std::none_of(dockWidgets.constBegin(), dockWidgets.constEnd(),
|
|
|
|
|
[&it](QDockWidget * w) { return w->objectName() == it; })) {
|
|
|
|
|
auto className = it.split(';').at(0);
|
|
|
|
|
if (widgetTypeToConstructorMap.contains(className)) {
|
|
|
|
|
auto widget = widgetTypeToConstructorMap[className](this);
|
|
|
|
|
widget->setObjectName(it);
|
|
|
|
|
addExtraWidget(widget);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-06-12 15:42:38 +00:00
|
|
|
|
restoreDocks();
|
2020-05-22 11:49:34 +00:00
|
|
|
|
|
|
|
|
|
QList<QDockWidget *> newDocks;
|
|
|
|
|
|
|
|
|
|
for (auto dock : dockWidgets) {
|
|
|
|
|
auto properties = layout.viewProperties.find(dock->objectName());
|
|
|
|
|
if (properties != layout.viewProperties.end()) {
|
|
|
|
|
dock->deserializeViewProperties(*properties);
|
|
|
|
|
} else {
|
|
|
|
|
dock->deserializeViewProperties({}); // call with empty properties to reset the widget
|
|
|
|
|
newDocks.push_back(dock);
|
|
|
|
|
}
|
2020-07-19 19:00:05 +00:00
|
|
|
|
dock->ignoreVisibilityStatus(true);
|
2020-05-22 11:49:34 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!isDefault) {
|
|
|
|
|
restoreState(layout.state);
|
|
|
|
|
|
|
|
|
|
for (auto dock : newDocks) {
|
|
|
|
|
dock->hide(); // hide to prevent dockOnMainArea putting them on each other
|
|
|
|
|
}
|
|
|
|
|
for (auto dock : newDocks) {
|
|
|
|
|
dockOnMainArea(dock);
|
|
|
|
|
// Show any new docks by default.
|
|
|
|
|
// Showing new builtin docks helps discovering features added in latest release.
|
|
|
|
|
// Installing a new plugin hints that usre will likely want to use it.
|
|
|
|
|
dock->show();
|
|
|
|
|
}
|
2018-07-24 23:12:15 +00:00
|
|
|
|
} else {
|
2020-05-22 11:49:34 +00:00
|
|
|
|
if (isDebug) {
|
|
|
|
|
showDebugDocks();
|
|
|
|
|
} else {
|
|
|
|
|
showZenDocks();
|
|
|
|
|
}
|
2018-07-24 23:12:15 +00:00
|
|
|
|
}
|
2020-07-19 19:00:05 +00:00
|
|
|
|
|
|
|
|
|
for (auto dock : dockWidgets) {
|
|
|
|
|
dock->ignoreVisibilityStatus(false);
|
|
|
|
|
}
|
2020-07-21 09:40:53 +00:00
|
|
|
|
lastSyncMemoryWidget = nullptr;
|
|
|
|
|
lastMemoryWidget = nullptr;
|
2018-06-12 15:42:38 +00:00
|
|
|
|
}
|
|
|
|
|
|
2020-05-22 11:49:34 +00:00
|
|
|
|
void MainWindow::loadLayouts(QSettings &settings)
|
2019-06-18 13:02:41 +00:00
|
|
|
|
{
|
2020-05-22 11:49:34 +00:00
|
|
|
|
this->layouts.clear();
|
|
|
|
|
int size = settings.beginReadArray("layouts");
|
|
|
|
|
for (int i = 0; i < size; i++) {
|
|
|
|
|
CutterLayout layout;
|
|
|
|
|
settings.setArrayIndex(i);
|
|
|
|
|
QString name = settings.value("name", "layout").toString();
|
|
|
|
|
layout.geometry = settings.value("geometry").toByteArray();
|
|
|
|
|
layout.state = settings.value("state").toByteArray();
|
|
|
|
|
|
|
|
|
|
auto docks = settings.value("docks").toMap();
|
|
|
|
|
for (auto it = docks.begin(), end = docks.end(); it != end; it++) {
|
|
|
|
|
layout.viewProperties.insert(it.key(), it.value().toMap());
|
2019-06-18 13:02:41 +00:00
|
|
|
|
}
|
2020-05-22 11:49:34 +00:00
|
|
|
|
|
|
|
|
|
layouts.insert(name, std::move(layout));
|
2019-06-18 13:02:41 +00:00
|
|
|
|
}
|
2020-05-22 11:49:34 +00:00
|
|
|
|
settings.endArray();
|
|
|
|
|
updateLayoutsMenu();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void MainWindow::saveLayouts(QSettings &settings)
|
|
|
|
|
{
|
|
|
|
|
settings.beginWriteArray("layouts", layouts.size());
|
|
|
|
|
int arrayIndex = 0;
|
|
|
|
|
for (auto it = layouts.begin(), end = layouts.end(); it != end; ++it, ++arrayIndex) {
|
|
|
|
|
settings.setArrayIndex(arrayIndex);
|
|
|
|
|
settings.setValue("name", it.key());
|
|
|
|
|
auto &layout = it.value();
|
|
|
|
|
settings.setValue("state", layout.state);
|
|
|
|
|
settings.setValue("geometry", layout.geometry);
|
|
|
|
|
QVariantMap properties;
|
|
|
|
|
for (auto it = layout.viewProperties.begin(), end = layout.viewProperties.end(); it != end; ++it) {
|
|
|
|
|
properties.insert(it.key(), it.value());
|
|
|
|
|
}
|
|
|
|
|
settings.setValue("docks", properties);
|
2019-06-18 13:02:41 +00:00
|
|
|
|
}
|
2020-05-22 11:49:34 +00:00
|
|
|
|
settings.endArray();
|
2019-06-18 13:02:41 +00:00
|
|
|
|
}
|
|
|
|
|
|
2017-03-29 10:18:37 +00:00
|
|
|
|
|
2018-01-16 14:09:51 +00:00
|
|
|
|
void MainWindow::on_actionDefault_triggered()
|
2017-03-29 10:18:37 +00:00
|
|
|
|
{
|
2019-01-14 08:17:10 +00:00
|
|
|
|
if (core->currentlyDebugging) {
|
2020-05-22 11:49:34 +00:00
|
|
|
|
layouts[LAYOUT_DEBUG] = {};
|
|
|
|
|
setViewLayout(layouts[LAYOUT_DEBUG]);
|
2019-01-14 08:17:10 +00:00
|
|
|
|
} else {
|
2020-05-22 11:49:34 +00:00
|
|
|
|
layouts[LAYOUT_DEFAULT] = {};
|
|
|
|
|
setViewLayout(layouts[LAYOUT_DEFAULT]);
|
2019-01-14 08:17:10 +00:00
|
|
|
|
}
|
2017-03-29 10:18:37 +00:00
|
|
|
|
}
|
|
|
|
|
|
2019-01-14 08:17:10 +00:00
|
|
|
|
|
2018-06-20 09:24:28 +00:00
|
|
|
|
/**
|
|
|
|
|
* @brief MainWindow::on_actionNew_triggered
|
|
|
|
|
* Open a new Cutter session.
|
|
|
|
|
*/
|
2017-03-29 10:18:37 +00:00
|
|
|
|
void MainWindow::on_actionNew_triggered()
|
|
|
|
|
{
|
2018-06-20 09:24:28 +00:00
|
|
|
|
// Create a new Cutter process
|
2020-04-20 21:22:10 +00:00
|
|
|
|
static_cast<CutterApplication*>(qApp)->launchNewInstance();
|
2017-03-29 10:18:37 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void MainWindow::on_actionSave_triggered()
|
|
|
|
|
{
|
2020-12-01 11:45:26 +00:00
|
|
|
|
showProjectSaveError(saveProject(nullptr));
|
2017-03-29 10:18:37 +00:00
|
|
|
|
}
|
|
|
|
|
|
2017-10-21 19:20:10 +00:00
|
|
|
|
void MainWindow::on_actionSaveAs_triggered()
|
|
|
|
|
{
|
2020-12-01 11:45:26 +00:00
|
|
|
|
showProjectSaveError(saveProjectAs(nullptr));
|
2017-10-21 19:20:10 +00:00
|
|
|
|
}
|
|
|
|
|
|
2017-03-29 10:18:37 +00:00
|
|
|
|
void MainWindow::on_actionRun_Script_triggered()
|
|
|
|
|
{
|
|
|
|
|
QFileDialog dialog(this);
|
|
|
|
|
dialog.setFileMode(QFileDialog::ExistingFile);
|
|
|
|
|
dialog.setViewMode(QFileDialog::Detail);
|
|
|
|
|
dialog.setDirectory(QDir::home());
|
|
|
|
|
|
2019-02-07 10:52:08 +00:00
|
|
|
|
const QString &fileName = QDir::toNativeSeparators(dialog.getOpenFileName(this,
|
2020-12-13 09:33:08 +00:00
|
|
|
|
tr("Select Rizin script")));
|
2018-11-02 06:48:17 +00:00
|
|
|
|
if (fileName.isEmpty()) // Cancel was pressed
|
2017-04-09 20:36:17 +00:00
|
|
|
|
return;
|
2019-01-28 13:37:20 +00:00
|
|
|
|
|
|
|
|
|
RunScriptTask *runScriptTask = new RunScriptTask();
|
|
|
|
|
runScriptTask->setFileName(fileName);
|
|
|
|
|
|
|
|
|
|
AsyncTask::Ptr runScriptTaskPtr(runScriptTask);
|
|
|
|
|
|
|
|
|
|
AsyncTaskDialog *taskDialog = new AsyncTaskDialog(runScriptTaskPtr, this);
|
|
|
|
|
taskDialog->setInterruptOnClose(true);
|
|
|
|
|
taskDialog->setAttribute(Qt::WA_DeleteOnClose);
|
|
|
|
|
taskDialog->show();
|
|
|
|
|
|
|
|
|
|
Core()->getAsyncTaskManager()->start(runScriptTaskPtr);
|
2017-03-29 10:18:37 +00:00
|
|
|
|
}
|
|
|
|
|
|
2018-06-20 09:24:28 +00:00
|
|
|
|
/**
|
|
|
|
|
* @brief MainWindow::on_actionOpen_triggered
|
|
|
|
|
* Open a file as in "load (add) a file in current session".
|
|
|
|
|
*/
|
2020-04-07 13:18:41 +00:00
|
|
|
|
void MainWindow::on_actionMap_triggered()
|
2017-03-29 10:18:37 +00:00
|
|
|
|
{
|
2020-04-07 13:20:52 +00:00
|
|
|
|
MapFileDialog dialog(this);
|
2018-06-20 09:24:28 +00:00
|
|
|
|
dialog.exec();
|
2017-03-29 10:18:37 +00:00
|
|
|
|
}
|
|
|
|
|
|
2017-04-09 19:55:06 +00:00
|
|
|
|
void MainWindow::toggleResponsive(bool maybe)
|
|
|
|
|
{
|
2017-03-29 10:18:37 +00:00
|
|
|
|
this->responsive = maybe;
|
|
|
|
|
// Save options in settings
|
2017-04-03 00:18:09 +00:00
|
|
|
|
QSettings settings;
|
2017-03-29 10:18:37 +00:00
|
|
|
|
settings.setValue("responsive", this->responsive);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void MainWindow::on_actionTabs_on_Top_triggered()
|
|
|
|
|
{
|
|
|
|
|
this->on_actionTabs_triggered();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void MainWindow::on_actionReset_settings_triggered()
|
|
|
|
|
{
|
2017-05-19 07:45:26 +00:00
|
|
|
|
QMessageBox::StandardButton ret =
|
2017-09-25 12:55:41 +00:00
|
|
|
|
(QMessageBox::StandardButton)QMessageBox::question(this, APPNAME,
|
2019-02-07 10:52:08 +00:00
|
|
|
|
tr("Do you really want to clear all settings?"),
|
|
|
|
|
QMessageBox::Ok | QMessageBox::Cancel);
|
2018-03-21 20:32:32 +00:00
|
|
|
|
if (ret == QMessageBox::Ok) {
|
2017-12-03 12:10:09 +00:00
|
|
|
|
Config()->resetAll();
|
2020-05-22 11:49:34 +00:00
|
|
|
|
readSettings();
|
|
|
|
|
setViewLayout(getViewLayout(Core()->currentlyDebugging ? LAYOUT_DEBUG : LAYOUT_DEFAULT));
|
2017-03-29 10:18:37 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
2017-03-31 21:54:06 +00:00
|
|
|
|
|
|
|
|
|
void MainWindow::on_actionQuit_triggered()
|
|
|
|
|
{
|
|
|
|
|
close();
|
|
|
|
|
}
|
2017-04-12 21:04:39 +00:00
|
|
|
|
|
2018-03-08 09:11:19 +00:00
|
|
|
|
void MainWindow::on_actionBackward_triggered()
|
|
|
|
|
{
|
2018-08-27 11:16:48 +00:00
|
|
|
|
core->seekPrev();
|
2018-03-08 09:11:19 +00:00
|
|
|
|
}
|
|
|
|
|
|
2018-01-16 14:09:51 +00:00
|
|
|
|
void MainWindow::on_actionForward_triggered()
|
2018-03-08 09:11:19 +00:00
|
|
|
|
{
|
2018-08-27 11:16:48 +00:00
|
|
|
|
core->seekNext();
|
2018-01-16 14:09:51 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void MainWindow::on_actionDisasAdd_comment_triggered()
|
|
|
|
|
{
|
2019-03-23 06:32:31 +00:00
|
|
|
|
CommentsDialog c(this);
|
|
|
|
|
c.exec();
|
2018-01-16 14:09:51 +00:00
|
|
|
|
}
|
|
|
|
|
|
2017-05-26 08:52:17 +00:00
|
|
|
|
void MainWindow::on_actionRefresh_contents_triggered()
|
|
|
|
|
{
|
2017-11-19 12:56:10 +00:00
|
|
|
|
refreshAll();
|
2017-05-26 08:52:17 +00:00
|
|
|
|
}
|
2017-08-31 17:43:46 +00:00
|
|
|
|
|
2017-12-14 13:42:24 +00:00
|
|
|
|
void MainWindow::on_actionPreferences_triggered()
|
2017-08-31 17:43:46 +00:00
|
|
|
|
{
|
2020-08-22 16:49:21 +00:00
|
|
|
|
if (!findChild<PreferencesDialog*>()) {
|
|
|
|
|
auto dialog = new PreferencesDialog(this);
|
|
|
|
|
dialog->show();
|
|
|
|
|
}
|
2017-09-02 08:17:48 +00:00
|
|
|
|
}
|
2017-10-21 19:20:10 +00:00
|
|
|
|
|
2018-01-16 14:09:51 +00:00
|
|
|
|
void MainWindow::on_actionTabs_triggered()
|
|
|
|
|
{
|
|
|
|
|
tabsOnTop = !tabsOnTop;
|
|
|
|
|
setTabLocation();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void MainWindow::on_actionAbout_triggered()
|
|
|
|
|
{
|
|
|
|
|
AboutDialog *a = new AboutDialog(this);
|
2019-02-06 14:30:29 +00:00
|
|
|
|
a->setAttribute(Qt::WA_DeleteOnClose);
|
2018-01-16 14:09:51 +00:00
|
|
|
|
a->open();
|
|
|
|
|
}
|
2019-04-09 07:44:44 +00:00
|
|
|
|
|
2019-02-07 10:52:08 +00:00
|
|
|
|
void MainWindow::on_actionIssue_triggered()
|
|
|
|
|
{
|
2019-04-09 07:44:44 +00:00
|
|
|
|
openIssue();
|
2019-02-07 10:52:08 +00:00
|
|
|
|
}
|
2018-01-16 14:09:51 +00:00
|
|
|
|
|
2020-08-11 12:52:52 +00:00
|
|
|
|
void MainWindow::documentationClicked()
|
|
|
|
|
{
|
|
|
|
|
QDesktopServices::openUrl(QUrl("https://cutter.re/docs/user-docs"));
|
|
|
|
|
}
|
|
|
|
|
|
2018-01-16 14:09:51 +00:00
|
|
|
|
void MainWindow::on_actionRefresh_Panels_triggered()
|
|
|
|
|
{
|
|
|
|
|
this->refreshAll();
|
|
|
|
|
}
|
|
|
|
|
|
2020-08-10 16:12:38 +00:00
|
|
|
|
/**
|
|
|
|
|
* @brief A signal that creates an AsyncTask to re-analyze the current file
|
|
|
|
|
*/
|
2018-01-27 10:40:26 +00:00
|
|
|
|
void MainWindow::on_actionAnalyze_triggered()
|
|
|
|
|
{
|
2020-08-10 16:12:38 +00:00
|
|
|
|
auto *analTask = new AnalTask();
|
|
|
|
|
InitialOptions options;
|
|
|
|
|
options.analCmd = { {"aaa", "Auto analysis"} };
|
|
|
|
|
analTask->setOptions(options);
|
|
|
|
|
AsyncTask::Ptr analTaskPtr(analTask);
|
|
|
|
|
|
|
|
|
|
auto *taskDialog = new AsyncTaskDialog(analTaskPtr);
|
|
|
|
|
taskDialog->setInterruptOnClose(true);
|
|
|
|
|
taskDialog->setAttribute(Qt::WA_DeleteOnClose);
|
|
|
|
|
taskDialog->show();
|
|
|
|
|
connect(analTask, &AnalTask::finished, this, &MainWindow::refreshAll);
|
|
|
|
|
|
|
|
|
|
Core()->getAsyncTaskManager()->start(analTaskPtr);
|
2018-01-27 10:40:26 +00:00
|
|
|
|
}
|
|
|
|
|
|
2018-01-16 14:09:51 +00:00
|
|
|
|
void MainWindow::on_actionImportPDB_triggered()
|
|
|
|
|
{
|
|
|
|
|
QFileDialog dialog(this);
|
|
|
|
|
dialog.setWindowTitle(tr("Select PDB file"));
|
|
|
|
|
dialog.setNameFilters({ tr("PDB file (*.pdb)"), tr("All files (*)") });
|
|
|
|
|
|
2018-03-21 20:32:32 +00:00
|
|
|
|
if (!dialog.exec()) {
|
2018-01-16 14:09:51 +00:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2018-11-02 06:48:17 +00:00
|
|
|
|
const QString &pdbFile = QDir::toNativeSeparators(dialog.selectedFiles().first());
|
2018-01-16 14:09:51 +00:00
|
|
|
|
|
2018-03-21 20:32:32 +00:00
|
|
|
|
if (!pdbFile.isEmpty()) {
|
2018-08-27 11:16:48 +00:00
|
|
|
|
core->loadPDB(pdbFile);
|
|
|
|
|
core->message(tr("%1 loaded.").arg(pdbFile));
|
2018-11-17 10:10:44 +00:00
|
|
|
|
this->refreshAll();
|
2018-01-16 14:09:51 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-08-24 22:36:15 +00:00
|
|
|
|
void MainWindow::on_actionExport_as_code_triggered()
|
|
|
|
|
{
|
|
|
|
|
QStringList filters;
|
|
|
|
|
QMap<QString, QString> cmdMap;
|
|
|
|
|
|
|
|
|
|
filters << tr("C uin8_t array (*.c)");
|
|
|
|
|
cmdMap[filters.last()] = "pc";
|
|
|
|
|
filters << tr("C uin16_t array (*.c)");
|
|
|
|
|
cmdMap[filters.last()] = "pch";
|
|
|
|
|
filters << tr("C uin32_t array (*.c)");
|
|
|
|
|
cmdMap[filters.last()] = "pcw";
|
|
|
|
|
filters << tr("C uin64_t array (*.c)");
|
|
|
|
|
cmdMap[filters.last()] = "pcd";
|
|
|
|
|
filters << tr("C string (*.c)");
|
|
|
|
|
cmdMap[filters.last()] = "pcs";
|
|
|
|
|
filters << tr("Shell-script that reconstructs the bin (*.sh)");
|
|
|
|
|
cmdMap[filters.last()] = "pcS";
|
|
|
|
|
filters << tr("JSON array (*.json)");
|
|
|
|
|
cmdMap[filters.last()] = "pcj";
|
|
|
|
|
filters << tr("JavaScript array (*.js)");
|
|
|
|
|
cmdMap[filters.last()] = "pcJ";
|
|
|
|
|
filters << tr("Python array (*.py)");
|
|
|
|
|
cmdMap[filters.last()] = "pcp";
|
2020-12-16 10:59:23 +00:00
|
|
|
|
filters << tr("Print 'wx' Rizin commands (*.rz)");
|
2018-08-24 22:36:15 +00:00
|
|
|
|
cmdMap[filters.last()] = "pc*";
|
2018-10-25 21:22:34 +00:00
|
|
|
|
filters << tr("GAS .byte blob (*.asm, *.s)");
|
2018-08-24 22:36:15 +00:00
|
|
|
|
cmdMap[filters.last()] = "pca";
|
|
|
|
|
filters << tr(".bytes with instructions in comments (*.txt)");
|
|
|
|
|
cmdMap[filters.last()] = "pcA";
|
|
|
|
|
|
|
|
|
|
QFileDialog dialog(this, tr("Export as code"));
|
|
|
|
|
dialog.setAcceptMode(QFileDialog::AcceptSave);
|
|
|
|
|
dialog.setFileMode(QFileDialog::AnyFile);
|
|
|
|
|
dialog.setNameFilters(filters);
|
|
|
|
|
dialog.selectFile("dump");
|
|
|
|
|
dialog.setDefaultSuffix("c");
|
|
|
|
|
if (!dialog.exec())
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
QFile file(dialog.selectedFiles()[0]);
|
|
|
|
|
if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
|
|
|
|
|
qWarning() << "Can't open file";
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
TempConfig tempConfig;
|
|
|
|
|
tempConfig.set("io.va", false);
|
|
|
|
|
QTextStream fileOut(&file);
|
|
|
|
|
QString &cmd = cmdMap[dialog.selectedNameFilter()];
|
2020-03-20 18:14:21 +00:00
|
|
|
|
|
|
|
|
|
// Use cmd because cmdRaw would not handle such input
|
2018-08-24 22:36:15 +00:00
|
|
|
|
fileOut << Core()->cmd(cmd + " $s @ 0");
|
|
|
|
|
}
|
|
|
|
|
|
2019-05-08 11:38:25 +00:00
|
|
|
|
void MainWindow::on_actionGrouped_dock_dragging_triggered(bool checked)
|
|
|
|
|
{
|
2019-10-06 17:35:44 +00:00
|
|
|
|
#if QT_VERSION >= QT_VERSION_CHECK(5, 7, 0)
|
2019-05-08 11:38:25 +00:00
|
|
|
|
auto options = dockOptions();
|
|
|
|
|
options.setFlag(QMainWindow::DockOption::GroupedDragging, checked);
|
|
|
|
|
setDockOptions(options);
|
2019-10-06 17:35:44 +00:00
|
|
|
|
#else
|
|
|
|
|
Q_UNUSED(checked);
|
|
|
|
|
#endif
|
2019-05-08 11:38:25 +00:00
|
|
|
|
}
|
|
|
|
|
|
2019-05-30 08:41:14 +00:00
|
|
|
|
void MainWindow::seekToFunctionLastInstruction()
|
|
|
|
|
{
|
|
|
|
|
Core()->seek(Core()->getLastFunctionInstruction(Core()->getOffset()));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void MainWindow::seekToFunctionStart()
|
|
|
|
|
{
|
|
|
|
|
Core()->seek(Core()->getFunctionStart(Core()->getOffset()));
|
|
|
|
|
}
|
2018-08-24 22:36:15 +00:00
|
|
|
|
|
2019-12-13 17:30:55 +00:00
|
|
|
|
void MainWindow::toggleDebugView()
|
2018-06-12 15:42:38 +00:00
|
|
|
|
{
|
2020-05-22 11:49:34 +00:00
|
|
|
|
MemoryWidgetType memType = getMemoryWidgetTypeToRestore();
|
2019-12-13 17:30:55 +00:00
|
|
|
|
if (Core()->currentlyDebugging) {
|
2020-05-22 11:49:34 +00:00
|
|
|
|
layouts[LAYOUT_DEFAULT] = getViewLayout();
|
|
|
|
|
setViewLayout(getViewLayout(LAYOUT_DEBUG));
|
2019-12-13 17:30:55 +00:00
|
|
|
|
enableDebugWidgetsMenu(true);
|
|
|
|
|
} else {
|
2020-05-22 11:49:34 +00:00
|
|
|
|
layouts[LAYOUT_DEBUG] = getViewLayout();
|
|
|
|
|
setViewLayout(getViewLayout(LAYOUT_DEFAULT));
|
2019-12-13 17:30:55 +00:00
|
|
|
|
enableDebugWidgetsMenu(false);
|
|
|
|
|
}
|
2020-05-22 11:49:34 +00:00
|
|
|
|
showMemoryWidget(memType);
|
2018-06-12 15:42:38 +00:00
|
|
|
|
}
|
|
|
|
|
|
2018-05-13 07:50:01 +00:00
|
|
|
|
void MainWindow::mousePressEvent(QMouseEvent *event)
|
|
|
|
|
{
|
|
|
|
|
switch (event->button()) {
|
|
|
|
|
case Qt::BackButton:
|
2018-08-27 11:16:48 +00:00
|
|
|
|
core->seekPrev();
|
2018-05-13 07:50:01 +00:00
|
|
|
|
break;
|
|
|
|
|
case Qt::ForwardButton:
|
2018-08-27 11:16:48 +00:00
|
|
|
|
core->seekNext();
|
2018-05-13 07:50:01 +00:00
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool MainWindow::eventFilter(QObject *, QEvent *event)
|
|
|
|
|
{
|
|
|
|
|
if (event->type() == QEvent::MouseButtonPress) {
|
2018-07-18 10:15:10 +00:00
|
|
|
|
QMouseEvent *mouseEvent = static_cast<QMouseEvent *>(event);
|
2018-05-13 07:50:01 +00:00
|
|
|
|
if (mouseEvent->button() == Qt::ForwardButton || mouseEvent->button() == Qt::BackButton) {
|
|
|
|
|
mousePressEvent(mouseEvent);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2020-01-07 22:03:25 +00:00
|
|
|
|
bool MainWindow::event(QEvent *event)
|
|
|
|
|
{
|
|
|
|
|
if (event->type() == QEvent::FontChange
|
|
|
|
|
|| event->type() == QEvent::StyleChange
|
|
|
|
|
|| event->type() == QEvent::PaletteChange) {
|
|
|
|
|
#if QT_VERSION < QT_VERSION_CHECK(5,10,0)
|
|
|
|
|
QMetaObject::invokeMethod(Config(), "refreshFont", Qt::ConnectionType::QueuedConnection);
|
|
|
|
|
#else
|
|
|
|
|
QMetaObject::invokeMethod(Config(), &Configuration::refreshFont, Qt::ConnectionType::QueuedConnection);
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
return QMainWindow::event(event);
|
|
|
|
|
}
|
|
|
|
|
|
2019-03-06 20:30:39 +00:00
|
|
|
|
/**
|
|
|
|
|
* @brief Show a warning message box.
|
2019-02-11 19:05:53 +00:00
|
|
|
|
*
|
|
|
|
|
* This API can either be used in Cutter internals, or by Python plugins.
|
|
|
|
|
*/
|
|
|
|
|
void MainWindow::messageBoxWarning(QString title, QString message)
|
|
|
|
|
{
|
|
|
|
|
QMessageBox mb(this);
|
|
|
|
|
mb.setIcon(QMessageBox::Warning);
|
|
|
|
|
mb.setStandardButtons(QMessageBox::Ok);
|
|
|
|
|
mb.setWindowTitle(title);
|
|
|
|
|
mb.setText(message);
|
|
|
|
|
mb.exec();
|
|
|
|
|
}
|
2019-04-27 17:58:44 +00:00
|
|
|
|
|
|
|
|
|
/**
|
2019-12-08 07:19:58 +00:00
|
|
|
|
* @brief When theme changed, change icons which have a special version for the theme.
|
2019-04-27 17:58:44 +00:00
|
|
|
|
*/
|
|
|
|
|
void MainWindow::chooseThemeIcons()
|
|
|
|
|
{
|
|
|
|
|
// List of QActions which have alternative icons in different themes
|
|
|
|
|
const QList<QPair<void*, QString>> kSupportedIconsNames {
|
|
|
|
|
{ ui->actionForward, QStringLiteral("arrow_right.svg") },
|
|
|
|
|
{ ui->actionBackward, QStringLiteral("arrow_left.svg") },
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Set the correct icon for the QAction
|
|
|
|
|
qhelpers::setThemeIcons(kSupportedIconsNames, [](void *obj, const QIcon &icon) {
|
|
|
|
|
static_cast<QAction*>(obj)->setIcon(icon);
|
|
|
|
|
});
|
|
|
|
|
}
|
2019-10-12 05:50:10 +00:00
|
|
|
|
|
|
|
|
|
void MainWindow::onZoomIn()
|
|
|
|
|
{
|
|
|
|
|
Config()->setZoomFactor(Config()->getZoomFactor() + 0.1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void MainWindow::onZoomOut()
|
|
|
|
|
{
|
|
|
|
|
Config()->setZoomFactor(Config()->getZoomFactor() - 0.1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void MainWindow::onZoomReset()
|
|
|
|
|
{
|
|
|
|
|
Config()->setZoomFactor(1.0);
|
|
|
|
|
}
|
2020-01-31 10:13:28 +00:00
|
|
|
|
|
|
|
|
|
QMenu *MainWindow::getContextMenuExtensions(ContextMenuType type)
|
|
|
|
|
{
|
|
|
|
|
switch (type) {
|
|
|
|
|
case ContextMenuType::Disassembly:
|
|
|
|
|
return disassemblyContextMenuExtensions;
|
|
|
|
|
case ContextMenuType::Addressable:
|
|
|
|
|
return addressableContextMenuExtensions;
|
|
|
|
|
default:
|
|
|
|
|
return nullptr;
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-05-23 11:41:12 +00:00
|
|
|
|
|
|
|
|
|
void MainWindow::setAvailableIOModeOptions()
|
|
|
|
|
{
|
|
|
|
|
switch (ioModesController.getIOMode()) {
|
|
|
|
|
case IOModesController::Mode::WRITE:
|
|
|
|
|
ui->actionWriteMode->setChecked(true);
|
|
|
|
|
break;
|
|
|
|
|
case IOModesController::Mode::CACHE:
|
|
|
|
|
ui->actionCacheMode->setChecked(true);
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
ui->actionReadOnly->setChecked(true);
|
|
|
|
|
}
|
|
|
|
|
}
|