2017-10-01 19:09:42 +00:00
|
|
|
|
#include "MainWindow.h"
|
|
|
|
|
#include "ui_MainWindow.h"
|
2018-10-17 07:55:53 +00:00
|
|
|
|
#include "common/Helpers.h"
|
2017-03-29 10:18:37 +00:00
|
|
|
|
|
2018-11-17 10:10:44 +00:00
|
|
|
|
// Qt Headers
|
2018-01-09 12:09:25 +00:00
|
|
|
|
#include <QApplication>
|
2017-05-14 16:21:54 +00:00
|
|
|
|
#include <QComboBox>
|
|
|
|
|
#include <QCompleter>
|
2017-03-29 10:18:37 +00:00
|
|
|
|
#include <QDebug>
|
|
|
|
|
#include <QDesktopServices>
|
2017-05-14 16:21:54 +00:00
|
|
|
|
#include <QDir>
|
|
|
|
|
#include <QDockWidget>
|
|
|
|
|
#include <QFile>
|
2017-03-29 10:18:37 +00:00
|
|
|
|
#include <QFileDialog>
|
2017-05-14 16:21:54 +00:00
|
|
|
|
#include <QFont>
|
|
|
|
|
#include <QFontDialog>
|
|
|
|
|
#include <QLabel>
|
|
|
|
|
#include <QLineEdit>
|
|
|
|
|
#include <QList>
|
|
|
|
|
#include <QMessageBox>
|
|
|
|
|
#include <QProcess>
|
2017-03-29 10:18:37 +00:00
|
|
|
|
#include <QPropertyAnimation>
|
2017-05-14 16:21:54 +00:00
|
|
|
|
#include <QScrollBar>
|
|
|
|
|
#include <QSettings>
|
|
|
|
|
#include <QShortcut>
|
|
|
|
|
#include <QStringListModel>
|
2017-03-29 10:18:37 +00:00
|
|
|
|
#include <QStyledItemDelegate>
|
2017-05-14 16:21:54 +00:00
|
|
|
|
#include <QStyleFactory>
|
|
|
|
|
#include <QTextCursor>
|
2017-03-29 10:18:37 +00:00
|
|
|
|
#include <QtGlobal>
|
2017-05-14 16:21:54 +00:00
|
|
|
|
#include <QToolButton>
|
2017-03-29 10:18:37 +00:00
|
|
|
|
#include <QToolTip>
|
2017-04-13 15:36:20 +00:00
|
|
|
|
#include <QTreeWidgetItem>
|
2017-12-23 16:42:42 +00:00
|
|
|
|
#include <QSvgRenderer>
|
2017-05-14 16:21:54 +00:00
|
|
|
|
|
2018-11-17 10:10:44 +00:00
|
|
|
|
// Common Headers
|
2018-10-17 07:55:53 +00:00
|
|
|
|
#include "common/Highlighter.h"
|
|
|
|
|
#include "common/HexAsciiHighlighter.h"
|
|
|
|
|
#include "common/Helpers.h"
|
|
|
|
|
#include "common/SvgIconEngine.h"
|
|
|
|
|
#include "common/ProgressIndicator.h"
|
|
|
|
|
#include "common/TempConfig.h"
|
2017-04-13 15:36:20 +00:00
|
|
|
|
|
2018-11-17 10:10:44 +00:00
|
|
|
|
// Dialogs
|
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/SaveProjectDialog.h"
|
|
|
|
|
#include "dialogs/CommentsDialog.h"
|
|
|
|
|
#include "dialogs/AboutDialog.h"
|
|
|
|
|
#include "dialogs/RenameDialog.h"
|
|
|
|
|
#include "dialogs/preferences/PreferencesDialog.h"
|
|
|
|
|
#include "dialogs/OpenFileDialog.h"
|
|
|
|
|
|
2018-11-17 10:10:44 +00:00
|
|
|
|
// Widgets Headers
|
2017-12-13 22:38:46 +00:00
|
|
|
|
#include "widgets/DisassemblerGraphView.h"
|
2018-03-16 21:46:57 +00:00
|
|
|
|
#include "widgets/GraphWidget.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"
|
|
|
|
|
#include "widgets/SdbDock.h"
|
|
|
|
|
#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"
|
2017-12-13 17:36:00 +00:00
|
|
|
|
#include "widgets/JupyterWidget.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"
|
2017-04-13 15:36: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>
|
|
|
|
|
|
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
|
|
|
|
panelLock = false;
|
|
|
|
|
tabsOnTop = false;
|
2018-02-26 22:25:23 +00:00
|
|
|
|
configuration = Config();
|
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
|
|
|
|
|
2017-03-29 10:18:37 +00:00
|
|
|
|
/*
|
2018-11-01 22:23:01 +00:00
|
|
|
|
* Toolbar
|
|
|
|
|
*/
|
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);
|
|
|
|
|
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
|
2018-12-21 20:36:40 +00:00
|
|
|
|
ui->menuDebug->addAction(debugActions->actionStartEmul);
|
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);
|
|
|
|
|
spacer4->setMinimumSize(20, 20);
|
|
|
|
|
spacer4->setMaximumWidth(100);
|
|
|
|
|
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);
|
|
|
|
|
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);
|
|
|
|
|
spacer->setMinimumSize(20, 20);
|
|
|
|
|
ui->mainToolBar->addWidget(spacer);
|
|
|
|
|
|
2018-06-24 19:12:02 +00:00
|
|
|
|
tasksProgressIndicator = new ProgressIndicator();
|
|
|
|
|
ui->mainToolBar->addWidget(tasksProgressIndicator);
|
|
|
|
|
|
|
|
|
|
QWidget *spacerEnd = new QWidget();
|
|
|
|
|
spacerEnd->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Expanding);
|
|
|
|
|
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);
|
2018-10-10 09:37:24 +00:00
|
|
|
|
QObject::connect(configuration, &Configuration::colorsUpdated, [this]() {
|
|
|
|
|
this->visualNavbar->updateGraphicsScene();
|
|
|
|
|
});
|
2017-03-29 10:18:37 +00:00
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Dock Widgets
|
|
|
|
|
*/
|
2018-02-04 14:32:18 +00:00
|
|
|
|
dockWidgets.reserve(20);
|
2017-10-11 11:22:30 +00:00
|
|
|
|
|
2018-03-16 21:46:57 +00:00
|
|
|
|
disassemblyDock = new DisassemblyWidget(this, ui->actionDisassembly);
|
|
|
|
|
hexdumpDock = new HexdumpWidget(this, ui->actionHexdump);
|
2019-01-13 15:41:08 +00:00
|
|
|
|
pseudocodeDock = new PseudocodeWidget(this, ui->actionPseudocode);
|
2018-03-16 21:46:57 +00:00
|
|
|
|
consoleDock = new ConsoleWidget(this, ui->actionConsole);
|
2017-12-10 16:55:42 +00:00
|
|
|
|
|
2017-10-11 21:07:32 +00:00
|
|
|
|
// Add graph view as dockable
|
2018-03-16 21:46:57 +00:00
|
|
|
|
graphDock = new GraphWidget(this, ui->actionGraph);
|
2017-12-10 16:55:42 +00:00
|
|
|
|
|
2019-01-13 15:41:08 +00:00
|
|
|
|
sectionsDock = new SectionsWidget(this, ui->actionSections);
|
2019-01-12 17:02:51 +00:00
|
|
|
|
//segmentsDock = new SegmentsWidget(this, ui->actionSegments);
|
|
|
|
|
//entrypointDock = new EntrypointWidget(this, ui->actionEntrypoints);
|
2018-03-16 21:46:57 +00:00
|
|
|
|
functionsDock = new FunctionsWidget(this, ui->actionFunctions);
|
2019-01-12 17:02:51 +00:00
|
|
|
|
//importsDock = new ImportsWidget(this, ui->actionImports);
|
|
|
|
|
//exportsDock = new ExportsWidget(this, ui->actionExports);
|
|
|
|
|
//headersDock = new HeadersWidget(this, ui->actionHeaders);
|
|
|
|
|
//zignaturesDock = new ZignaturesWidget(this, ui->actionZignatures);
|
|
|
|
|
//typesDock = new TypesWidget(this, ui->actionTypes);
|
|
|
|
|
//searchDock = new SearchWidget(this, ui->actionSearch);
|
|
|
|
|
//symbolsDock = new SymbolsWidget(this, ui->actionSymbols);
|
|
|
|
|
//relocsDock = new RelocsWidget(this, ui->actionRelocs);
|
2019-01-13 15:41:08 +00:00
|
|
|
|
commentsDock = new CommentsWidget(this, ui->actionComments);
|
2019-01-12 17:02:51 +00:00
|
|
|
|
//stringsDock = new StringsWidget(this, ui->actionStrings);
|
|
|
|
|
//flagsDock = new FlagsWidget(this, ui->actionFlags);
|
|
|
|
|
//stackDock = new StackWidget(this, ui->actionStack);
|
|
|
|
|
//backtraceDock = new BacktraceWidget(this, ui->actionBacktrace);
|
|
|
|
|
//registersDock = new RegistersWidget(this, ui->actionRegisters);
|
|
|
|
|
//memoryMapDock = new MemoryMapWidget(this, ui->actionMemoryMap);
|
|
|
|
|
//breakpointDock = new BreakpointWidget(this, ui->actionBreakpoint);
|
|
|
|
|
//registerRefsDock = new RegisterRefsWidget(this, ui->actionRegisterRefs);
|
2018-03-02 13:15:53 +00:00
|
|
|
|
#ifdef CUTTER_ENABLE_JUPYTER
|
2019-01-12 17:02:51 +00:00
|
|
|
|
//jupyterDock = new JupyterWidget(this, ui->actionJupyter);
|
2018-03-02 14:06:08 +00:00
|
|
|
|
#else
|
|
|
|
|
ui->actionJupyter->setEnabled(false);
|
|
|
|
|
ui->actionJupyter->setVisible(false);
|
2018-03-02 13:15:53 +00:00
|
|
|
|
#endif
|
2018-03-16 21:46:57 +00:00
|
|
|
|
dashboardDock = new Dashboard(this, ui->actionDashboard);
|
2019-01-12 17:02:51 +00:00
|
|
|
|
//sdbDock = new SdbDock(this, ui->actionSDBBrowser);
|
|
|
|
|
//classesDock = new ClassesWidget(this, ui->actionClasses);
|
|
|
|
|
//resourcesDock = new ResourcesWidget(this, ui->actionResources);
|
|
|
|
|
//vTablesDock = new VTablesWidget(this, ui->actionVTables);
|
2017-12-23 16:42:42 +00:00
|
|
|
|
|
2017-03-29 10:18:37 +00:00
|
|
|
|
|
2018-03-11 11:41:19 +00:00
|
|
|
|
// Set up dock widgets default layout
|
|
|
|
|
resetToDefaultLayout();
|
2018-10-22 18:06:30 +00:00
|
|
|
|
enableDebugWidgetsMenu(false);
|
2018-03-11 11:41:19 +00:00
|
|
|
|
|
2017-03-29 10:18:37 +00:00
|
|
|
|
// Restore saved settings
|
|
|
|
|
this->readSettings();
|
|
|
|
|
// TODO: Allow the user to select this option visually in the GUI settings
|
|
|
|
|
// Adjust the DockWidget areas
|
2017-04-09 19:55:06 +00:00
|
|
|
|
setCorner(Qt::TopLeftCorner, Qt::LeftDockWidgetArea);
|
2017-03-29 10:18:37 +00:00
|
|
|
|
//setCorner( Qt::TopRightCorner, Qt::RightDockWidgetArea );
|
2017-04-09 19:55:06 +00:00
|
|
|
|
setCorner(Qt::BottomLeftCorner, Qt::LeftDockWidgetArea);
|
2017-03-29 10:18:37 +00:00
|
|
|
|
//setCorner( Qt::BottomRightCorner, Qt::RightDockWidgetArea );
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Some global shortcuts
|
|
|
|
|
*/
|
|
|
|
|
// Period goes to command entry
|
2017-04-09 19:55:06 +00:00
|
|
|
|
QShortcut *cmd_shortcut = new QShortcut(QKeySequence(Qt::Key_Period), this);
|
2017-12-10 16:55:42 +00:00
|
|
|
|
connect(cmd_shortcut, SIGNAL(activated()), consoleDock, SLOT(focusInputLineEdit()));
|
2017-03-29 10:18:37 +00:00
|
|
|
|
|
|
|
|
|
// G and S goes to goto entry
|
2017-04-09 19:55:06 +00:00
|
|
|
|
QShortcut *goto_shortcut = new QShortcut(QKeySequence(Qt::Key_G), this);
|
2017-03-29 10:18:37 +00:00
|
|
|
|
connect(goto_shortcut, SIGNAL(activated()), this->omnibar, SLOT(setFocus()));
|
2017-04-09 19:55:06 +00:00
|
|
|
|
QShortcut *seek_shortcut = new QShortcut(QKeySequence(Qt::Key_S), this);
|
2017-03-29 10:18:37 +00:00
|
|
|
|
connect(seek_shortcut, SIGNAL(activated()), this->omnibar, SLOT(setFocus()));
|
|
|
|
|
|
2017-04-12 21:04:39 +00:00
|
|
|
|
QShortcut *refresh_shortcut = new QShortcut(QKeySequence(QKeySequence::Refresh), this);
|
2017-11-19 12:56:10 +00:00
|
|
|
|
connect(refresh_shortcut, SIGNAL(activated()), this, SLOT(refreshAll()));
|
2017-05-13 18:09:36 +00:00
|
|
|
|
|
2018-11-02 06:48:17 +00:00
|
|
|
|
connect(core, SIGNAL(projectSaved(bool, const QString &)), this, SLOT(projectSaved(bool, const QString &)));
|
2018-06-22 18:34:25 +00:00
|
|
|
|
|
2018-06-12 15:42:38 +00:00
|
|
|
|
connect(core, &CutterCore::changeDebugView, this, &MainWindow::changeDebugView);
|
|
|
|
|
connect(core, &CutterCore::changeDefinedView, this, &MainWindow::changeDefinedView);
|
|
|
|
|
|
2018-08-27 11:16:48 +00:00
|
|
|
|
connect(core, SIGNAL(newMessage(const QString &)),
|
|
|
|
|
this->consoleDock, SLOT(addOutput(const QString &)));
|
|
|
|
|
connect(core, SIGNAL(newDebugMessage(const QString &)),
|
|
|
|
|
this->consoleDock, SLOT(addDebugOutput(const QString &)));
|
|
|
|
|
|
2018-06-22 18:34:25 +00:00
|
|
|
|
updateTasksIndicator();
|
2018-07-18 10:15:10 +00:00
|
|
|
|
connect(core->getAsyncTaskManager(), &AsyncTaskManager::tasksChanged, this,
|
|
|
|
|
&MainWindow::updateTasksIndicator);
|
2018-06-26 07:40:08 +00:00
|
|
|
|
|
2018-11-01 22:23:01 +00:00
|
|
|
|
/* Setup plugins interfaces */
|
2018-08-27 11:16:48 +00:00
|
|
|
|
QList<CutterPlugin *> plugins = core->getCutterPlugins();
|
2018-06-26 07:40:08 +00:00
|
|
|
|
for (auto plugin : plugins) {
|
|
|
|
|
CutterDockWidget *pluginDock = plugin->setupInterface(this);
|
|
|
|
|
tabifyDockWidget(dashboardDock, pluginDock);
|
|
|
|
|
}
|
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
|
|
|
|
}
|
|
|
|
|
|
2018-05-25 14:30:59 +00:00
|
|
|
|
void MainWindow::on_actionExtraGraph_triggered()
|
|
|
|
|
{
|
|
|
|
|
QDockWidget *extraDock = new GraphWidget(this, 0);
|
|
|
|
|
addExtraWidget(extraDock);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void MainWindow::on_actionExtraHexdump_triggered()
|
|
|
|
|
{
|
|
|
|
|
QDockWidget *extraDock = new HexdumpWidget(this, 0);
|
|
|
|
|
addExtraWidget(extraDock);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void MainWindow::on_actionExtraDisassembly_triggered()
|
|
|
|
|
{
|
|
|
|
|
QDockWidget *extraDock = new DisassemblyWidget(this, 0);
|
|
|
|
|
addExtraWidget(extraDock);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void MainWindow::addExtraWidget(QDockWidget *extraDock)
|
|
|
|
|
{
|
|
|
|
|
addDockWidget(Qt::TopDockWidgetArea, extraDock);
|
|
|
|
|
auto restoreExtraDock = qhelpers::forceWidth(extraDock->widget(), 600);
|
|
|
|
|
qApp->processEvents();
|
|
|
|
|
restoreExtraDock.restoreWidth(extraDock->widget());
|
|
|
|
|
}
|
|
|
|
|
|
2018-08-18 16:04:45 +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
|
|
|
|
|
2017-12-14 12:45:03 +00:00
|
|
|
|
/* Prompt to load filename.r2 script */
|
2018-08-18 16:04:45 +00:00
|
|
|
|
if (options.script.isEmpty()) {
|
|
|
|
|
QString script = QString("%1.r2").arg(this->filename);
|
|
|
|
|
if (r_file_exists(script.toStdString().data())) {
|
|
|
|
|
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();
|
|
|
|
|
}
|
|
|
|
|
|
2018-02-06 18:23:01 +00:00
|
|
|
|
void MainWindow::displayNewFileDialog()
|
|
|
|
|
{
|
|
|
|
|
NewFileDialog *n = new NewFileDialog();
|
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
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void MainWindow::openProject(const QString &project_name)
|
|
|
|
|
{
|
|
|
|
|
QString filename = core->cmd("Pi " + project_name);
|
|
|
|
|
setFilename(filename.trimmed());
|
|
|
|
|
|
2017-10-21 19:20:10 +00:00
|
|
|
|
core->openProject(project_name);
|
2017-05-13 18:09:36 +00:00
|
|
|
|
|
|
|
|
|
initUI();
|
|
|
|
|
finalizeOpen();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void MainWindow::finalizeOpen()
|
|
|
|
|
{
|
|
|
|
|
core->getOpcodes();
|
2018-09-06 17:32:12 +00:00
|
|
|
|
core->updateSeek();
|
2018-08-27 11:16:48 +00:00
|
|
|
|
core->message(tr(" > Populating UI"));
|
2017-11-19 12:56:10 +00:00
|
|
|
|
refreshAll();
|
2017-05-13 18:09:36 +00:00
|
|
|
|
|
2018-08-27 11:16:48 +00:00
|
|
|
|
core->message(tr(" > Finished, happy reversing :)"));
|
2017-05-13 18:09:36 +00:00
|
|
|
|
// Add fortune message
|
2018-08-27 11:16:48 +00:00
|
|
|
|
core->message("\n" + core->cmd("fo"));
|
2017-05-13 18:09:36 +00:00
|
|
|
|
showMaximized();
|
|
|
|
|
}
|
|
|
|
|
|
2017-10-21 19:20:10 +00:00
|
|
|
|
bool MainWindow::saveProject(bool quit)
|
2017-05-13 18:09:36 +00:00
|
|
|
|
{
|
2017-10-21 19:20:10 +00:00
|
|
|
|
QString projectName = core->getConfig("prj.name");
|
2018-03-21 20:32:32 +00:00
|
|
|
|
if (projectName.isEmpty()) {
|
2017-10-21 19:20:10 +00:00
|
|
|
|
return saveProjectAs(quit);
|
2018-03-21 20:32:32 +00:00
|
|
|
|
} else {
|
2017-10-21 19:20:10 +00:00
|
|
|
|
core->saveProject(projectName);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool MainWindow::saveProjectAs(bool quit)
|
|
|
|
|
{
|
2017-11-03 17:22:54 +00:00
|
|
|
|
SaveProjectDialog dialog(quit, this);
|
2018-10-14 15:43:15 +00:00
|
|
|
|
return SaveProjectDialog::Rejected != dialog.exec();
|
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)
|
|
|
|
|
{
|
2017-09-25 12:55:41 +00:00
|
|
|
|
QMessageBox::StandardButton ret = QMessageBox::question(this, APPNAME,
|
2018-03-21 20:32:32 +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;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (ret == QMessageBox::Save && !saveProject(true)) {
|
2017-03-29 10:18:37 +00:00
|
|
|
|
event->ignore();
|
2018-10-12 07:08:04 +00:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void MainWindow::readSettings()
|
|
|
|
|
{
|
2017-04-03 00:18:09 +00:00
|
|
|
|
QSettings settings;
|
2017-03-29 10:18:37 +00:00
|
|
|
|
QByteArray geo = settings.value("geometry", QByteArray()).toByteArray();
|
|
|
|
|
restoreGeometry(geo);
|
|
|
|
|
QByteArray state = settings.value("state", QByteArray()).toByteArray();
|
|
|
|
|
restoreState(state);
|
|
|
|
|
this->responsive = settings.value("responsive").toBool();
|
2017-12-17 13:49:03 +00:00
|
|
|
|
panelLock = settings.value("panelLock").toBool();
|
|
|
|
|
setPanelLock();
|
|
|
|
|
tabsOnTop = settings.value("tabsOnTop").toBool();
|
|
|
|
|
setTabLocation();
|
2018-07-23 23:12:36 +00:00
|
|
|
|
QPoint pos = settings.value("pos", QPoint(200, 200)).toPoint();
|
|
|
|
|
QSize size = settings.value("size", QSize(400, 400)).toSize();
|
|
|
|
|
resize(size);
|
|
|
|
|
move(pos);
|
2017-12-21 15:47:36 +00:00
|
|
|
|
updateDockActionsChecked();
|
2017-12-17 13:49:03 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void MainWindow::saveSettings()
|
|
|
|
|
{
|
|
|
|
|
QSettings settings;
|
|
|
|
|
settings.setValue("geometry", saveGeometry());
|
|
|
|
|
settings.setValue("size", size());
|
|
|
|
|
settings.setValue("pos", pos());
|
|
|
|
|
settings.setValue("state", saveState());
|
|
|
|
|
settings.setValue("panelLock", panelLock);
|
|
|
|
|
settings.setValue("tabsOnTop", tabsOnTop);
|
|
|
|
|
}
|
|
|
|
|
|
2018-07-23 23:12:36 +00:00
|
|
|
|
void MainWindow::readDebugSettings()
|
|
|
|
|
{
|
|
|
|
|
QSettings settings;
|
|
|
|
|
QByteArray geo = settings.value("debug.geometry", QByteArray()).toByteArray();
|
|
|
|
|
restoreGeometry(geo);
|
|
|
|
|
QByteArray state = settings.value("debug.state", QByteArray()).toByteArray();
|
|
|
|
|
restoreState(state);
|
|
|
|
|
QPoint pos = settings.value("pos", QPoint(200, 200)).toPoint();
|
|
|
|
|
QSize size = settings.value("size", QSize(400, 400)).toSize();
|
|
|
|
|
resize(size);
|
|
|
|
|
move(pos);
|
|
|
|
|
updateDockActionsChecked();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void MainWindow::saveDebugSettings()
|
|
|
|
|
{
|
|
|
|
|
QSettings settings;
|
|
|
|
|
settings.setValue("debug.geometry", saveGeometry());
|
|
|
|
|
settings.setValue("debug.state", saveState());
|
|
|
|
|
settings.setValue("size", size());
|
|
|
|
|
settings.setValue("pos", pos());
|
|
|
|
|
}
|
|
|
|
|
|
2017-12-17 13:49:03 +00:00
|
|
|
|
void MainWindow::setPanelLock()
|
|
|
|
|
{
|
2018-03-21 20:32:32 +00:00
|
|
|
|
if (panelLock) {
|
2018-05-04 07:58:32 +00:00
|
|
|
|
for (QDockWidget *dockWidget : findChildren<QDockWidget *>()) {
|
2017-12-17 13:49:03 +00:00
|
|
|
|
dockWidget->setFeatures(QDockWidget::NoDockWidgetFeatures);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ui->actionLock->setChecked(false);
|
2018-03-21 20:32:32 +00:00
|
|
|
|
} else {
|
2018-05-04 07:58:32 +00:00
|
|
|
|
for (QDockWidget *dockWidget : findChildren<QDockWidget *>()) {
|
2017-12-17 13:49:03 +00:00
|
|
|
|
dockWidget->setFeatures(QDockWidget::AllDockWidgetFeatures);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ui->actionLock->setChecked(true);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void MainWindow::lockUnlock_Docks(bool what)
|
|
|
|
|
{
|
2018-03-21 20:32:32 +00:00
|
|
|
|
if (what) {
|
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 *>()) {
|
2017-03-29 10:18:37 +00:00
|
|
|
|
dockWidget->setFeatures(QDockWidget::AllDockWidgetFeatures);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void MainWindow::restoreDocks()
|
|
|
|
|
{
|
2017-12-10 16:55:42 +00:00
|
|
|
|
// In the upper half the functions are the first widget
|
2019-01-12 17:02:51 +00:00
|
|
|
|
//addDockWidget(Qt::TopDockWidgetArea, functionsDock);
|
2017-10-13 13:53:23 +00:00
|
|
|
|
|
2018-12-24 15:25:55 +00:00
|
|
|
|
// Function | Dashboard
|
2017-11-03 17:22:54 +00:00
|
|
|
|
splitDockWidget(functionsDock, dashboardDock, Qt::Horizontal);
|
|
|
|
|
|
2017-12-10 16:55:42 +00:00
|
|
|
|
// In the lower half the console is the first widget
|
|
|
|
|
addDockWidget(Qt::BottomDockWidgetArea, consoleDock);
|
|
|
|
|
|
|
|
|
|
// Console | Sections
|
2019-01-12 17:02:51 +00:00
|
|
|
|
//splitDockWidget(consoleDock, sectionsDock, Qt::Horizontal);
|
|
|
|
|
//splitDockWidget(consoleDock, segmentsDock, Qt::Horizontal);
|
2017-11-03 17:22:54 +00:00
|
|
|
|
|
2018-01-09 12:09:25 +00:00
|
|
|
|
// Tabs for center (must be applied after splitDockWidget())
|
2019-01-12 17:02:51 +00:00
|
|
|
|
//tabifyDockWidget(sectionsDock, commentsDock);
|
|
|
|
|
//tabifyDockWidget(segmentsDock, commentsDock);
|
2017-11-03 17:22:54 +00:00
|
|
|
|
tabifyDockWidget(dashboardDock, disassemblyDock);
|
|
|
|
|
tabifyDockWidget(dashboardDock, graphDock);
|
|
|
|
|
tabifyDockWidget(dashboardDock, hexdumpDock);
|
2019-01-12 17:02:51 +00:00
|
|
|
|
//tabifyDockWidget(dashboardDock, pseudocodeDock);
|
|
|
|
|
//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);
|
2018-06-06 11:05:20 +00:00
|
|
|
|
|
|
|
|
|
// Add Stack, Registers and Backtrace vertically stacked
|
2019-01-12 17:02:51 +00:00
|
|
|
|
//addDockWidget(Qt::TopDockWidgetArea, stackDock);
|
|
|
|
|
//splitDockWidget(stackDock, registersDock, Qt::Vertical);
|
|
|
|
|
//tabifyDockWidget(stackDock, backtraceDock);
|
2018-07-19 14:35:46 +00:00
|
|
|
|
// MemoryMap/Breakpoint/RegRefs widget goes in the center tabs
|
2019-01-12 17:02:51 +00:00
|
|
|
|
//tabifyDockWidget(dashboardDock, memoryMapDock);
|
|
|
|
|
//tabifyDockWidget(dashboardDock, breakpointDock);
|
|
|
|
|
//tabifyDockWidget(dashboardDock, registerRefsDock);
|
2018-03-02 13:15:53 +00:00
|
|
|
|
#ifdef CUTTER_ENABLE_JUPYTER
|
2019-01-12 17:02:51 +00:00
|
|
|
|
//tabifyDockWidget(dashboardDock, jupyterDock);
|
2018-03-02 13:15:53 +00:00
|
|
|
|
#endif
|
2017-11-03 17:22:54 +00:00
|
|
|
|
|
2017-12-21 15:47:36 +00:00
|
|
|
|
updateDockActionsChecked();
|
2017-03-29 10:18:37 +00:00
|
|
|
|
}
|
|
|
|
|
|
2017-11-03 17:22:54 +00:00
|
|
|
|
|
2017-03-29 10:18:37 +00:00
|
|
|
|
void MainWindow::hideAllDocks()
|
|
|
|
|
{
|
2018-03-21 20:32:32 +00:00
|
|
|
|
for (auto w : dockWidgets) {
|
2018-01-09 12:09:25 +00:00
|
|
|
|
removeDockWidget(w);
|
2017-04-13 15:36:20 +00:00
|
|
|
|
}
|
2017-12-21 15:47:36 +00:00
|
|
|
|
|
|
|
|
|
updateDockActionsChecked();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void MainWindow::updateDockActionsChecked()
|
|
|
|
|
{
|
2018-03-21 20:32:32 +00:00
|
|
|
|
for (auto i = dockWidgetActions.constBegin(); i != dockWidgetActions.constEnd(); i++) {
|
2017-12-21 15:47:36 +00:00
|
|
|
|
i.key()->setChecked(!i.value()->isHidden());
|
|
|
|
|
}
|
2017-03-29 10:18:37 +00:00
|
|
|
|
}
|
|
|
|
|
|
2018-10-25 21:22:34 +00:00
|
|
|
|
void MainWindow::updateDockActionChecked(QAction *action)
|
2018-10-16 06:25:09 +00:00
|
|
|
|
{
|
|
|
|
|
action->setChecked(!dockWidgetActions[action]->isHidden());
|
|
|
|
|
}
|
|
|
|
|
|
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,
|
|
|
|
|
graphDock,
|
|
|
|
|
disassemblyDock,
|
|
|
|
|
hexdumpDock,
|
2018-09-30 20:00:44 +00:00
|
|
|
|
searchDock,
|
|
|
|
|
importsDock,
|
|
|
|
|
#ifdef CUTTER_ENABLE_JUPYTER
|
|
|
|
|
jupyterDock
|
|
|
|
|
#endif
|
2018-07-18 10:15:10 +00:00
|
|
|
|
};
|
2018-05-25 14:30:59 +00:00
|
|
|
|
for (auto w : dockWidgets) {
|
|
|
|
|
if (zenDocks.contains(w)) {
|
|
|
|
|
w->show();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
updateDockActionsChecked();
|
|
|
|
|
}
|
|
|
|
|
|
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,
|
|
|
|
|
graphDock,
|
|
|
|
|
disassemblyDock,
|
|
|
|
|
hexdumpDock,
|
|
|
|
|
searchDock,
|
|
|
|
|
stackDock,
|
|
|
|
|
registersDock,
|
|
|
|
|
backtraceDock,
|
|
|
|
|
memoryMapDock,
|
|
|
|
|
breakpointDock
|
2018-06-12 15:42:38 +00:00
|
|
|
|
};
|
|
|
|
|
for (auto w : dockWidgets) {
|
|
|
|
|
if (debugDocks.contains(w)) {
|
|
|
|
|
w->show();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
updateDockActionsChecked();
|
|
|
|
|
}
|
|
|
|
|
|
2018-10-22 18:06:30 +00:00
|
|
|
|
void MainWindow::enableDebugWidgetsMenu(bool enable)
|
|
|
|
|
{
|
|
|
|
|
ui->menuAddDebugWidgets->setEnabled(enable);
|
|
|
|
|
}
|
|
|
|
|
|
2017-11-03 17:22:54 +00:00
|
|
|
|
void MainWindow::resetToDefaultLayout()
|
2018-05-25 14:30:59 +00:00
|
|
|
|
{
|
|
|
|
|
hideAllDocks();
|
|
|
|
|
restoreDocks();
|
|
|
|
|
showZenDocks();
|
2018-09-30 20:00:44 +00:00
|
|
|
|
dashboardDock->raise();
|
2018-05-25 14:30:59 +00:00
|
|
|
|
|
2018-09-30 20:00:44 +00:00
|
|
|
|
// Ugly workaround to set the default widths of functions docks
|
2018-05-25 14:30:59 +00:00
|
|
|
|
// if anyone finds a way to do this cleaner that also works, feel free to change it!
|
|
|
|
|
auto restoreFunctionDock = qhelpers::forceWidth(functionsDock->widget(), 200);
|
|
|
|
|
qApp->processEvents();
|
|
|
|
|
restoreFunctionDock.restoreWidth(functionsDock->widget());
|
|
|
|
|
|
2018-08-27 11:16:48 +00:00
|
|
|
|
core->setMemoryWidgetPriority(CutterCore::MemoryWidgetType::Disassembly);
|
2018-05-25 14:30:59 +00:00
|
|
|
|
}
|
|
|
|
|
|
2018-06-12 15:42:38 +00:00
|
|
|
|
void MainWindow::resetToDebugLayout()
|
2019-01-14 08:17:10 +00:00
|
|
|
|
{
|
|
|
|
|
CutterCore::MemoryWidgetType memType = core->getMemoryWidgetPriority();
|
|
|
|
|
hideAllDocks();
|
|
|
|
|
restoreDocks();
|
|
|
|
|
showDebugDocks();
|
|
|
|
|
core->raisePrioritizedMemoryWidget(memType);
|
|
|
|
|
|
|
|
|
|
auto restoreStackDock = qhelpers::forceWidth(stackDock->widget(), 400);
|
|
|
|
|
qApp->processEvents();
|
|
|
|
|
restoreStackDock.restoreWidth(stackDock->widget());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void MainWindow::restoreDebugLayout()
|
2018-06-12 15:42:38 +00:00
|
|
|
|
{
|
2018-08-27 11:16:48 +00:00
|
|
|
|
CutterCore::MemoryWidgetType memType = core->getMemoryWidgetPriority();
|
2018-07-24 23:12:15 +00:00
|
|
|
|
bool isMaxim = isMaximized();
|
2018-06-12 15:42:38 +00:00
|
|
|
|
hideAllDocks();
|
|
|
|
|
restoreDocks();
|
|
|
|
|
showDebugDocks();
|
2018-07-23 23:12:36 +00:00
|
|
|
|
readDebugSettings();
|
2018-08-27 11:16:48 +00:00
|
|
|
|
core->raisePrioritizedMemoryWidget(memType);
|
2018-07-24 23:12:15 +00:00
|
|
|
|
if (isMaxim) {
|
|
|
|
|
showMaximized();
|
|
|
|
|
} else {
|
|
|
|
|
showNormal();
|
|
|
|
|
}
|
2018-06-12 15:42:38 +00:00
|
|
|
|
}
|
|
|
|
|
|
2018-01-16 14:09:51 +00:00
|
|
|
|
void MainWindow::on_actionLock_triggered()
|
|
|
|
|
{
|
|
|
|
|
panelLock = !panelLock;
|
|
|
|
|
setPanelLock();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void MainWindow::on_actionLockUnlock_triggered()
|
|
|
|
|
{
|
2018-03-21 20:32:32 +00:00
|
|
|
|
if (ui->actionLockUnlock->isChecked()) {
|
2018-05-04 07:58:32 +00:00
|
|
|
|
for (QDockWidget *dockWidget : findChildren<QDockWidget *>()) {
|
2018-01-16 14:09:51 +00:00
|
|
|
|
dockWidget->setFeatures(QDockWidget::NoDockWidgetFeatures);
|
|
|
|
|
}
|
|
|
|
|
ui->actionLockUnlock->setIcon(QIcon(":/lock"));
|
2018-03-21 20:32:32 +00:00
|
|
|
|
} else {
|
2018-05-04 07:58:32 +00:00
|
|
|
|
for (QDockWidget *dockWidget : findChildren<QDockWidget *>()) {
|
2018-01-16 14:09:51 +00:00
|
|
|
|
dockWidget->setFeatures(QDockWidget::AllDockWidgetFeatures);
|
|
|
|
|
}
|
|
|
|
|
ui->actionLockUnlock->setIcon(QIcon(":/unlock"));
|
|
|
|
|
}
|
2017-03-29 10:18:37 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void MainWindow::on_actionFunctionsRename_triggered()
|
|
|
|
|
{
|
2017-04-09 19:55:06 +00:00
|
|
|
|
RenameDialog *r = new RenameDialog(this);
|
2017-03-29 10:18:37 +00:00
|
|
|
|
// Get function based on click position
|
|
|
|
|
//r->setFunctionName(fcn_name);
|
|
|
|
|
r->open();
|
|
|
|
|
}
|
|
|
|
|
|
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) {
|
|
|
|
|
resetToDebugLayout();
|
|
|
|
|
} else {
|
|
|
|
|
resetToDefaultLayout();
|
|
|
|
|
}
|
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
|
|
|
|
|
QProcess process(this);
|
|
|
|
|
process.setEnvironment(QProcess::systemEnvironment());
|
|
|
|
|
process.startDetached(qApp->applicationFilePath());
|
2017-03-29 10:18:37 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void MainWindow::on_actionSave_triggered()
|
|
|
|
|
{
|
2017-05-13 18:09:36 +00:00
|
|
|
|
saveProject();
|
2017-03-29 10:18:37 +00:00
|
|
|
|
}
|
|
|
|
|
|
2017-10-21 19:20:10 +00:00
|
|
|
|
void MainWindow::on_actionSaveAs_triggered()
|
|
|
|
|
{
|
2017-11-03 17:22:54 +00:00
|
|
|
|
saveProjectAs();
|
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());
|
|
|
|
|
|
2018-11-02 06:48:17 +00:00
|
|
|
|
const QString &fileName = QDir::toNativeSeparators(dialog.getOpenFileName(this, tr("Select radare2 script")));
|
|
|
|
|
if (fileName.isEmpty()) // Cancel was pressed
|
2017-04-09 20:36:17 +00:00
|
|
|
|
return;
|
2018-08-27 11:16:48 +00:00
|
|
|
|
core->loadScript(fileName);
|
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".
|
|
|
|
|
*/
|
2018-01-16 14:09:51 +00:00
|
|
|
|
void MainWindow::on_actionOpen_triggered()
|
2017-03-29 10:18:37 +00:00
|
|
|
|
{
|
2018-06-20 09:24:28 +00:00
|
|
|
|
OpenFileDialog dialog(this);
|
|
|
|
|
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,
|
2018-03-21 20:32:32 +00:00
|
|
|
|
tr("Do you really want to clear all settings?"),
|
|
|
|
|
QMessageBox::Ok | QMessageBox::Cancel);
|
|
|
|
|
if (ret == QMessageBox::Ok) {
|
2017-12-03 12:10:09 +00:00
|
|
|
|
Config()->resetAll();
|
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-08-27 11:16:48 +00:00
|
|
|
|
core->seekNext();
|
2018-03-08 09:11:19 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void MainWindow::on_actionUndoSeek_triggered()
|
|
|
|
|
{
|
2018-08-27 11:16:48 +00:00
|
|
|
|
core->seekPrev();
|
2018-03-08 09:11:19 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void MainWindow::on_actionRedoSeek_triggered()
|
|
|
|
|
{
|
2018-08-27 11:16:48 +00:00
|
|
|
|
core->seekNext();
|
2018-01-16 14:09:51 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void MainWindow::on_actionDisasAdd_comment_triggered()
|
|
|
|
|
{
|
|
|
|
|
CommentsDialog *c = new CommentsDialog(this);
|
|
|
|
|
c->exec();
|
|
|
|
|
delete c;
|
|
|
|
|
}
|
|
|
|
|
|
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
|
|
|
|
{
|
2017-12-14 13:42:24 +00:00
|
|
|
|
auto dialog = new PreferencesDialog(this);
|
2017-10-01 14:36:40 +00:00
|
|
|
|
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);
|
|
|
|
|
a->open();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void MainWindow::on_actionRefresh_Panels_triggered()
|
|
|
|
|
{
|
|
|
|
|
this->refreshAll();
|
|
|
|
|
}
|
|
|
|
|
|
2018-01-27 10:40:26 +00:00
|
|
|
|
void MainWindow::on_actionAnalyze_triggered()
|
|
|
|
|
{
|
2018-08-18 16:04:45 +00:00
|
|
|
|
// TODO: implement this, but do NOT open InitialOptionsDialog!!
|
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";
|
|
|
|
|
filters << tr("Print 'wx' r2 commands (*.r2)");
|
|
|
|
|
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()];
|
|
|
|
|
fileOut << Core()->cmd(cmd + " $s @ 0");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2018-11-02 06:48:17 +00:00
|
|
|
|
void MainWindow::projectSaved(bool successfully, const QString &name)
|
2017-10-21 19:20:10 +00:00
|
|
|
|
{
|
2018-11-02 06:48:17 +00:00
|
|
|
|
if (successfully)
|
|
|
|
|
core->message(tr("Project saved: %1").arg(name));
|
|
|
|
|
else
|
|
|
|
|
core->message(tr("Failed to save project: %1").arg(name));
|
2017-10-21 19:20:10 +00:00
|
|
|
|
}
|
2018-03-16 21:46:57 +00:00
|
|
|
|
|
2018-06-12 15:42:38 +00:00
|
|
|
|
void MainWindow::changeDebugView()
|
|
|
|
|
{
|
|
|
|
|
saveSettings();
|
2019-01-14 08:17:10 +00:00
|
|
|
|
restoreDebugLayout();
|
2018-10-22 18:06:30 +00:00
|
|
|
|
enableDebugWidgetsMenu(true);
|
2018-06-12 15:42:38 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void MainWindow::changeDefinedView()
|
|
|
|
|
{
|
2018-07-23 23:12:36 +00:00
|
|
|
|
saveDebugSettings();
|
2018-08-27 11:16:48 +00:00
|
|
|
|
CutterCore::MemoryWidgetType memType = core->getMemoryWidgetPriority();
|
2018-07-23 23:12:36 +00:00
|
|
|
|
hideAllDocks();
|
|
|
|
|
restoreDocks();
|
2018-06-12 15:42:38 +00:00
|
|
|
|
readSettings();
|
2018-10-22 18:06:30 +00:00
|
|
|
|
enableDebugWidgetsMenu(false);
|
2018-08-27 11:16:48 +00:00
|
|
|
|
core->raisePrioritizedMemoryWidget(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;
|
|
|
|
|
}
|
|
|
|
|
|
2018-03-21 20:32:32 +00:00
|
|
|
|
void MainWindow::addToDockWidgetList(QDockWidget *dockWidget)
|
|
|
|
|
{
|
2018-03-16 21:46:57 +00:00
|
|
|
|
this->dockWidgets.push_back(dockWidget);
|
|
|
|
|
}
|
|
|
|
|
|
2018-03-21 20:32:32 +00:00
|
|
|
|
void MainWindow::addDockWidgetAction(QDockWidget *dockWidget, QAction *action)
|
|
|
|
|
{
|
2018-03-16 21:46:57 +00:00
|
|
|
|
this->dockWidgetActions[action] = dockWidget;
|
|
|
|
|
}
|