Refactor Layout Save and Restore Feature (#1538)

#1515
This commit is contained in:
optizone 2019-06-18 16:02:41 +03:00 committed by Florian Märkl
parent bb80cbd862
commit 06aceaf9b1
19 changed files with 371 additions and 171 deletions

View File

@ -70,7 +70,7 @@ static void initializeSettings()
qInfo() << "Migrating Settings to Version" << v; qInfo() << "Migrating Settings to Version" << v;
switch (v) { switch (v) {
case 1: case 1:
migrateSettingsTo1(settings); migrateSettingsTo1(settings); break;
default: default:
break; break;
} }

View File

@ -13,6 +13,13 @@ CutterSeekable::CutterSeekable(QObject *parent)
CutterSeekable::~CutterSeekable() {} CutterSeekable::~CutterSeekable() {}
void CutterSeekable::setSynchronization(bool sync)
{
synchronized = sync;
onCoreSeekChanged(Core()->getOffset());
emit syncChanged();
}
void CutterSeekable::onCoreSeekChanged(RVA addr) void CutterSeekable::onCoreSeekChanged(RVA addr)
{ {
if (synchronized && widgetOffset != addr) { if (synchronized && widgetOffset != addr) {
@ -48,8 +55,7 @@ RVA CutterSeekable::getOffset()
void CutterSeekable::toggleSynchronization() void CutterSeekable::toggleSynchronization()
{ {
synchronized = !synchronized; setSynchronization(!synchronized);
onCoreSeekChanged(Core()->getOffset());
} }
bool CutterSeekable::isSynchronized() bool CutterSeekable::isSynchronized()

View File

@ -23,10 +23,10 @@ public:
void seek(RVA addr) { updateSeek(addr, false); } void seek(RVA addr) { updateSeek(addr, false); }
/** /**
* @brief toggleSyncWithCore toggles * @brief setSynchronization sets
* Core seek synchronization. * Core seek synchronization.
*/ */
void toggleSynchronization(); void setSynchronization(bool sync);
/** /**
* @brief getOffset returns the seekable offset. * @brief getOffset returns the seekable offset.
@ -50,6 +50,12 @@ public slots:
*/ */
void seekPrev(); void seekPrev();
/**
* @brief toggleSyncWithCore toggles
* Core seek synchronization.
*/
void toggleSynchronization();
private slots: private slots:
/** /**
* @brief onCoreSeekChanged * @brief onCoreSeekChanged
@ -82,5 +88,5 @@ private:
signals: signals:
void seekableSeekChanged(RVA addr); void seekableSeekChanged(RVA addr);
void syncChanged();
}; };

View File

@ -106,6 +106,9 @@
#include <QGraphicsScene> #include <QGraphicsScene>
#include <QGraphicsView> #include <QGraphicsView>
template<class T>
T* getNewInstance(MainWindow *m, QAction *a) { return new T(m, a); }
MainWindow::MainWindow(QWidget *parent) : MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent), QMainWindow(parent),
core(Core()), core(Core()),
@ -124,9 +127,21 @@ void MainWindow::initUI()
{ {
ui->setupUi(this); ui->setupUi(this);
connect(ui->actionExtraGraph, &QAction::triggered, this, &MainWindow::addExtraGraph);
connect(ui->actionExtraDisassembly, &QAction::triggered, this, &MainWindow::addExtraDisassembly);
connect(ui->actionExtraHexdump, &QAction::triggered, this, &MainWindow::addExtraHexdump);
classNameToConstructorAndActionMap.insert(GraphWidget::getWidgetType(),
{getNewInstance<GraphWidget>, ui->actionGraph});
classNameToConstructorAndActionMap.insert(DisassemblyWidget::getWidgetType(),
{getNewInstance<DisassemblyWidget>, ui->actionDisassembly});
classNameToConstructorAndActionMap.insert(HexdumpWidget::getWidgetType(),
{getNewInstance<HexdumpWidget>, ui->actionHexdump});
initToolBar(); initToolBar();
initDocks(); initDocks();
emptyState = saveState();
/* /*
* Some global shortcuts * Some global shortcuts
*/ */
@ -251,8 +266,6 @@ void MainWindow::initToolBar()
void MainWindow::initDocks() void MainWindow::initDocks()
{ {
dockWidgets.reserve(20); dockWidgets.reserve(20);
disassemblyDock = new DisassemblyWidget(this, ui->actionDisassembly);
hexdumpDock = new HexdumpWidget(this, ui->actionHexdump);
pseudocodeDock = new PseudocodeWidget(this, ui->actionPseudocode); pseudocodeDock = new PseudocodeWidget(this, ui->actionPseudocode);
consoleDock = new ConsoleWidget(this, ui->actionConsole); consoleDock = new ConsoleWidget(this, ui->actionConsole);
@ -271,7 +284,6 @@ void MainWindow::initDocks()
}); });
ui->actionOverview->setChecked(overviewDock->getUserOpened()); ui->actionOverview->setChecked(overviewDock->getUserOpened());
graphDock = new GraphWidget(this, ui->actionGraph);
sectionsDock = new SectionsWidget(this, ui->actionSections); sectionsDock = new SectionsWidget(this, ui->actionSections);
segmentsDock = new SegmentsWidget(this, ui->actionSegments); segmentsDock = new SegmentsWidget(this, ui->actionSegments);
entrypointDock = new EntrypointWidget(this, ui->actionEntrypoints); entrypointDock = new EntrypointWidget(this, ui->actionEntrypoints);
@ -298,6 +310,38 @@ void MainWindow::initDocks()
classesDock = new ClassesWidget(this, ui->actionClasses); classesDock = new ClassesWidget(this, ui->actionClasses);
resourcesDock = new ResourcesWidget(this, ui->actionResources); resourcesDock = new ResourcesWidget(this, ui->actionResources);
vTablesDock = new VTablesWidget(this, ui->actionVTables); vTablesDock = new VTablesWidget(this, ui->actionVTables);
QSettings s;
QStringList docks = s.value("docks").toStringList();
// Restore all extra widgets
QString className;
for (const auto &it : docks) {
if (std::none_of(dockWidgets.constBegin(), dockWidgets.constEnd(),
[&it](QDockWidget *w) { return w->objectName() == it; })) {
className = it.split(';').at(0);
if (classNameToConstructorAndActionMap.contains(className)) {
auto widget = classNameToConstructorAndActionMap[className]
.first(this, classNameToConstructorAndActionMap[className].second);
widget->setObjectName(it);
addExtraWidget(widget);
}
}
}
updateMemberPointers();
if (!disassemblyDock) {
addExtraDisassembly();
}
if (!graphDock) {
addExtraGraph();
}
if (!hexdumpDock) {
addExtraHexdump();
}
updateMemberPointers();
} }
void MainWindow::initLayout() void MainWindow::initLayout()
@ -306,10 +350,8 @@ void MainWindow::initLayout()
enableDebugWidgetsMenu(false); enableDebugWidgetsMenu(false);
// Restore saved settings // Restore saved settings
readSettingsOrDefault(); readSettingsOrDefault();
// TODO: Allow the user to select this option visually in the GUI settings
// Adjust the DockWidget areas initCorners();
setCorner(Qt::TopLeftCorner, Qt::LeftDockWidgetArea);
setCorner(Qt::BottomLeftCorner, Qt::LeftDockWidgetArea);
} }
void MainWindow::toggleOverview(bool visibility, GraphWidget *targetGraph) void MainWindow::toggleOverview(bool visibility, GraphWidget *targetGraph)
@ -328,28 +370,31 @@ void MainWindow::updateTasksIndicator()
tasksProgressIndicator->setProgressIndicatorVisible(running); tasksProgressIndicator->setProgressIndicatorVisible(running);
} }
void MainWindow::on_actionExtraGraph_triggered() void MainWindow::addExtraGraph()
{ {
auto *extraDock = new GraphWidget(this, nullptr); auto *extraDock = new GraphWidget(this, ui->actionGraph);
extraDock->setObjectName(getUniqueObjectName(extraDock->getWidgetType()));
addExtraWidget(extraDock); addExtraWidget(extraDock);
} }
void MainWindow::on_actionExtraHexdump_triggered() void MainWindow::addExtraHexdump()
{ {
auto *extraDock = new HexdumpWidget(this, nullptr); auto *extraDock = new HexdumpWidget(this, ui->actionHexdump);
extraDock->setObjectName(getUniqueObjectName(extraDock->getWidgetType()));
addExtraWidget(extraDock); addExtraWidget(extraDock);
} }
void MainWindow::on_actionExtraDisassembly_triggered() void MainWindow::addExtraDisassembly()
{ {
auto *extraDock = new DisassemblyWidget(this, nullptr); auto *extraDock = new DisassemblyWidget(this, ui->actionDisassembly);
extraDock->setObjectName(getUniqueObjectName(extraDock->getWidgetType()));
addExtraWidget(extraDock); addExtraWidget(extraDock);
} }
void MainWindow::addExtraWidget(CutterDockWidget *extraDock) void MainWindow::addExtraWidget(CutterDockWidget *extraDock)
{ {
extraDock->setTransient(true); extraDock->setTransient(true);
addDockWidget(Qt::TopDockWidgetArea, extraDock); addDockWidget(Qt::TopDockWidgetArea, extraDock, Qt::Orientation::Horizontal);
auto restoreExtraDock = qhelpers::forceWidth(extraDock->widget(), 600); auto restoreExtraDock = qhelpers::forceWidth(extraDock->widget(), 600);
qApp->processEvents(); qApp->processEvents();
restoreExtraDock.restoreWidth(extraDock->widget()); restoreExtraDock.restoreWidth(extraDock->widget());
@ -385,7 +430,8 @@ QMenu *MainWindow::getMenuByType(MenuType type)
void MainWindow::addPluginDockWidget(QDockWidget *dockWidget, QAction *action) void MainWindow::addPluginDockWidget(QDockWidget *dockWidget, QAction *action)
{ {
addDockWidget(Qt::TopDockWidgetArea, dockWidget); addDockWidget(Qt::TopDockWidgetArea, dockWidget);
addDockWidgetAction(dockWidget, action); dockWidget->addAction(action);
addWidget(dockWidget);
ui->menuPlugins->addAction(action); ui->menuPlugins->addAction(action);
addDockWidget(Qt::DockWidgetArea::TopDockWidgetArea, dockWidget); addDockWidget(Qt::DockWidgetArea::TopDockWidgetArea, dockWidget);
updateDockActionChecked(action); updateDockActionChecked(action);
@ -494,6 +540,14 @@ void MainWindow::finalizeOpen()
showMaximized(); showMaximized();
QSettings s;
QStringList unsync = s.value("unsync").toStringList();
for (auto it : dockWidgets) {
auto w = qobject_cast<MemoryDockWidget*>(it);
if (w) {
w->getSeekable()->setSynchronization(!unsync.contains(it->objectName()));
}
}
// Set focus to disasm or graph widget // Set focus to disasm or graph widget
@ -606,7 +660,7 @@ void MainWindow::readSettingsOrDefault()
* Check if saved settings exist * Check if saved settings exist
* If not, then read the default layout * If not, then read the default layout
*/ */
if (!geo.length() && !state.length()) { if (!geo.length() || !state.length()) {
resetToDefaultLayout(); resetToDefaultLayout();
return; return;
} }
@ -642,6 +696,22 @@ void MainWindow::readSettingsOrDefault()
void MainWindow::saveSettings() void MainWindow::saveSettings()
{ {
QSettings settings; QSettings settings;
QStringList docks;
const QStringList syncable = QStringList()
<< HexdumpWidget::getWidgetType()
<< DisassemblyWidget::getWidgetType()
<< GraphWidget::getWidgetType();
QStringList unsync;
for (const auto &it : dockWidgets) {
docks.append(it->objectName());
if (syncable.contains(it->metaObject()->className()) &&
!qobject_cast<MemoryDockWidget*>(it)->getSeekable()->isSynchronized()) {
unsync.append(it->objectName());
}
}
settings.setValue("docks", docks);
settings.setValue("unsync", unsync);
settings.setValue("geometry", saveGeometry()); settings.setValue("geometry", saveGeometry());
settings.setValue("size", size()); settings.setValue("size", size());
settings.setValue("pos", pos()); settings.setValue("pos", pos());
@ -728,22 +798,7 @@ void MainWindow::restoreDocks()
addDockWidget(Qt::TopDockWidgetArea, overviewDock); addDockWidget(Qt::TopDockWidgetArea, overviewDock);
// Function | Dashboard // Function | Dashboard
splitDockWidget(overviewDock, dashboardDock, Qt::Horizontal); splitDockWidget(functionsDock, dashboardDock, Qt::Horizontal);
splitDockWidget(functionsDock, overviewDock, Qt::Vertical);
// In the lower half the console is the first widget
addDockWidget(Qt::BottomDockWidgetArea, consoleDock);
// Console | Sections
splitDockWidget(consoleDock, sectionsDock, Qt::Horizontal);
splitDockWidget(consoleDock, segmentsDock, Qt::Horizontal);
// Tabs for center (must be applied after splitDockWidget())
tabifyDockWidget(sectionsDock, commentsDock);
tabifyDockWidget(segmentsDock, commentsDock);
tabifyDockWidget(dashboardDock, disassemblyDock);
tabifyDockWidget(dashboardDock, graphDock);
tabifyDockWidget(dashboardDock, hexdumpDock);
tabifyDockWidget(dashboardDock, pseudocodeDock); tabifyDockWidget(dashboardDock, pseudocodeDock);
tabifyDockWidget(dashboardDock, entrypointDock); tabifyDockWidget(dashboardDock, entrypointDock);
tabifyDockWidget(dashboardDock, flagsDock); tabifyDockWidget(dashboardDock, flagsDock);
@ -760,17 +815,34 @@ void MainWindow::restoreDocks()
tabifyDockWidget(dashboardDock, resourcesDock); tabifyDockWidget(dashboardDock, resourcesDock);
tabifyDockWidget(dashboardDock, vTablesDock); tabifyDockWidget(dashboardDock, vTablesDock);
tabifyDockWidget(dashboardDock, sdbDock); tabifyDockWidget(dashboardDock, sdbDock);
tabifyDockWidget(dashboardDock, memoryMapDock);
tabifyDockWidget(dashboardDock, breakpointDock);
tabifyDockWidget(dashboardDock, registerRefsDock);
for (const auto &it : dockWidgets) {
// Check whether or not current widgets is graph, hexdump or disasm
if (qobject_cast<GraphWidget*>(it) ||
qobject_cast<HexdumpWidget*>(it) ||
qobject_cast<DisassemblyWidget*>(it)) {
tabifyDockWidget(dashboardDock, it);
}
}
splitDockWidget(functionsDock, overviewDock, Qt::Vertical);
// In the lower half the console is the first widget
addDockWidget(Qt::BottomDockWidgetArea, consoleDock);
// Console | Sections
splitDockWidget(consoleDock, sectionsDock, Qt::Horizontal);
splitDockWidget(consoleDock, segmentsDock, Qt::Horizontal);
tabifyDockWidget(sectionsDock, commentsDock);
// Add Stack, Registers and Backtrace vertically stacked // Add Stack, Registers and Backtrace vertically stacked
addDockWidget(Qt::TopDockWidgetArea, stackDock); addDockWidget(Qt::TopDockWidgetArea, stackDock);
splitDockWidget(stackDock, registersDock, Qt::Vertical); splitDockWidget(stackDock, registersDock, Qt::Vertical);
tabifyDockWidget(stackDock, backtraceDock); tabifyDockWidget(stackDock, backtraceDock);
// MemoryMap/Breakpoint/RegRefs widget goes in the center tabs
tabifyDockWidget(dashboardDock, memoryMapDock);
tabifyDockWidget(dashboardDock, breakpointDock);
tabifyDockWidget(dashboardDock, registerRefsDock);
updateDockActionsChecked(); updateDockActionsChecked();
} }
@ -784,14 +856,88 @@ void MainWindow::hideAllDocks()
void MainWindow::updateDockActionsChecked() void MainWindow::updateDockActionsChecked()
{ {
for (auto i = dockWidgetActions.constBegin(); i != dockWidgetActions.constEnd(); i++) { for (auto i = dockWidgetsOfAction.constBegin(); i != dockWidgetsOfAction.constEnd(); i++) {
i.key()->setChecked(!i.value()->isHidden()); updateDockActionChecked(i.key());
}
}
QString MainWindow::getUniqueObjectName(const QString& widgetType) const
{
QStringList docks;
docks.reserve(dockWidgets.size());
QString name;
for (const auto &it : dockWidgets) {
name = it->objectName();
if (name.split(';').at(0) == widgetType && name != "Graph Overview") {
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);
}
void MainWindow::initCorners()
{
// TODO: Allow the user to select this option visually in the GUI settings
// Adjust the DockWidget areas
setCorner(Qt::TopLeftCorner, Qt::LeftDockWidgetArea);
setCorner(Qt::BottomLeftCorner, Qt::LeftDockWidgetArea);
setCorner(Qt::BottomRightCorner, Qt::BottomDockWidgetArea);
setCorner(Qt::TopRightCorner, Qt::RightDockWidgetArea);
}
void MainWindow::updateMemberPointers()
{
QString className;
for (auto it : dockWidgets) {
if (!graphDock) {
graphDock = qobject_cast<GraphWidget*>(it);
}
if (!disassemblyDock) {
disassemblyDock = qobject_cast<DisassemblyWidget*>(it);
}
if (!hexdumpDock) {
hexdumpDock = qobject_cast<HexdumpWidget*>(it);
}
}
}
void MainWindow::addWidget(QDockWidget* widget)
{
dockWidgets.push_back(widget);
for (auto action : widget->actions()) {
dockWidgetsOfAction.insert(action, widget);
connect(qobject_cast<CutterDockWidget*>(widget), &CutterDockWidget::closed,
this, [this]() {
QDockWidget *widget = qobject_cast<QDockWidget*>(sender());
dockWidgets.removeOne(widget);
for (auto action : widget->actions()) {
dockWidgetsOfAction.remove(action, widget);
}
updateMemberPointers();
updateDockActionsChecked();
});
} }
} }
void MainWindow::updateDockActionChecked(QAction *action) void MainWindow::updateDockActionChecked(QAction *action)
{ {
action->setChecked(!dockWidgetActions[action]->isHidden()); auto actions = dockWidgetsOfAction.values(action);
action->setChecked(!std::accumulate(actions.begin(), actions.end(), false,
[](bool a, QDockWidget* w) -> bool {
return a || w->isHidden();
}));
} }
void MainWindow::showZenDocks() void MainWindow::showZenDocks()
@ -799,17 +945,20 @@ void MainWindow::showZenDocks()
const QList<QDockWidget *> zenDocks = { functionsDock, const QList<QDockWidget *> zenDocks = { functionsDock,
dashboardDock, dashboardDock,
stringsDock, stringsDock,
graphDock,
disassemblyDock,
hexdumpDock,
searchDock, searchDock,
importsDock, importsDock
}; };
int width = functionsDock->maximumWidth();
functionsDock->setMaximumWidth(200);
for (auto w : dockWidgets) { for (auto w : dockWidgets) {
if (zenDocks.contains(w)) { if (zenDocks.contains(w) ||
qobject_cast<GraphWidget*>(w) ||
qobject_cast<HexdumpWidget*>(w) ||
qobject_cast<DisassemblyWidget*>(w)) {
w->show(); w->show();
} }
} }
functionsDock->setMaximumWidth(width);
updateDockActionsChecked(); updateDockActionsChecked();
} }
@ -817,9 +966,6 @@ void MainWindow::showDebugDocks()
{ {
const QList<QDockWidget *> debugDocks = { functionsDock, const QList<QDockWidget *> debugDocks = { functionsDock,
stringsDock, stringsDock,
graphDock,
disassemblyDock,
hexdumpDock,
searchDock, searchDock,
stackDock, stackDock,
registersDock, registersDock,
@ -827,11 +973,17 @@ void MainWindow::showDebugDocks()
memoryMapDock, memoryMapDock,
breakpointDock breakpointDock
}; };
int width = functionsDock->maximumWidth();
functionsDock->setMaximumWidth(200);
for (auto w : dockWidgets) { for (auto w : dockWidgets) {
if (debugDocks.contains(w)) { if (debugDocks.contains(w) ||
qobject_cast<GraphWidget*>(w) ||
qobject_cast<HexdumpWidget*>(w) ||
qobject_cast<DisassemblyWidget*>(w)) {
w->show(); w->show();
} }
} }
functionsDock->setMaximumWidth(width);
updateDockActionsChecked(); updateDockActionsChecked();
} }
@ -877,6 +1029,23 @@ void MainWindow::restoreDebugLayout()
} }
} }
void MainWindow::resetDockWidgetList()
{
QStringList isLeft;
QList<QWidget*> toClose;
for (auto it : dockWidgets) {
if (isLeft.contains(it->metaObject()->className())) {
toClose.append(it);
} else if (QRegExp("\\w+ \\d+").exactMatch(it->objectName())) {
isLeft.append(it->metaObject()->className());
}
}
for (auto it : toClose) {
it->close();
}
updateMemberPointers();
}
void MainWindow::on_actionLock_triggered() void MainWindow::on_actionLock_triggered()
{ {
panelLock = !panelLock; panelLock = !panelLock;
@ -908,9 +1077,21 @@ void MainWindow::on_actionFunctionsRename_triggered()
void MainWindow::on_actionDefault_triggered() void MainWindow::on_actionDefault_triggered()
{ {
QSettings s;
restoreState(emptyState);
initCorners();
resetDockWidgetList();
if (core->currentlyDebugging) { if (core->currentlyDebugging) {
resetToDefaultLayout();
saveSettings();
resetToDebugLayout(); resetToDebugLayout();
} else { } else {
resetToDebugLayout();
saveDebugSettings();
resetToDefaultLayout(); resetToDefaultLayout();
} }
} }
@ -1207,16 +1388,6 @@ bool MainWindow::eventFilter(QObject *, QEvent *event)
return false; return false;
} }
void MainWindow::addToDockWidgetList(QDockWidget *dockWidget)
{
this->dockWidgets.push_back(dockWidget);
}
void MainWindow::addDockWidgetAction(QDockWidget *dockWidget, QAction *action)
{
this->dockWidgetActions[action] = dockWidget;
}
/** /**
* @brief Show a warning message box. * @brief Show a warning message box.
* *

View File

@ -94,8 +94,7 @@ public:
void setFilename(const QString &fn); void setFilename(const QString &fn);
void refreshOmniBar(const QStringList &flags); void refreshOmniBar(const QStringList &flags);
void addToDockWidgetList(QDockWidget *dockWidget); void addWidget(QDockWidget* widget);
void addDockWidgetAction(QDockWidget *dockWidget, QAction *action);
void addExtraWidget(CutterDockWidget *extraDock); void addExtraWidget(CutterDockWidget *extraDock);
void addPluginDockWidget(QDockWidget *dockWidget, QAction *action); void addPluginDockWidget(QDockWidget *dockWidget, QAction *action);
@ -138,9 +137,9 @@ public slots:
private slots: private slots:
void on_actionAbout_triggered(); void on_actionAbout_triggered();
void on_actionIssue_triggered(); void on_actionIssue_triggered();
void on_actionExtraGraph_triggered(); void addExtraGraph();
void on_actionExtraHexdump_triggered(); void addExtraHexdump();
void on_actionExtraDisassembly_triggered(); void addExtraDisassembly();
void on_actionRefresh_Panels_triggered(); void on_actionRefresh_Panels_triggered();
@ -204,11 +203,12 @@ private:
VisualNavbar *visualNavbar; VisualNavbar *visualNavbar;
Omnibar *omnibar; Omnibar *omnibar;
ProgressIndicator *tasksProgressIndicator; ProgressIndicator *tasksProgressIndicator;
QByteArray emptyState;
Configuration *configuration; Configuration *configuration;
QList<QDockWidget *> dockWidgets; QList<QDockWidget *> dockWidgets;
QMap<QAction *, QDockWidget *> dockWidgetActions; QMultiMap<QAction *, QDockWidget *> dockWidgetsOfAction;
DisassemblyWidget *disassemblyDock = nullptr; DisassemblyWidget *disassemblyDock = nullptr;
HexdumpWidget *hexdumpDock = nullptr; HexdumpWidget *hexdumpDock = nullptr;
PseudocodeWidget *pseudocodeDock = nullptr; PseudocodeWidget *pseudocodeDock = nullptr;
@ -250,12 +250,15 @@ private:
void initToolBar(); void initToolBar();
void initDocks(); void initDocks();
void initLayout(); void initLayout();
void initCorners();
void displayInitialOptionsDialog(const InitialOptions &options = InitialOptions(), bool skipOptionsDialog = false); void displayInitialOptionsDialog(const InitialOptions &options = InitialOptions(), bool skipOptionsDialog = false);
void resetToDefaultLayout(); void resetToDefaultLayout();
void resetToDebugLayout(); void resetToDebugLayout();
void restoreDebugLayout(); void restoreDebugLayout();
void updateMemberPointers();
void resetDockWidgetList();
void restoreDocks(); void restoreDocks();
void hideAllDocks(); void hideAllDocks();
void showZenDocks(); void showZenDocks();
@ -267,6 +270,14 @@ private:
void updateDockActionsChecked(); void updateDockActionsChecked();
void setOverviewData(); void setOverviewData();
bool isOverviewActive(); bool isOverviewActive();
/**
* @brief Map, where key is class name an value is pair of
* pointer to class constructor and action that passed to this constructor.
*/
QMap<QString, std::pair<std::function<CutterDockWidget*(MainWindow*, QAction*)>, QAction*>> classNameToConstructorAndActionMap;
QString getUniqueObjectName(const QString &widgetType) const;
}; };
#endif // MAINWINDOW_H #endif // MAINWINDOW_H

View File

@ -8,12 +8,12 @@ CutterDockWidget::CutterDockWidget(MainWindow *parent, QAction *action) :
QDockWidget(parent), QDockWidget(parent),
action(action) action(action)
{ {
if (action) {
addAction(action);
connect(action, &QAction::triggered, this, &CutterDockWidget::toggleDockWidget);
}
if (parent) { if (parent) {
parent->addToDockWidgetList(this); parent->addWidget(this);
if (action) {
parent->addDockWidgetAction(this, action);
connect(action, &QAction::triggered, this, &CutterDockWidget::toggleDockWidget);
}
} }
// Install event filter to catch redraw widgets when needed // Install event filter to catch redraw widgets when needed
@ -39,7 +39,7 @@ bool CutterDockWidget::eventFilter(QObject *object, QEvent *event)
void CutterDockWidget::toggleDockWidget(bool show) void CutterDockWidget::toggleDockWidget(bool show)
{ {
if (!show) { if (!show) {
this->close(); this->hide();
} else { } else {
this->show(); this->show();
this->raise(); this->raise();
@ -73,6 +73,8 @@ void CutterDockWidget::closeEvent(QCloseEvent *event)
if (isTransient) { if (isTransient) {
deleteLater(); deleteLater();
} }
emit closed();
} }
QAction *CutterDockWidget::getBoundAction() const QAction *CutterDockWidget::getBoundAction() const

View File

@ -57,6 +57,7 @@ public:
signals: signals:
void becameVisibleToUser(); void becameVisibleToUser();
void closed();
public slots: public slots:
void toggleDockWidget(bool show); void toggleDockWidget(bool show);

View File

@ -29,12 +29,12 @@
#include <cmath> #include <cmath>
DisassemblerGraphView::DisassemblerGraphView(QWidget *parent) DisassemblerGraphView::DisassemblerGraphView(QWidget *parent, CutterSeekable* seekable)
: GraphView(parent), : GraphView(parent),
mFontMetrics(nullptr), mFontMetrics(nullptr),
blockMenu(new DisassemblyContextMenu(this)), blockMenu(new DisassemblyContextMenu(this)),
contextMenu(new QMenu(this)), contextMenu(new QMenu(this)),
seekable(new CutterSeekable(this)) seekable(seekable)
{ {
highlight_token = nullptr; highlight_token = nullptr;
auto *layout = new QVBoxLayout(this); auto *layout = new QVBoxLayout(this);
@ -107,7 +107,7 @@ DisassemblerGraphView::DisassemblerGraphView(QWidget *parent)
actionExportGraph.setText(tr("Export Graph")); actionExportGraph.setText(tr("Export Graph"));
connect(&actionExportGraph, SIGNAL(triggered(bool)), this, SLOT(on_actionExportGraph_triggered())); connect(&actionExportGraph, SIGNAL(triggered(bool)), this, SLOT(on_actionExportGraph_triggered()));
actionSyncOffset.setText(tr("Sync/unsync offset")); actionSyncOffset.setText(tr("Sync/unsync offset"));
connect(&actionSyncOffset, SIGNAL(triggered(bool)), this, SLOT(toggleSync())); connect(&actionSyncOffset, &QAction::triggered, seekable, &CutterSeekable::toggleSynchronization);
// Context menu that applies to everything // Context menu that applies to everything
contextMenu->addAction(&actionExportGraph); contextMenu->addAction(&actionExportGraph);
@ -190,16 +190,6 @@ DisassemblerGraphView::~DisassemblerGraphView()
} }
} }
void DisassemblerGraphView::toggleSync()
{
seekable->toggleSynchronization();
if (seekable->isSynchronized()) {
parentWidget()->setWindowTitle(windowTitle);
} else {
parentWidget()->setWindowTitle(windowTitle + CutterSeekable::tr(" (unsynced)"));
}
}
void DisassemblerGraphView::refreshView() void DisassemblerGraphView::refreshView()
{ {
initFont(); initFont();
@ -259,11 +249,7 @@ void DisassemblerGraphView::loadCurrentGraph()
} else if (!funcName.isEmpty()) { } else if (!funcName.isEmpty()) {
windowTitle += " (" + funcName + ")"; windowTitle += " (" + funcName + ")";
} }
if (!seekable->isSynchronized()) { emit nameChanged(windowTitle);
parentWidget()->setWindowTitle(windowTitle + CutterSeekable::tr(" (unsynced)"));
} else {
parentWidget()->setWindowTitle(windowTitle);
}
RVA entry = func["offset"].toVariant().toULongLong(); RVA entry = func["offset"].toVariant().toULongLong();

View File

@ -87,7 +87,7 @@ class DisassemblerGraphView : public GraphView
}; };
public: public:
DisassemblerGraphView(QWidget *parent); DisassemblerGraphView(QWidget *parent, CutterSeekable* seekable);
~DisassemblerGraphView() override; ~DisassemblerGraphView() override;
std::unordered_map<ut64, DisassemblyBlock> disassembly_blocks; std::unordered_map<ut64, DisassemblyBlock> disassembly_blocks;
virtual void drawBlock(QPainter &p, GraphView::GraphBlock &block) override; virtual void drawBlock(QPainter &p, GraphView::GraphBlock &block) override;
@ -115,8 +115,6 @@ public slots:
void colorsUpdatedSlot(); void colorsUpdatedSlot();
void fontsUpdatedSlot(); void fontsUpdatedSlot();
void onSeekChanged(RVA addr); void onSeekChanged(RVA addr);
void toggleSync();
void zoom(QPointF mouseRelativePos, double velocity); void zoom(QPointF mouseRelativePos, double velocity);
void zoomReset(); void zoomReset();
@ -215,6 +213,7 @@ signals:
void viewZoomed(); void viewZoomed();
void graphMoved(); void graphMoved();
void resized(); void resized();
void nameChanged(const QString& name);
public: public:
bool isGraphEmpty() { return emptyGraph; } bool isGraphEmpty() { return emptyGraph; }

View File

@ -38,26 +38,15 @@ DisassemblyWidget::DisassemblyWidget(MainWindow *main, QAction *action)
, mCtxMenu(new DisassemblyContextMenu(this)) , mCtxMenu(new DisassemblyContextMenu(this))
, mDisasScrollArea(new DisassemblyScrollArea(this)) , mDisasScrollArea(new DisassemblyScrollArea(this))
, mDisasTextEdit(new DisassemblyTextEdit(this)) , mDisasTextEdit(new DisassemblyTextEdit(this))
, seekable(new CutterSeekable(this))
{ {
/* setObjectName(getWidgetType());
* Ugly hack just for the layout issue
* QSettings saves the state with the object names
* By doing this hack,
* you can at least avoid some mess by dismissing all the Extra Widgets
*/
QString name = "Disassembly";
if (!action) {
name = "Extra Disassembly";
}
setObjectName(name);
topOffset = bottomOffset = RVA_INVALID; topOffset = bottomOffset = RVA_INVALID;
cursorLineOffset = 0; cursorLineOffset = 0;
cursorCharOffset = 0; cursorCharOffset = 0;
seekFromCursor = false; seekFromCursor = false;
setWindowTitle(tr("Disassembly")); setWindowTitle(getWindowTitle());
QVBoxLayout *layout = new QVBoxLayout(); QVBoxLayout *layout = new QVBoxLayout();
layout->addWidget(mDisasTextEdit); layout->addWidget(mDisasTextEdit);
@ -144,7 +133,7 @@ DisassemblyWidget::DisassemblyWidget(MainWindow *main, QAction *action)
mCtxMenu->addSeparator(); mCtxMenu->addSeparator();
syncIt.setText(tr("Sync/unsync offset")); syncIt.setText(tr("Sync/unsync offset"));
mCtxMenu->addAction(&syncIt); mCtxMenu->addAction(&syncIt);
connect(&syncIt, SIGNAL(triggered(bool)), this, SLOT(toggleSync())); connect(&syncIt, &QAction::triggered, seekable, &CutterSeekable::toggleSynchronization);
connect(seekable, &CutterSeekable::seekableSeekChanged, this, &DisassemblyWidget::on_seekChanged); connect(seekable, &CutterSeekable::seekableSeekChanged, this, &DisassemblyWidget::on_seekChanged);
addActions(mCtxMenu->actions()); addActions(mCtxMenu->actions());
@ -191,17 +180,6 @@ DisassemblyWidget::DisassemblyWidget(MainWindow *main, QAction *action)
#undef ADD_ACTION #undef ADD_ACTION
} }
void DisassemblyWidget::toggleSync()
{
QString windowTitle = tr("Disassembly");
seekable->toggleSynchronization();
if (seekable->isSynchronized()) {
setWindowTitle(windowTitle);
} else {
setWindowTitle(windowTitle + CutterSeekable::tr(" (unsynced)"));
}
}
void DisassemblyWidget::setPreviewMode(bool previewMode) void DisassemblyWidget::setPreviewMode(bool previewMode)
{ {
mDisasTextEdit->setContextMenuPolicy(previewMode mDisasTextEdit->setContextMenuPolicy(previewMode
@ -217,8 +195,8 @@ void DisassemblyWidget::setPreviewMode(bool previewMode)
action->setEnabled(!previewMode); action->setEnabled(!previewMode);
} }
} }
if (seekable->isSynchronized() && previewMode) { if (previewMode) {
toggleSync(); seekable->setSynchronization(false);
} }
} }
@ -227,6 +205,11 @@ QWidget *DisassemblyWidget::getTextWidget()
return mDisasTextEdit; return mDisasTextEdit;
} }
QString DisassemblyWidget::getWidgetType()
{
return "Disassembly";
}
void DisassemblyWidget::refreshDisasm(RVA offset) void DisassemblyWidget::refreshDisasm(RVA offset)
{ {
if(!disasmRefresh->attemptRefresh(offset == RVA_INVALID ? nullptr : new RVA(offset))) { if(!disasmRefresh->attemptRefresh(offset == RVA_INVALID ? nullptr : new RVA(offset))) {
@ -655,6 +638,11 @@ bool DisassemblyWidget::eventFilter(QObject *obj, QEvent *event)
return CutterDockWidget::eventFilter(obj, event); return CutterDockWidget::eventFilter(obj, event);
} }
QString DisassemblyWidget::getWindowTitle() const
{
return tr("Disassembly");
}
void DisassemblyWidget::on_seekChanged(RVA offset) void DisassemblyWidget::on_seekChanged(RVA offset)
{ {
if (!seekFromCursor) { if (!seekFromCursor) {

View File

@ -23,13 +23,14 @@ public:
explicit DisassemblyWidget(MainWindow *main, QAction *action = nullptr); explicit DisassemblyWidget(MainWindow *main, QAction *action = nullptr);
QWidget *getTextWidget(); QWidget *getTextWidget();
static QString getWidgetType();
public slots: public slots:
void highlightCurrentLine(); void highlightCurrentLine();
void showDisasContextMenu(const QPoint &pt); void showDisasContextMenu(const QPoint &pt);
void fontsUpdatedSlot(); void fontsUpdatedSlot();
void colorsUpdatedSlot(); void colorsUpdatedSlot();
void seekPrev(); void seekPrev();
void toggleSync();
void setPreviewMode(bool previewMode); void setPreviewMode(bool previewMode);
protected slots: protected slots:
@ -68,6 +69,7 @@ private:
RVA readCurrentDisassemblyOffset(); RVA readCurrentDisassemblyOffset();
RVA readDisassemblyOffset(QTextCursor tc); RVA readDisassemblyOffset(QTextCursor tc);
bool eventFilter(QObject *obj, QEvent *event) override; bool eventFilter(QObject *obj, QEvent *event) override;
QString getWindowTitle() const override;
QList<RVA> breakpoints; QList<RVA> breakpoints;
@ -82,7 +84,6 @@ private:
QList<QTextEdit::ExtraSelection> getSameWordsSelections(); QList<QTextEdit::ExtraSelection> getSameWordsSelections();
QAction syncIt; QAction syncIt;
CutterSeekable *seekable;
}; };
class DisassemblyScrollArea : public QAbstractScrollArea class DisassemblyScrollArea : public QAbstractScrollArea

View File

@ -6,19 +6,9 @@
GraphWidget::GraphWidget(MainWindow *main, QAction *action) : GraphWidget::GraphWidget(MainWindow *main, QAction *action) :
MemoryDockWidget(CutterCore::MemoryWidgetType::Graph, main, action) MemoryDockWidget(CutterCore::MemoryWidgetType::Graph, main, action)
{ {
/* setObjectName(getWidgetType());
* Ugly hack just for the layout issue
* QSettings saves the state with the object names
* By doing this hack,
* you can at least avoid some mess by dismissing all the Extra Widgets
*/
QString name = "Graph";
if (!action) {
name = "Extra Graph";
}
setObjectName(name);
setAllowedAreas(Qt::AllDockWidgetAreas); setAllowedAreas(Qt::AllDockWidgetAreas);
graphView = new DisassemblerGraphView(this); graphView = new DisassemblerGraphView(this, seekable);
setWidget(graphView); setWidget(graphView);
// getting the name of the class is implementation defined, and cannot be // getting the name of the class is implementation defined, and cannot be
@ -30,6 +20,8 @@ GraphWidget::GraphWidget(MainWindow *main, QAction *action) :
main->updateDockActionChecked(action); main->updateDockActionChecked(action);
}); });
connect(graphView, &DisassemblerGraphView::nameChanged, this, &MemoryDockWidget::updateWindowTitle);
connect(this, &QDockWidget::visibilityChanged, this, [ = ](bool visibility) { connect(this, &QDockWidget::visibilityChanged, this, [ = ](bool visibility) {
main->toggleOverview(visibility, this); main->toggleOverview(visibility, this);
if (visibility) { if (visibility) {
@ -54,7 +46,17 @@ void GraphWidget::closeEvent(QCloseEvent *event)
emit graphClosed(); emit graphClosed();
} }
QString GraphWidget::getWindowTitle() const
{
return graphView->windowTitle;
}
DisassemblerGraphView *GraphWidget::getGraphView() const DisassemblerGraphView *GraphWidget::getGraphView() const
{ {
return graphView; return graphView;
} }
QString GraphWidget::getWidgetType()
{
return "Graph";
}

View File

@ -12,10 +12,12 @@ class GraphWidget : public MemoryDockWidget
public: public:
explicit GraphWidget(MainWindow *main, QAction *action = nullptr); explicit GraphWidget(MainWindow *main, QAction *action = nullptr);
~GraphWidget() {} ~GraphWidget() override {}
DisassemblerGraphView *getGraphView() const; DisassemblerGraphView *getGraphView() const;
static QString getWidgetType();
signals: signals:
void graphClosed(); void graphClosed();
@ -25,6 +27,8 @@ protected:
private: private:
void closeEvent(QCloseEvent *event) override; void closeEvent(QCloseEvent *event) override;
QString getWindowTitle() const override;
DisassemblerGraphView *graphView; DisassemblerGraphView *graphView;
}; };

View File

@ -17,22 +17,11 @@
HexdumpWidget::HexdumpWidget(MainWindow *main, QAction *action) : HexdumpWidget::HexdumpWidget(MainWindow *main, QAction *action) :
MemoryDockWidget(CutterCore::MemoryWidgetType::Hexdump, main, action), MemoryDockWidget(CutterCore::MemoryWidgetType::Hexdump, main, action),
ui(new Ui::HexdumpWidget), ui(new Ui::HexdumpWidget)
seekable(new CutterSeekable(this))
{ {
ui->setupUi(this); ui->setupUi(this);
/* setObjectName(getWidgetType());
* Ugly hack just for the layout issue
* QSettings saves the state with the object names
* By doing this hack,
* you can at least avoid some mess by dismissing all the Extra Widgets
*/
QString name = "Hexdump";
if (!action) {
name = "Extra Hexdump";
}
setObjectName(name);
ui->copyMD5->setIcon(QIcon(":/img/icons/copy.svg")); ui->copyMD5->setIcon(QIcon(":/img/icons/copy.svg"));
ui->copySHA1->setIcon(QIcon(":/img/icons/copy.svg")); ui->copySHA1->setIcon(QIcon(":/img/icons/copy.svg"));
@ -75,13 +64,13 @@ HexdumpWidget::HexdumpWidget(MainWindow *main, QAction *action) :
" border-color : #3daee9" " border-color : #3daee9"
"}"); "}");
this->setWindowTitle(tr("Hexdump")); setWindowTitle(getWindowTitle());
refreshDeferrer = createReplacingRefreshDeferrer<RVA>(false, [this](const RVA *offset) { refreshDeferrer = createReplacingRefreshDeferrer<RVA>(false, [this](const RVA *offset) {
refresh(offset ? *offset : RVA_INVALID); refresh(offset ? *offset : RVA_INVALID);
}); });
connect(&syncAction, SIGNAL(triggered(bool)), this, SLOT(toggleSync())); connect(&syncAction, &QAction::triggered, seekable, &CutterSeekable::toggleSynchronization);
syncAction.setText(tr("Sync/unsync offset")); syncAction.setText(tr("Sync/unsync offset"));
this->ui->hexTextView->addAction(&syncAction); this->ui->hexTextView->addAction(&syncAction);
@ -121,6 +110,11 @@ void HexdumpWidget::onSeekChanged(RVA addr)
HexdumpWidget::~HexdumpWidget() {} HexdumpWidget::~HexdumpWidget() {}
QString HexdumpWidget::getWidgetType()
{
return "Hexdump";
}
void HexdumpWidget::refresh() void HexdumpWidget::refresh()
{ {
refresh(RVA_INVALID); refresh(RVA_INVALID);
@ -169,19 +163,6 @@ void HexdumpWidget::on_parseBitsComboBox_currentTextChanged(const QString &/*arg
refreshSelectionInfo(); refreshSelectionInfo();
} }
void HexdumpWidget::toggleSync()
{
QString windowTitle = tr("Hexdump");
seekable->toggleSynchronization();
if (seekable->isSynchronized()) {
setWindowTitle(windowTitle);
} else {
setWindowTitle(windowTitle + CutterSeekable::tr(" (unsynced)"));
}
}
void HexdumpWidget::setupFonts() void HexdumpWidget::setupFonts()
{ {
QFont font = Config()->getFont(); QFont font = Config()->getFont();
@ -216,6 +197,11 @@ void HexdumpWidget::showSidePanel(bool show)
} }
} }
QString HexdumpWidget::getWindowTitle() const
{
return tr("Hexdump");
}
void HexdumpWidget::updateParseWindow(RVA start_address, int size) void HexdumpWidget::updateParseWindow(RVA start_address, int size)
{ {
if (!ui->hexSideTab_2->isVisible()) { if (!ui->hexSideTab_2->isVisible()) {

View File

@ -34,10 +34,11 @@ public:
~HexdumpWidget() override; ~HexdumpWidget() override;
Highlighter *highlighter; Highlighter *highlighter;
static QString getWidgetType();
public slots: public slots:
void initParsing(); void initParsing();
void toggleSync();
protected: protected:
virtual void resizeEvent(QResizeEvent *event) override; virtual void resizeEvent(QResizeEvent *event) override;
QWidget *widgetToFocusOnRaise() override; QWidget *widgetToFocusOnRaise() override;
@ -59,8 +60,9 @@ private:
void clearParseWindow(); void clearParseWindow();
void showSidePanel(bool show); void showSidePanel(bool show);
QString getWindowTitle() const override;
QAction syncAction; QAction syncAction;
CutterSeekable *seekable;
private slots: private slots:
void onSeekChanged(RVA addr); void onSeekChanged(RVA addr);

View File

@ -1,12 +1,14 @@
#include "MemoryDockWidget.h" #include "MemoryDockWidget.h"
#include "common/CutterSeekable.h"
#include <QAction> #include <QAction>
MemoryDockWidget::MemoryDockWidget(CutterCore::MemoryWidgetType type, MainWindow *parent, QAction *action) MemoryDockWidget::MemoryDockWidget(CutterCore::MemoryWidgetType type, MainWindow *parent, QAction *action)
: CutterDockWidget(parent, action) : CutterDockWidget(parent, action)
, mType(type) , mType(type), seekable(new CutterSeekable(this))
{ {
connect(Core(), &CutterCore::raisePrioritizedMemoryWidget, this, &MemoryDockWidget::handleRaiseMemoryWidget); connect(Core(), &CutterCore::raisePrioritizedMemoryWidget, this, &MemoryDockWidget::handleRaiseMemoryWidget);
connect(seekable, &CutterSeekable::syncChanged, this, &MemoryDockWidget::updateWindowTitle);
} }
void MemoryDockWidget::handleRaiseMemoryWidget(CutterCore::MemoryWidgetType raiseType) void MemoryDockWidget::handleRaiseMemoryWidget(CutterCore::MemoryWidgetType raiseType)
@ -26,3 +28,17 @@ void MemoryDockWidget::handleRaiseMemoryWidget(CutterCore::MemoryWidgetType rais
widgetToFocusOnRaise()->setFocus(Qt::FocusReason::TabFocusReason); widgetToFocusOnRaise()->setFocus(Qt::FocusReason::TabFocusReason);
} }
} }
void MemoryDockWidget::updateWindowTitle()
{
if (seekable->isSynchronized()) {
setWindowTitle(getWindowTitle());
} else {
setWindowTitle(getWindowTitle() + CutterSeekable::tr(" (unsynced)"));
}
}
CutterSeekable* MemoryDockWidget::getSeekable() const
{
return seekable;
}

View File

@ -4,6 +4,8 @@
#include "CutterDockWidget.h" #include "CutterDockWidget.h"
#include "core/Cutter.h" #include "core/Cutter.h"
class CutterSeekable;
class MemoryDockWidget : public CutterDockWidget class MemoryDockWidget : public CutterDockWidget
{ {
Q_OBJECT Q_OBJECT
@ -11,10 +13,20 @@ public:
MemoryDockWidget(CutterCore::MemoryWidgetType type, MainWindow *parent, QAction *action = nullptr); MemoryDockWidget(CutterCore::MemoryWidgetType type, MainWindow *parent, QAction *action = nullptr);
~MemoryDockWidget() {} ~MemoryDockWidget() {}
CutterSeekable* getSeekable() const;
private: private:
void handleRaiseMemoryWidget(CutterCore::MemoryWidgetType raiseType); void handleRaiseMemoryWidget(CutterCore::MemoryWidgetType raiseType);
CutterCore::MemoryWidgetType mType; CutterCore::MemoryWidgetType mType;
public slots:
void updateWindowTitle();
protected:
CutterSeekable *seekable = nullptr;
virtual QString getWindowTitle() const = 0;
}; };
#endif // MEMORYDOCKWIDGET_H #endif // MEMORYDOCKWIDGET_H

View File

@ -86,6 +86,11 @@ void PseudocodeWidget::setupFonts()
ui->textEdit->setFont(font); ui->textEdit->setFont(font);
} }
QString PseudocodeWidget::getWindowTitle() const
{
return tr("Pseudocode");
}
void PseudocodeWidget::fontsUpdated() void PseudocodeWidget::fontsUpdated()
{ {
setupFonts(); setupFonts();

View File

@ -34,6 +34,8 @@ private:
void doRefresh(RVA addr); void doRefresh(RVA addr);
void setupFonts(); void setupFonts();
QString getWindowTitle() const override;
}; };
#endif // PSEUDOCODEWIDGET_H #endif // PSEUDOCODEWIDGET_H