Rewrite layout management code (#2172)

* Use QDockWidget::toggleViewAction instead of custom solution.
* Improve new dock placement.
This commit is contained in:
karliss 2020-05-22 14:49:34 +03:00 committed by GitHub
parent 8a3b51c291
commit 3545f059f1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
87 changed files with 841 additions and 964 deletions

View File

@ -1,6 +1,7 @@
View Menu View Menu
============================== ==============================
Refresh contents Refresh contents
---------------------------------------- ----------------------------------------
**Description:** In some cases, not all the displayed information on Cutter's widgets will be up-to-date, for example - after defining a new function from the integrated radare2 console. By refreshing the contents, Cutter will fetch the most up to date information from the session and will update the different views. **Description:** In some cases, not all the displayed information on Cutter's widgets will be up-to-date, for example - after defining a new function from the integrated radare2 console. By refreshing the contents, Cutter will fetch the most up to date information from the session and will update the different views.
@ -12,9 +13,9 @@ Refresh contents
Reset to default layout Reset to default layout
---------------------------------------- ----------------------------------------
**Description:** Reset the current layout to the default layout provided by Cutter. Future additions will include custom and user-defined layouts to which you'll be able to reset. **Description:** Reset the current layout to the default layout provided by Cutter.
**Steps:** View -> Reset Layout **Steps:** View -> Reset to default layout
Reset to default settings Reset to default settings
---------------------------------------- ----------------------------------------
@ -24,7 +25,7 @@ Reset to default settings
Lock and Unlock panels Lock and Unlock panels
---------------------------------------- ----------------------------------------
**Description:** Allow or disable locking the different widgets. **Description:** Allow or disable moving and closing of different widgets. Uncheck this option to prevent accidentally modifying current layout.
**Steps:** View -> Unlock Panels **Steps:** View -> Unlock Panels
@ -64,3 +65,16 @@ Reset Zoom
**Steps:** View -> Zoom -> Reset **Steps:** View -> Zoom -> Reset
**Shortcut:** :kbd:`Ctrl` + :kbd:`=` **Shortcut:** :kbd:`Ctrl` + :kbd:`=`
Save layout
----------------------------------------
**Description:** Save the current layout with a given name. A layout includes the set of currently opened widgets, their position, and some properties.
**Steps:** View -> Save Layout , enter a layout name in the dialog.
Layouts
----------------------------------------
**Description:** Load the settings from the selected layout into the current layout. Loading a layout will not cause it to automatically be modified. To do that you must use the `Save layout`_ command.
**Steps:** View -> Layouts -> layout name

View File

@ -421,7 +421,8 @@ SOURCES += \
widgets/ListDockWidget.cpp \ widgets/ListDockWidget.cpp \
dialogs/MultitypeFileSaveDialog.cpp \ dialogs/MultitypeFileSaveDialog.cpp \
widgets/BoolToggleDelegate.cpp \ widgets/BoolToggleDelegate.cpp \
common/IOModesController.cpp common/IOModesController.cpp \
common/SettingsUpgrade.cpp
GRAPHVIZ_SOURCES = \ GRAPHVIZ_SOURCES = \
widgets/GraphvizLayout.cpp widgets/GraphvizLayout.cpp
@ -568,7 +569,8 @@ HEADERS += \
widgets/AddressableItemList.h \ widgets/AddressableItemList.h \
dialogs/MultitypeFileSaveDialog.h \ dialogs/MultitypeFileSaveDialog.h \
widgets/BoolToggleDelegate.h \ widgets/BoolToggleDelegate.h \
common/IOModesController.h common/IOModesController.h \
common/SettingsUpgrade.h
GRAPHVIZ_HEADERS = widgets/GraphGridLayout.h GRAPHVIZ_HEADERS = widgets/GraphGridLayout.h

View File

@ -2,124 +2,13 @@
#include "CutterApplication.h" #include "CutterApplication.h"
#include "core/MainWindow.h" #include "core/MainWindow.h"
#include "common/UpdateWorker.h" #include "common/UpdateWorker.h"
#include "common/ColorThemeWorker.h"
#include "CutterConfig.h" #include "CutterConfig.h"
#include "common/CrashHandler.h" #include "common/CrashHandler.h"
#include "common/SettingsUpgrade.h"
#include <QJsonObject> #include <QJsonObject>
#include <QJsonArray> #include <QJsonArray>
/**
* @brief Migrate Settings used before Cutter 1.8
*
* @return whether any settings have been migrated
*/
static bool migrateSettingsPre18(QSettings &newSettings)
{
if(newSettings.value("settings_migrated", false).toBool()) {
return false;
}
QSettings oldSettings(QSettings::NativeFormat, QSettings::Scope::UserScope, "Cutter", "Cutter");
QStringList allKeys = oldSettings.allKeys();
if (allKeys.isEmpty()) {
return false;
}
qInfo() << "Migrating Settings from pre-1.8";
for (const QString &key : allKeys) {
newSettings.setValue(key, oldSettings.value(key));
}
oldSettings.clear();
QFile settingsFile(oldSettings.fileName());
settingsFile.remove();
newSettings.setValue("settings_migrated", true);
return true;
}
#define CUTTER_SETTINGS_VERSION_CURRENT 2
#define CUTTER_SETTINGS_VERSION_KEY "version"
/*
* How Settings migrations work:
*
* Every time settings are changed in a way that needs migration,
* CUTTER_SETTINGS_VERSION_CURRENT is raised by 1 and a function migrateSettingsToX
* is implemented and added to initializeSettings().
* This function takes care of migrating from EXACTLY version X-1 to X.
*/
static void migrateSettingsTo1(QSettings &settings) {
settings.remove("settings_migrated"); // now handled by version
settings.remove("updated_custom_themes"); // now handled by theme_version
}
static void migrateSettingsTo2(QSettings &settings) {
QStringList docks = settings.value("docks").toStringList(); // get current list of docks
// replace occurences of "PseudocodeWidget" with "DecompilerWidget"
settings.setValue("docks", docks.replaceInStrings("PseudocodeWidget", "DecompilerWidget"));
}
static void initializeSettings()
{
QSettings::setDefaultFormat(QSettings::IniFormat);
QSettings settings;
int settingsVersion = settings.value(CUTTER_SETTINGS_VERSION_KEY, 0).toInt();
if(settingsVersion == 0) {
migrateSettingsPre18(settings);
}
if(settings.allKeys().length() > 0) {
if (settingsVersion > CUTTER_SETTINGS_VERSION_CURRENT) {
qWarning() << "Settings have a higher version than current! Skipping migration.";
} else if(settingsVersion >= 0) {
for (int v = settingsVersion + 1; v <= CUTTER_SETTINGS_VERSION_CURRENT; v++) {
qInfo() << "Migrating Settings to Version" << v;
switch (v) {
case 1:
migrateSettingsTo1(settings); break;
case 2:
migrateSettingsTo2(settings);
default:
break;
}
}
}
}
settings.setValue(CUTTER_SETTINGS_VERSION_KEY, CUTTER_SETTINGS_VERSION_CURRENT);
}
#define THEME_VERSION_CURRENT 1
#define THEME_VERSION_KEY "theme_version"
static void removeObsoleteOptionsFromCustomThemes() {
const QStringList options = Core()->cmdj("ecj").object().keys()
<< ColorThemeWorker::cutterSpecificOptions;
for (auto theme : Core()->cmdList("eco*")) {
theme = theme.trimmed();
if (!ThemeWorker().isCustomTheme(theme)) {
continue;
}
QJsonObject updatedTheme;
auto sch = ThemeWorker().getTheme(theme).object();
for (const auto& key : sch.keys()) {
if (options.contains(key)) {
updatedTheme.insert(key, sch[key]);
}
}
ThemeWorker().save(QJsonDocument(updatedTheme), theme);
}
}
static void migrateThemes()
{
QSettings settings;
int themeVersion = settings.value(THEME_VERSION_KEY, 0).toInt();
if (themeVersion != THEME_VERSION_CURRENT) {
removeObsoleteOptionsFromCustomThemes();
settings.setValue(THEME_VERSION_KEY, THEME_VERSION_CURRENT);
}
}
/** /**
* @brief Attempt to connect to a parent console and configure outputs. * @brief Attempt to connect to a parent console and configure outputs.
@ -183,7 +72,7 @@ int main(int argc, char *argv[])
QCoreApplication::setOrganizationName("RadareOrg"); QCoreApplication::setOrganizationName("RadareOrg");
QCoreApplication::setApplicationName("Cutter"); QCoreApplication::setApplicationName("Cutter");
initializeSettings(); Cutter::initializeSettings();
QCoreApplication::setAttribute(Qt::AA_ShareOpenGLContexts); // needed for QtWebEngine inside Plugins QCoreApplication::setAttribute(Qt::AA_ShareOpenGLContexts); // needed for QtWebEngine inside Plugins
#ifdef Q_OS_WIN #ifdef Q_OS_WIN
@ -195,7 +84,7 @@ int main(int argc, char *argv[])
CutterApplication a(argc, argv); CutterApplication a(argc, argv);
migrateThemes(); Cutter::migrateThemes();
if (Config()->getAutoUpdateEnabled()) { if (Config()->getAutoUpdateEnabled()) {
#if CUTTER_UPDATE_WORKER_AVAILABLE #if CUTTER_UPDATE_WORKER_AVAILABLE

View File

@ -145,14 +145,14 @@ SizePolicyMinMax forceHeight(QWidget *widget, int height)
void SizePolicyMinMax::restoreWidth(QWidget *widget) void SizePolicyMinMax::restoreWidth(QWidget *widget)
{ {
widget->setSizePolicy(sizePolicy); widget->setSizePolicy(sizePolicy.horizontalPolicy(), widget->sizePolicy().verticalPolicy());
widget->setMinimumWidth(min); widget->setMinimumWidth(min);
widget->setMaximumWidth(max); widget->setMaximumWidth(max);
} }
void SizePolicyMinMax::restoreHeight(QWidget *widget) void SizePolicyMinMax::restoreHeight(QWidget *widget)
{ {
widget->setSizePolicy(sizePolicy); widget->setSizePolicy(widget->sizePolicy().horizontalPolicy(), sizePolicy.verticalPolicy());
widget->setMinimumHeight(min); widget->setMinimumHeight(min);
widget->setMaximumHeight(max); widget->setMaximumHeight(max);
} }

View File

@ -0,0 +1,168 @@
#include "SettingsUpgrade.h"
#include "common/ColorThemeWorker.h"
/**
* @brief Migrate Settings used before Cutter 1.8
*
* @return whether any settings have been migrated
*/
static bool migrateSettingsPre18(QSettings &newSettings)
{
if(newSettings.value("settings_migrated", false).toBool()) {
return false;
}
QSettings oldSettings(QSettings::NativeFormat, QSettings::Scope::UserScope, "Cutter", "Cutter");
QStringList allKeys = oldSettings.allKeys();
if (allKeys.isEmpty()) {
return false;
}
qInfo() << "Migrating Settings from pre-1.8";
for (const QString &key : allKeys) {
newSettings.setValue(key, oldSettings.value(key));
}
oldSettings.clear();
QFile settingsFile(oldSettings.fileName());
settingsFile.remove();
newSettings.setValue("settings_migrated", true);
return true;
}
#define CUTTER_SETTINGS_VERSION_CURRENT 3
#define CUTTER_SETTINGS_VERSION_KEY "version"
/*
* How Settings migrations work:
*
* Every time settings are changed in a way that needs migration,
* CUTTER_SETTINGS_VERSION_CURRENT is raised by 1 and a function migrateSettingsToX
* is implemented and added to initializeSettings().
* This function takes care of migrating from EXACTLY version X-1 to X.
*/
static void migrateSettingsTo1(QSettings &settings) {
settings.remove("settings_migrated"); // now handled by version
settings.remove("updated_custom_themes"); // now handled by theme_version
}
static void migrateSettingsTo2(QSettings &settings) {
QStringList docks = settings.value("docks").toStringList(); // get current list of docks
// replace occurences of "PseudocodeWidget" with "DecompilerWidget"
settings.setValue("docks", docks.replaceInStrings("PseudocodeWidget", "DecompilerWidget"));
}
static void migrateSettingsTo3(QSettings &settings) {
auto defaultGeometry = settings.value("geometry").toByteArray();
auto defaultState = settings.value("state").toByteArray();
auto debugGeometry = settings.value("debug.geometry").toByteArray();
auto debugState = settings.value("debug.state").toByteArray();
const auto docks = settings.value("docks", QStringList()).toStringList();
auto unsyncList = settings.value("unsync", QStringList()).toStringList();
#if QT_VERSION < QT_VERSION_CHECK(5,14,0)
QSet<QString> unsyncDocks = unsyncList.toSet();
#else
QSet<QString> unsyncDocks(unsyncList.begin(), unsyncList.end());
#endif
QVariantMap viewProperties;
for (auto &dock : docks) {
QVariantMap properties;
bool synchronized = true;
if (unsyncDocks.contains(dock)) {
synchronized = false;
}
properties.insert("synchronized", synchronized);
viewProperties.insert(dock, properties);
}
settings.beginWriteArray("layouts", 2);
settings.setArrayIndex(0);
settings.setValue("name", "Default");
settings.setValue("geometry", defaultGeometry);
settings.setValue("state", defaultState);
settings.setValue("docks", viewProperties);
settings.setArrayIndex(1);
settings.setValue("name", "Debug");
settings.setValue("geometry", debugGeometry);
settings.setValue("state", debugState);
settings.setValue("docks", viewProperties);
settings.endArray();
settings.remove("pos"); // Pos and size already stored within geometry
settings.remove("size");
// keep geometry but with slightly different usecase
settings.remove("state");
settings.remove("debug.geometry");
settings.remove("debug.state");
settings.remove("docks");
settings.remove("unsync");
}
void Cutter::initializeSettings()
{
QSettings::setDefaultFormat(QSettings::IniFormat);
QSettings settings;
int settingsVersion = settings.value(CUTTER_SETTINGS_VERSION_KEY, 0).toInt();
if(settingsVersion == 0) {
migrateSettingsPre18(settings);
}
if(settings.allKeys().length() > 0) {
if (settingsVersion > CUTTER_SETTINGS_VERSION_CURRENT) {
qWarning() << "Settings have a higher version than current! Skipping migration.";
} else if(settingsVersion >= 0) {
for (int v = settingsVersion + 1; v <= CUTTER_SETTINGS_VERSION_CURRENT; v++) {
qInfo() << "Migrating Settings to Version" << v;
switch (v) {
case 1:
migrateSettingsTo1(settings); break;
case 2:
migrateSettingsTo2(settings); break;
case 3:
migrateSettingsTo3(settings); break;
default:
break;
}
}
}
}
settings.setValue(CUTTER_SETTINGS_VERSION_KEY, CUTTER_SETTINGS_VERSION_CURRENT);
}
#define THEME_VERSION_CURRENT 1
#define THEME_VERSION_KEY "theme_version"
static void removeObsoleteOptionsFromCustomThemes() {
const QStringList options = Core()->cmdj("ecj").object().keys()
<< ColorThemeWorker::cutterSpecificOptions;
for (auto theme : Core()->cmdList("eco*")) {
theme = theme.trimmed();
if (!ThemeWorker().isCustomTheme(theme)) {
continue;
}
QJsonObject updatedTheme;
auto sch = ThemeWorker().getTheme(theme).object();
for (const auto& key : sch.keys()) {
if (options.contains(key)) {
updatedTheme.insert(key, sch[key]);
}
}
ThemeWorker().save(QJsonDocument(updatedTheme), theme);
}
}
void Cutter::migrateThemes()
{
QSettings settings;
int themeVersion = settings.value(THEME_VERSION_KEY, 0).toInt();
if (themeVersion != THEME_VERSION_CURRENT) {
removeObsoleteOptionsFromCustomThemes();
settings.setValue(THEME_VERSION_KEY, THEME_VERSION_CURRENT);
}
}

View File

@ -0,0 +1,12 @@
#ifndef COMMON_SETTINGS_UPGRADE_H
#define COMMON_SETTINGS_UPGRADE_H
#include <QSettings>
#include <core/Cutter.h>
namespace Cutter {
void initializeSettings();
void migrateThemes();
}
#endif // COMMON_SETTINGS_UPGRADE_H

View File

@ -61,5 +61,12 @@ inline QString RHexString(RVA size)
#define CUTTER_EXPORT Q_DECL_IMPORT #define CUTTER_EXPORT Q_DECL_IMPORT
#endif #endif
#if defined(__has_cpp_attribute) && __has_cpp_attribute(deprecated)
#define CUTTER_DEPRECATED(msg) [[deprecated(msg)]]
#else
#define CUTTER_DEPRECATED(msg)
#endif
#endif // CUTTERCORE_H #endif // CUTTERCORE_H

View File

@ -91,6 +91,7 @@
#include <QSysInfo> #include <QSysInfo>
#include <QJsonObject> #include <QJsonObject>
#include <QJsonArray> #include <QJsonArray>
#include <QInputDialog>
#include <QScrollBar> #include <QScrollBar>
#include <QSettings> #include <QSettings>
@ -111,7 +112,14 @@
#include <QGraphicsView> #include <QGraphicsView>
template<class T> template<class T>
T* getNewInstance(MainWindow *m, QAction *a) { return new T(m, a); } T *getNewInstance(MainWindow *m) { return new T(m); }
static const QString LAYOUT_DEFAULT = "Default";
static const QString LAYOUT_DEBUG = "Debug";
static bool isBuiltinLayoutName(const QString &name) {
return name == LAYOUT_DEFAULT || name == LAYOUT_DEBUG;
}
MainWindow::MainWindow(QWidget *parent) : MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent), QMainWindow(parent),
@ -147,7 +155,8 @@ void MainWindow::initUI()
connect(Core(), &CutterCore::ioCacheChanged, ui->actionCommitChanges, &QAction::setEnabled); connect(Core(), &CutterCore::ioCacheChanged, ui->actionCommitChanges, &QAction::setEnabled);
widgetTypeToConstructorMap.insert(GraphWidget::getWidgetType(), getNewInstance<GraphWidget>); widgetTypeToConstructorMap.insert(GraphWidget::getWidgetType(), getNewInstance<GraphWidget>);
widgetTypeToConstructorMap.insert(DisassemblyWidget::getWidgetType(), getNewInstance<DisassemblyWidget>); widgetTypeToConstructorMap.insert(DisassemblyWidget::getWidgetType(),
getNewInstance<DisassemblyWidget>);
widgetTypeToConstructorMap.insert(HexdumpWidget::getWidgetType(), getNewInstance<HexdumpWidget>); widgetTypeToConstructorMap.insert(HexdumpWidget::getWidgetType(), getNewInstance<HexdumpWidget>);
initToolBar(); initToolBar();
@ -202,6 +211,8 @@ void MainWindow::initUI()
initBackForwardMenu(); initBackForwardMenu();
connect(ui->actionSaveLayout, &QAction::triggered, this, &MainWindow::saveNamedLayout);
/* Setup plugins interfaces */ /* Setup plugins interfaces */
for (auto &plugin : Plugins()->getPlugins()) { for (auto &plugin : Plugins()->getPlugins()) {
plugin->setupInterface(this); plugin->setupInterface(this);
@ -211,7 +222,8 @@ void MainWindow::initUI()
ui->actionGrouped_dock_dragging->setVisible(false); ui->actionGrouped_dock_dragging->setVisible(false);
#endif #endif
initLayout(); enableDebugWidgetsMenu(false);
readSettings();
} }
void MainWindow::initToolBar() void MainWindow::initToolBar()
@ -296,85 +308,97 @@ void MainWindow::initToolBar()
void MainWindow::initDocks() void MainWindow::initDocks()
{ {
dockWidgets.reserve(20); dockWidgets.reserve(20);
decompilerDock = new DecompilerWidget(this, ui->actionDecompiler); decompilerDock = new DecompilerWidget(this);
consoleDock = new ConsoleWidget(this, ui->actionConsole); consoleDock = new ConsoleWidget(this);
overviewDock = new OverviewWidget(this, ui->actionOverview); overviewDock = new OverviewWidget(this);
overviewDock->hide(); overviewDock->hide();
actionOverview = overviewDock->toggleViewAction();
connect(overviewDock, &OverviewWidget::isAvailableChanged, this, [this](bool isAvailable) { connect(overviewDock, &OverviewWidget::isAvailableChanged, this, [this](bool isAvailable) {
ui->actionOverview->setEnabled(isAvailable); actionOverview->setEnabled(isAvailable);
});
ui->actionOverview->setEnabled(overviewDock->getIsAvailable());
connect(ui->actionOverview, &QAction::toggled, [this](bool checked) {
if (checked) {
overviewDock->show();
} else {
overviewDock->hide();
}
}); });
actionOverview->setEnabled(overviewDock->getIsAvailable());
actionOverview->setChecked(overviewDock->getUserOpened());
ui->actionOverview->setChecked(overviewDock->getUserOpened()); dashboardDock = new Dashboard(this);
sectionsDock = new SectionsWidget(this, ui->actionSections); functionsDock = new FunctionsWidget(this);
segmentsDock = new SegmentsWidget(this, ui->actionSegments); typesDock = new TypesWidget(this);
entrypointDock = new EntrypointWidget(this, ui->actionEntrypoints); searchDock = new SearchWidget(this);
functionsDock = new FunctionsWidget(this, ui->actionFunctions); commentsDock = new CommentsWidget(this);
importsDock = new ImportsWidget(this, ui->actionImports); stringsDock = new StringsWidget(this);
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);
commentsDock = new CommentsWidget(this, ui->actionComments);
stringsDock = new StringsWidget(this, ui->actionStrings);
flagsDock = new FlagsWidget(this, ui->actionFlags);
stackDock = new StackWidget(this, ui->actionStack);
threadsDock = new ThreadsWidget(this, ui->actionThreads);
processesDock = new ProcessesWidget(this, ui->actionProcesses);
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);
dashboardDock = new Dashboard(this, ui->actionDashboard);
sdbDock = new SdbWidget(this, ui->actionSDBBrowser);
classesDock = new ClassesWidget(this, ui->actionClasses);
resourcesDock = new ResourcesWidget(this, ui->actionResources);
vTablesDock = new VTablesWidget(this, ui->actionVTables);
QSettings s; QList<CutterDockWidget *> debugDocks = {
QStringList docks = s.value("docks", QStringList { stackDock = new StackWidget(this),
DisassemblyWidget::getWidgetType(), threadsDock = new ThreadsWidget(this),
GraphWidget::getWidgetType(), processesDock = new ProcessesWidget(this),
HexdumpWidget::getWidgetType() backtraceDock = new BacktraceWidget(this),
}).toStringList(); registersDock = new RegistersWidget(this),
memoryMapDock = new MemoryMapWidget(this),
breakpointDock = new BreakpointWidget(this),
registerRefsDock = new RegisterRefsWidget(this)
};
// Restore all extra widgets QList<CutterDockWidget *> infoDocks = {
QString className; classesDock = new ClassesWidget(this),
for (const auto &it : docks) { entrypointDock = new EntrypointWidget(this),
if (std::none_of(dockWidgets.constBegin(), dockWidgets.constEnd(), exportsDock = new ExportsWidget(this),
[&it](QDockWidget * w) { return w->objectName() == it; })) { flagsDock = new FlagsWidget(this),
className = it.split(';').at(0); headersDock = new HeadersWidget(this),
if (widgetTypeToConstructorMap.contains(className)) { importsDock = new ImportsWidget(this),
auto widget = widgetTypeToConstructorMap[className](this, nullptr); relocsDock = new RelocsWidget(this),
widget->setObjectName(it); resourcesDock = new ResourcesWidget(this),
addExtraWidget(widget); sdbDock = new SdbWidget(this),
sectionsDock = new SectionsWidget(this),
segmentsDock = new SegmentsWidget(this),
symbolsDock = new SymbolsWidget(this),
vTablesDock = new VTablesWidget(this),
zignaturesDock = new ZignaturesWidget(this)
};
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);
} }
} }
return result;
};
QList<CutterDockWidget *> windowDocks = {
dashboardDock,
nullptr,
functionsDock,
decompilerDock,
overviewDock,
nullptr,
searchDock,
stringsDock,
typesDock,
nullptr,
};
ui->menuWindows->insertActions(ui->actionExtraDisassembly, makeActionList(windowDocks));
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);
}
} }
} }
void MainWindow::initLayout()
{
// Set up dock widgets default layout
enableDebugWidgetsMenu(false);
// Restore saved settings
readSettingsOrDefault();
initCorners();
}
void MainWindow::toggleOverview(bool visibility, GraphWidget *targetGraph) void MainWindow::toggleOverview(bool visibility, GraphWidget *targetGraph)
{ {
if (!overviewDock) { if (!overviewDock) {
@ -393,29 +417,29 @@ void MainWindow::updateTasksIndicator()
void MainWindow::addExtraGraph() void MainWindow::addExtraGraph()
{ {
auto *extraDock = new GraphWidget(this, nullptr); auto *extraDock = new GraphWidget(this);
addExtraWidget(extraDock); addExtraWidget(extraDock);
} }
void MainWindow::addExtraHexdump() void MainWindow::addExtraHexdump()
{ {
auto *extraDock = new HexdumpWidget(this, nullptr); auto *extraDock = new HexdumpWidget(this);
addExtraWidget(extraDock); addExtraWidget(extraDock);
} }
void MainWindow::addExtraDisassembly() void MainWindow::addExtraDisassembly()
{ {
auto *extraDock = new DisassemblyWidget(this, nullptr); auto *extraDock = new DisassemblyWidget(this);
addExtraWidget(extraDock); addExtraWidget(extraDock);
} }
void MainWindow::addExtraWidget(CutterDockWidget *extraDock) void MainWindow::addExtraWidget(CutterDockWidget *extraDock)
{ {
extraDock->setTransient(true); extraDock->setTransient(true);
addDockWidget(Qt::TopDockWidgetArea, extraDock, Qt::Orientation::Horizontal); dockOnMainArea(extraDock);
auto restoreExtraDock = qhelpers::forceWidth(extraDock->widget(), 600); addWidget(extraDock);
qApp->processEvents(); extraDock->show();
restoreExtraDock.restoreWidth(extraDock->widget()); extraDock->raise();
} }
QMenu *MainWindow::getMenuByType(MenuType type) QMenu *MainWindow::getMenuByType(MenuType type)
@ -440,14 +464,12 @@ QMenu *MainWindow::getMenuByType(MenuType type)
} }
} }
void MainWindow::addPluginDockWidget(QDockWidget *dockWidget, QAction *action) void MainWindow::addPluginDockWidget(CutterDockWidget *dockWidget)
{ {
addDockWidget(Qt::TopDockWidgetArea, dockWidget);
dockWidget->addAction(action);
addWidget(dockWidget); addWidget(dockWidget);
ui->menuPlugins->addAction(action); ui->menuPlugins->addAction(dockWidget->toggleViewAction());
addDockWidget(Qt::DockWidgetArea::TopDockWidgetArea, dockWidget); addDockWidget(Qt::DockWidgetArea::TopDockWidgetArea, dockWidget);
updateDockActionChecked(action); pluginDocks.push_back(dockWidget);
} }
void MainWindow::addMenuFileAction(QAction *action) void MainWindow::addMenuFileAction(QAction *action)
@ -546,21 +568,21 @@ void MainWindow::finalizeOpen()
core->getOpcodes(); core->getOpcodes();
core->updateSeek(); core->updateSeek();
refreshAll(); refreshAll();
// Add fortune message // Add fortune message
core->message("\n" + core->cmdRaw("fo")); core->message("\n" + core->cmdRaw("fo"));
showMaximized();
Config()->adjustColorThemeDarkness();
QSettings s; QSettings settings;
QStringList unsync = s.value("unsync").toStringList(); auto geometry = settings.value("geometry").toByteArray();
for (auto it : dockWidgets) { if (!geometry.isEmpty()) {
auto w = qobject_cast<MemoryDockWidget*>(it); restoreGeometry(geometry);
if (w) { show();
w->getSeekable()->setSynchronization(!unsync.contains(it->objectName())); } else {
} showMaximized();
} }
Config()->adjustColorThemeDarkness();
setViewLayout(getViewLayout(LAYOUT_DEFAULT));
// Set focus to disasm or graph widget // Set focus to disasm or graph widget
// Use for loop to cover cases when main disasm/graph // Use for loop to cover cases when main disasm/graph
@ -577,7 +599,7 @@ void MainWindow::finalizeOpen()
bool graphContainsFunc = false; bool graphContainsFunc = false;
for (auto dockWidget : dockWidgets) { for (auto dockWidget : dockWidgets) {
const QString className = dockWidget->metaObject()->className(); const QString className = dockWidget->metaObject()->className();
auto graphWidget = qobject_cast<GraphWidget*>(dockWidget); auto graphWidget = qobject_cast<GraphWidget *>(dockWidget);
if (graphWidget && !dockWidget->visibleRegion().isNull()) { if (graphWidget && !dockWidget->visibleRegion().isNull()) {
graphContainsFunc = !graphWidget->getGraphView()->getBlocks().empty(); graphContainsFunc = !graphWidget->getGraphView()->getBlocks().empty();
if (graphContainsFunc) { if (graphContainsFunc) {
@ -585,7 +607,7 @@ void MainWindow::finalizeOpen()
break; break;
} }
} }
auto disasmWidget = qobject_cast<DisassemblyWidget*>(dockWidget); auto disasmWidget = qobject_cast<DisassemblyWidget *>(dockWidget);
if (disasmWidget && !dockWidget->visibleRegion().isNull()) { if (disasmWidget && !dockWidget->visibleRegion().isNull()) {
if (!graphContainsFunc) { if (!graphContainsFunc) {
disasmWidget->setFocus(); disasmWidget->setFocus();
@ -671,41 +693,18 @@ void MainWindow::paintEvent(QPaintEvent *event)
QMainWindow::paintEvent(event); QMainWindow::paintEvent(event);
/* /*
* Dirty hack * Dirty hack
* Just to adjust the width of Functions Widget to fixed size * Just to adjust the width of Functions Widget to fixed size.
* After everything is drawn, safely make it Preferred size policy * After everything is drawn, restore the max width limit.
* So that user can change the widget size with the mouse
*/ */
if (functionsDock) { if (functionsDock && functionDockWidthToRestore) {
functionsDock->changeSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred); functionsDock->setMaximumWidth(functionDockWidthToRestore);
functionDockWidthToRestore = 0;
} }
} }
void MainWindow::readSettingsOrDefault() void MainWindow::readSettings()
{ {
QSettings settings; QSettings settings;
QByteArray geo = settings.value("geometry", QByteArray()).toByteArray();
QByteArray state = settings.value("state", QByteArray()).toByteArray();
/*
* Check if saved settings exist
* If not, then read the default layout
*/
if (!geo.length() || !state.length()) {
resetToDefaultLayout();
return;
}
hideAllDocks();
restoreGeometry(geo);
restoreState(state);
// make sure all DockWidgets are part of the MainWindow
// also show them, so newly installed plugin widgets are shown right away
for (auto dockWidget : dockWidgets) {
if (dockWidgetArea(dockWidget) == Qt::DockWidgetArea::NoDockWidgetArea &&
!isDebugWidget(dockWidget)) {
addDockWidget(Qt::DockWidgetArea::TopDockWidgetArea, dockWidget);
dockWidget->show();
}
}
responsive = settings.value("responsive").toBool(); responsive = settings.value("responsive").toBool();
panelLock = settings.value("panelLock").toBool(); panelLock = settings.value("panelLock").toBool();
@ -716,59 +715,22 @@ void MainWindow::readSettingsOrDefault()
ui->actionGrouped_dock_dragging->setChecked(dockGroupedDragging); ui->actionGrouped_dock_dragging->setChecked(dockGroupedDragging);
on_actionGrouped_dock_dragging_triggered(dockGroupedDragging); on_actionGrouped_dock_dragging_triggered(dockGroupedDragging);
QPoint pos = settings.value("pos", QPoint(200, 200)).toPoint(); loadLayouts(settings);
QSize size = settings.value("size", QSize(400, 400)).toSize();
resize(size);
move(pos);
updateDockActionsChecked();
} }
void MainWindow::saveSettings() void MainWindow::saveSettings()
{ {
QSettings settings; QSettings settings;
QStringList docks;
QStringList unsync;
for (const auto &it : dockWidgets) {
docks.append(it->objectName());
auto memoryDockWidget = qobject_cast<MemoryDockWidget*>(it);
if (memoryDockWidget && !memoryDockWidget->getSeekable()->isSynchronized()) {
unsync.append(it->objectName());
}
}
settings.setValue("docks", docks);
settings.setValue("unsync", unsync);
settings.setValue("geometry", saveGeometry());
settings.setValue("size", size());
settings.setValue("pos", pos());
settings.setValue("state", saveState());
settings.setValue("panelLock", panelLock); settings.setValue("panelLock", panelLock);
settings.setValue("tabsOnTop", tabsOnTop); settings.setValue("tabsOnTop", tabsOnTop);
settings.setValue("docksGroupedDragging", ui->actionGrouped_dock_dragging->isChecked()); settings.setValue("docksGroupedDragging", ui->actionGrouped_dock_dragging->isChecked());
settings.setValue("geometry", saveGeometry());
layouts[Core()->currentlyDebugging ? LAYOUT_DEBUG : LAYOUT_DEFAULT] = getViewLayout();
saveLayouts(settings);
} }
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());
}
void MainWindow::setPanelLock() void MainWindow::setPanelLock()
{ {
@ -819,12 +781,19 @@ void MainWindow::lockUnlock_Docks(bool what)
void MainWindow::restoreDocks() void MainWindow::restoreDocks()
{ {
// In the upper half the functions are the first widget // Initial structure
addDockWidget(Qt::TopDockWidgetArea, functionsDock); // func | main area | debug
addDockWidget(Qt::TopDockWidgetArea, overviewDock); // |___________|
// | console |
// Function | Dashboard addDockWidget(Qt::LeftDockWidgetArea, functionsDock);
splitDockWidget(functionsDock, dashboardDock, Qt::Horizontal); splitDockWidget(functionsDock, dashboardDock, Qt::Horizontal);
splitDockWidget(dashboardDock, stackDock, Qt::Horizontal);
splitDockWidget(dashboardDock, consoleDock, Qt::Vertical);
// overview bellow func
splitDockWidget(functionsDock, overviewDock, Qt::Vertical);
// main area
tabifyDockWidget(dashboardDock, decompilerDock); tabifyDockWidget(dashboardDock, decompilerDock);
tabifyDockWidget(dashboardDock, entrypointDock); tabifyDockWidget(dashboardDock, entrypointDock);
tabifyDockWidget(dashboardDock, flagsDock); tabifyDockWidget(dashboardDock, flagsDock);
@ -846,46 +815,24 @@ void MainWindow::restoreDocks()
tabifyDockWidget(dashboardDock, registerRefsDock); tabifyDockWidget(dashboardDock, registerRefsDock);
for (const auto &it : dockWidgets) { for (const auto &it : dockWidgets) {
// Check whether or not current widgets is graph, hexdump or disasm // Check whether or not current widgets is graph, hexdump or disasm
if (qobject_cast<GraphWidget*>(it) || if (isExtraMemoryWidget(it)) {
qobject_cast<HexdumpWidget*>(it) ||
qobject_cast<DisassemblyWidget*>(it)) {
tabifyDockWidget(dashboardDock, it); tabifyDockWidget(dashboardDock, it);
} }
} }
splitDockWidget(functionsDock, overviewDock, Qt::Vertical); // Console | Sections/segments/comments
// In the lower half the console is the first widget
addDockWidget(Qt::BottomDockWidgetArea, consoleDock);
// Console | Sections
splitDockWidget(consoleDock, sectionsDock, Qt::Horizontal); splitDockWidget(consoleDock, sectionsDock, Qt::Horizontal);
splitDockWidget(consoleDock, segmentsDock, Qt::Horizontal); tabifyDockWidget(sectionsDock, segmentsDock);
tabifyDockWidget(sectionsDock, commentsDock); tabifyDockWidget(sectionsDock, commentsDock);
// Add Stack, Registers, Threads and Backtrace vertically stacked // Add Stack, Registers, Threads and Backtrace vertically stacked
addDockWidget(Qt::TopDockWidgetArea, stackDock);
splitDockWidget(stackDock, registersDock, Qt::Vertical); splitDockWidget(stackDock, registersDock, Qt::Vertical);
tabifyDockWidget(stackDock, backtraceDock); tabifyDockWidget(stackDock, backtraceDock);
tabifyDockWidget(backtraceDock, threadsDock); tabifyDockWidget(backtraceDock, threadsDock);
tabifyDockWidget(threadsDock, processesDock); tabifyDockWidget(threadsDock, processesDock);
updateDockActionsChecked(); for (auto dock : pluginDocks) {
} dockOnMainArea(dock);
void MainWindow::hideAllDocks()
{
for (auto w : dockWidgets) {
removeDockWidget(w);
}
}
void MainWindow::updateDockActionsChecked()
{
for (auto i = dockWidgetsOfAction.constBegin(); i != dockWidgetsOfAction.constEnd(); i++) {
updateDockActionChecked(i.key());
} }
} }
@ -901,6 +848,13 @@ bool MainWindow::isDebugWidget(QDockWidget *dock) const
dock == registerRefsDock; dock == registerRefsDock;
} }
bool MainWindow::isExtraMemoryWidget(QDockWidget *dock) const
{
return qobject_cast<GraphWidget*>(dock) ||
qobject_cast<HexdumpWidget*>(dock) ||
qobject_cast<DisassemblyWidget*>(dock);
}
MemoryWidgetType MainWindow::getMemoryWidgetTypeToRestore() MemoryWidgetType MainWindow::getMemoryWidgetTypeToRestore()
{ {
if (lastSyncMemoryWidget) { if (lastSyncMemoryWidget) {
@ -963,7 +917,7 @@ QMenu *MainWindow::createShowInMenu(QWidget *parent, RVA address)
for (auto &dock : dockWidgets) { for (auto &dock : dockWidgets) {
if (auto memoryWidget = qobject_cast<MemoryDockWidget *>(dock)) { if (auto memoryWidget = qobject_cast<MemoryDockWidget *>(dock)) {
QAction *action = new QAction(memoryWidget->windowTitle(), menu); QAction *action = new QAction(memoryWidget->windowTitle(), menu);
connect(action, &QAction::triggered, this, [this, memoryWidget, address](){ connect(action, &QAction::triggered, this, [this, memoryWidget, address]() {
memoryWidget->getSeekable()->seek(address); memoryWidget->getSeekable()->seek(address);
memoryWidget->raiseMemoryWidget(); memoryWidget->raiseMemoryWidget();
}); });
@ -973,7 +927,7 @@ QMenu *MainWindow::createShowInMenu(QWidget *parent, RVA address)
menu->addSeparator(); menu->addSeparator();
auto createAddNewWidgetAction = [this, menu, address](QString label, MemoryWidgetType type) { auto createAddNewWidgetAction = [this, menu, address](QString label, MemoryWidgetType type) {
QAction *action = new QAction(label, menu); QAction *action = new QAction(label, menu);
connect(action, &QAction::triggered, this, [this, address, type](){ connect(action, &QAction::triggered, this, [this, address, type]() {
addNewMemoryWidget(type, address, false); addNewMemoryWidget(type, address, false);
}); });
menu->addAction(action); menu->addAction(action);
@ -1023,18 +977,6 @@ MemoryDockWidget *MainWindow::addNewMemoryWidget(MemoryWidgetType type, RVA addr
return memoryWidget; return memoryWidget;
} }
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::initBackForwardMenu() void MainWindow::initBackForwardMenu()
{ {
auto prepareButtonMenu = [this](QAction *action) -> QMenu* { auto prepareButtonMenu = [this](QAction *action) -> QMenu* {
@ -1135,21 +1077,43 @@ void MainWindow::updateHistoryMenu(QMenu *menu, bool redo)
} }
void MainWindow::addWidget(QDockWidget* widget) void MainWindow::updateLayoutsMenu()
{
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));
});
ui->menuLayouts->addAction(action);
}
}
void MainWindow::saveNamedLayout()
{
bool ok = false;
QString name;
while (name.isEmpty() || isBuiltinLayoutName(name)) {
if (ok) {
QMessageBox::warning(this, tr("Save layout error"), tr("'%1' is not a valid name.").arg(name));
}
name = QInputDialog::getText(this, tr("Save layout"), tr("Enter name"), QLineEdit::Normal, {}, &ok);
if (!ok) {
return;
}
}
layouts[name] = getViewLayout();
updateLayoutsMenu();
saveSettings();
}
void MainWindow::addWidget(CutterDockWidget *widget)
{ {
dockWidgets.push_back(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);
}
updateDockActionsChecked();
});
}
} }
void MainWindow::addMemoryDockWidget(MemoryDockWidget *widget) void MainWindow::addMemoryDockWidget(MemoryDockWidget *widget)
@ -1161,9 +1125,10 @@ void MainWindow::addMemoryDockWidget(MemoryDockWidget *widget)
}); });
} }
void MainWindow::removeWidget(QDockWidget *widget) void MainWindow::removeWidget(CutterDockWidget *widget)
{ {
dockWidgets.removeAll(widget); dockWidgets.removeAll(widget);
pluginDocks.removeAll(widget);
if (lastSyncMemoryWidget == widget) { if (lastSyncMemoryWidget == widget) {
lastSyncMemoryWidget = nullptr; lastSyncMemoryWidget = nullptr;
} }
@ -1172,15 +1137,6 @@ void MainWindow::removeWidget(QDockWidget *widget)
} }
} }
void MainWindow::updateDockActionChecked(QAction *action)
{
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()
{ {
const QList<QDockWidget *> zenDocks = { functionsDock, const QList<QDockWidget *> zenDocks = { functionsDock,
@ -1189,18 +1145,15 @@ void MainWindow::showZenDocks()
searchDock, searchDock,
importsDock importsDock
}; };
int width = functionsDock->maximumWidth(); functionDockWidthToRestore = functionsDock->maximumWidth();
functionsDock->setMaximumWidth(200); functionsDock->setMaximumWidth(200);
for (auto w : dockWidgets) { for (auto w : dockWidgets) {
if (zenDocks.contains(w) || if (zenDocks.contains(w) ||
qobject_cast<GraphWidget*>(w) || isExtraMemoryWidget(w)) {
qobject_cast<HexdumpWidget*>(w) ||
qobject_cast<DisassemblyWidget*>(w)) {
w->show(); w->show();
} }
} }
functionsDock->setMaximumWidth(width); dashboardDock->raise();
updateDockActionsChecked();
} }
void MainWindow::showDebugDocks() void MainWindow::showDebugDocks()
@ -1215,18 +1168,54 @@ void MainWindow::showDebugDocks()
memoryMapDock, memoryMapDock,
breakpointDock breakpointDock
}; };
int width = functionsDock->maximumWidth(); functionDockWidthToRestore = functionsDock->maximumWidth();
functionsDock->setMaximumWidth(200); functionsDock->setMaximumWidth(200);
auto registerWidth = qhelpers::forceWidth(registersDock, std::min(500, this->width() / 4));
auto registerHeight = qhelpers::forceHeight(registersDock, std::max(100, height() / 2));
QDockWidget *widgetToFocus = nullptr;
for (auto w : dockWidgets) { for (auto w : dockWidgets) {
if (debugDocks.contains(w) || if (debugDocks.contains(w) ||
qobject_cast<GraphWidget*>(w) || isExtraMemoryWidget(w)) {
qobject_cast<HexdumpWidget*>(w) ||
qobject_cast<DisassemblyWidget*>(w)) {
w->show(); w->show();
} }
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);
} }
functionsDock->setMaximumWidth(width);
updateDockActionsChecked();
} }
void MainWindow::enableDebugWidgetsMenu(bool enable) void MainWindow::enableDebugWidgetsMenu(bool enable)
@ -1239,57 +1228,144 @@ void MainWindow::enableDebugWidgetsMenu(bool enable)
} }
} }
void MainWindow::resetToDefaultLayout() CutterLayout MainWindow::getViewLayout()
{ {
hideAllDocks(); CutterLayout layout;
restoreDocks(); layout.geometry = saveGeometry();
showZenDocks(); layout.state = saveState();
dashboardDock->raise();
}
void MainWindow::resetToDebugLayout() for (auto dock : dockWidgets) {
{ QVariantMap properties;
MemoryWidgetType memType = getMemoryWidgetTypeToRestore(); if (auto cutterDock = qobject_cast<CutterDockWidget *>(dock)) {
hideAllDocks(); properties = cutterDock->serializeViewProprties();
restoreDocks(); }
showDebugDocks(); layout.viewProperties.insert(dock->objectName(), std::move(properties));
showMemoryWidget(memType);
auto restoreStackDock = qhelpers::forceWidth(stackDock->widget(), 400);
qApp->processEvents();
restoreStackDock.restoreWidth(stackDock->widget());
}
void MainWindow::restoreDebugLayout()
{
MemoryWidgetType memType = getMemoryWidgetTypeToRestore();
bool isMaxim = isMaximized();
hideAllDocks();
restoreDocks();
showDebugDocks();
readDebugSettings();
showMemoryWidget(memType);
if (isMaxim) {
showMaximized();
} else {
showNormal();
} }
return layout;
} }
void MainWindow::resetDockWidgetList() CutterLayout MainWindow::getViewLayout(const QString &name)
{ {
QStringList isLeft; auto it = layouts.find(name);
QList<QWidget*> toClose; if (it != layouts.end()) {
for (auto it : dockWidgets) { return *it;
if (isLeft.contains(it->metaObject()->className())) { }
toClose.append(it); return {};
} else if (QRegularExpression("\\A(?:\\w+ \\d+)\\z").match(it->objectName()).hasMatch()) { }
isLeft.append(it->metaObject()->className());
void MainWindow::setViewLayout(const CutterLayout &layout)
{
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(),
HexdumpWidget::getWidgetType()
};
} 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);
}
} }
} }
for (auto it : toClose) {
it->close(); restoreDocks();
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);
}
} }
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();
}
} else {
if (isDebug) {
showDebugDocks();
} else {
showZenDocks();
}
}
}
void MainWindow::loadLayouts(QSettings &settings)
{
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());
}
layouts.insert(name, std::move(layout));
}
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);
}
settings.endArray();
} }
void MainWindow::on_actionLock_triggered() void MainWindow::on_actionLock_triggered()
@ -1323,22 +1399,12 @@ 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(); layouts[LAYOUT_DEBUG] = {};
saveSettings(); setViewLayout(layouts[LAYOUT_DEBUG]);
resetToDebugLayout();
} else { } else {
resetToDebugLayout(); layouts[LAYOUT_DEFAULT] = {};
saveDebugSettings(); setViewLayout(layouts[LAYOUT_DEFAULT]);
resetToDefaultLayout();
} }
} }
@ -1419,7 +1485,8 @@ void MainWindow::on_actionReset_settings_triggered()
QMessageBox::Ok | QMessageBox::Cancel); QMessageBox::Ok | QMessageBox::Cancel);
if (ret == QMessageBox::Ok) { if (ret == QMessageBox::Ok) {
Config()->resetAll(); Config()->resetAll();
on_actionDefault_triggered(); readSettings();
setViewLayout(getViewLayout(Core()->currentlyDebugging ? LAYOUT_DEBUG : LAYOUT_DEFAULT));
} }
} }
@ -1586,19 +1653,17 @@ void MainWindow::projectSaved(bool successfully, const QString &name)
void MainWindow::toggleDebugView() void MainWindow::toggleDebugView()
{ {
MemoryWidgetType memType = getMemoryWidgetTypeToRestore();
if (Core()->currentlyDebugging) { if (Core()->currentlyDebugging) {
saveSettings(); layouts[LAYOUT_DEFAULT] = getViewLayout();
restoreDebugLayout(); setViewLayout(getViewLayout(LAYOUT_DEBUG));
enableDebugWidgetsMenu(true); enableDebugWidgetsMenu(true);
} else { } else {
saveDebugSettings(); layouts[LAYOUT_DEBUG] = getViewLayout();
MemoryWidgetType memType = getMemoryWidgetTypeToRestore(); setViewLayout(getViewLayout(LAYOUT_DEFAULT));
hideAllDocks();
restoreDocks();
readSettingsOrDefault();
enableDebugWidgetsMenu(false); enableDebugWidgetsMenu(false);
showMemoryWidget(memType);
} }
showMemoryWidget(memType);
} }
void MainWindow::mousePressEvent(QMouseEvent *event) void MainWindow::mousePressEvent(QMouseEvent *event)

View File

@ -55,6 +55,13 @@ namespace Ui {
class MainWindow; class MainWindow;
} }
struct CutterLayout
{
QByteArray geometry;
QByteArray state;
QMap<QString, QVariantMap> viewProperties;
};
class MainWindow : public QMainWindow class MainWindow : public QMainWindow
{ {
Q_OBJECT Q_OBJECT
@ -85,21 +92,20 @@ public:
void closeEvent(QCloseEvent *event) override; void closeEvent(QCloseEvent *event) override;
void paintEvent(QPaintEvent *event) override; void paintEvent(QPaintEvent *event) override;
void readSettingsOrDefault(); void readSettings();
void saveSettings(); void saveSettings();
void readDebugSettings();
void saveDebugSettings();
void setFilename(const QString &fn); void setFilename(const QString &fn);
void refreshOmniBar(const QStringList &flags); void refreshOmniBar(const QStringList &flags);
void addWidget(QDockWidget *widget); void addWidget(CutterDockWidget *widget);
void addMemoryDockWidget(MemoryDockWidget *widget); void addMemoryDockWidget(MemoryDockWidget *widget);
void removeWidget(QDockWidget *widget); void removeWidget(CutterDockWidget *widget);
void addExtraWidget(CutterDockWidget *extraDock); void addExtraWidget(CutterDockWidget *extraDock);
MemoryDockWidget *addNewMemoryWidget(MemoryWidgetType type, RVA address, bool synchronized = true); MemoryDockWidget *addNewMemoryWidget(MemoryWidgetType type, RVA address, bool synchronized = true);
CUTTER_DEPRECATED("Action will be ignored. Use addPluginDockWidget(CutterDockWidget*) instead.")
void addPluginDockWidget(QDockWidget *dockWidget, QAction *action); void addPluginDockWidget(CutterDockWidget *dockWidget, QAction *) { addPluginDockWidget(dockWidget); }
void addPluginDockWidget(CutterDockWidget *dockWidget);
enum class MenuType { File, Edit, View, Windows, Debug, Help, Plugins }; enum class MenuType { File, Edit, View, Windows, Debug, Help, Plugins };
/** /**
* @brief Getter for MainWindow's different menus * @brief Getter for MainWindow's different menus
@ -109,8 +115,6 @@ public:
QMenu *getMenuByType(MenuType type); QMenu *getMenuByType(MenuType type);
void addMenuFileAction(QAction *action); void addMenuFileAction(QAction *action);
void updateDockActionChecked(QAction * action);
QString getFilename() const QString getFilename() const
{ {
return filename; return filename;
@ -233,10 +237,11 @@ private:
Configuration *configuration; Configuration *configuration;
QList<QDockWidget *> dockWidgets; QList<CutterDockWidget *> dockWidgets;
QMultiMap<QAction *, QDockWidget *> dockWidgetsOfAction; QList<CutterDockWidget *> pluginDocks;
DecompilerWidget *decompilerDock = nullptr; DecompilerWidget *decompilerDock = nullptr;
OverviewWidget *overviewDock = nullptr; OverviewWidget *overviewDock = nullptr;
QAction *actionOverview = nullptr;
EntrypointWidget *entrypointDock = nullptr; EntrypointWidget *entrypointDock = nullptr;
FunctionsWidget *functionsDock = nullptr; FunctionsWidget *functionsDock = nullptr;
ImportsWidget *importsDock = nullptr; ImportsWidget *importsDock = nullptr;
@ -250,7 +255,6 @@ private:
StringsWidget *stringsDock = nullptr; StringsWidget *stringsDock = nullptr;
FlagsWidget *flagsDock = nullptr; FlagsWidget *flagsDock = nullptr;
Dashboard *dashboardDock = nullptr; Dashboard *dashboardDock = nullptr;
QLineEdit *gotoEntry = nullptr;
SdbWidget *sdbDock = nullptr; SdbWidget *sdbDock = nullptr;
SectionsWidget *sectionsDock = nullptr; SectionsWidget *sectionsDock = nullptr;
SegmentsWidget *segmentsDock = nullptr; SegmentsWidget *segmentsDock = nullptr;
@ -259,40 +263,44 @@ private:
ClassesWidget *classesDock = nullptr; ClassesWidget *classesDock = nullptr;
ResourcesWidget *resourcesDock = nullptr; ResourcesWidget *resourcesDock = nullptr;
VTablesWidget *vTablesDock = nullptr; VTablesWidget *vTablesDock = nullptr;
DisassemblerGraphView *graphView = nullptr; CutterDockWidget *stackDock = nullptr;
QDockWidget *asmDock = nullptr; CutterDockWidget *threadsDock = nullptr;
QDockWidget *calcDock = nullptr; CutterDockWidget *processesDock = nullptr;
QDockWidget *stackDock = nullptr; CutterDockWidget *registersDock = nullptr;
QDockWidget *threadsDock = nullptr; CutterDockWidget *backtraceDock = nullptr;
QDockWidget *processesDock = nullptr; CutterDockWidget *memoryMapDock = nullptr;
QDockWidget *registersDock = nullptr;
QDockWidget *backtraceDock = nullptr;
QDockWidget *memoryMapDock = nullptr;
NewFileDialog *newFileDialog = nullptr; NewFileDialog *newFileDialog = nullptr;
QDockWidget *breakpointDock = nullptr; CutterDockWidget *breakpointDock = nullptr;
QDockWidget *registerRefsDock = nullptr; CutterDockWidget *registerRefsDock = nullptr;
QMenu *disassemblyContextMenuExtensions = nullptr; QMenu *disassemblyContextMenuExtensions = nullptr;
QMenu *addressableContextMenuExtensions = nullptr; QMenu *addressableContextMenuExtensions = nullptr;
QMap<QString, CutterLayout> layouts;
void initUI(); void initUI();
void initToolBar(); void initToolBar();
void initDocks(); void initDocks();
void initLayout();
void initCorners();
void initBackForwardMenu(); void initBackForwardMenu();
void displayInitialOptionsDialog(const InitialOptions &options = InitialOptions(), bool skipOptionsDialog = false); void displayInitialOptionsDialog(const InitialOptions &options = InitialOptions(), bool skipOptionsDialog = false);
void resetToDefaultLayout(); CutterLayout getViewLayout();
void resetToDebugLayout(); CutterLayout getViewLayout(const QString &name);
void restoreDebugLayout();
void setViewLayout(const CutterLayout &layout);
void loadLayouts(QSettings &settings);
void saveLayouts(QSettings &settings);
void updateMemberPointers(); void updateMemberPointers();
void resetDockWidgetList();
void restoreDocks(); void restoreDocks();
void hideAllDocks();
void showZenDocks(); void showZenDocks();
void showDebugDocks(); void showDebugDocks();
/**
* @brief Try to guess which is the "main" section of layout and dock there.
* @param widget that needs to be docked
*/
void dockOnMainArea(QDockWidget *widget);
void enableDebugWidgetsMenu(bool enable); void enableDebugWidgetsMenu(bool enable);
/** /**
* @brief Fill menu with seek history entries. * @brief Fill menu with seek history entries.
@ -300,10 +308,9 @@ private:
* @param redo set to false for undo history, true for redo. * @param redo set to false for undo history, true for redo.
*/ */
void updateHistoryMenu(QMenu *menu, bool redo = false); void updateHistoryMenu(QMenu *menu, bool redo = false);
void updateLayoutsMenu();
void saveNamedLayout();
void toggleDockWidget(QDockWidget *dock_widget, bool show);
void updateDockActionsChecked();
void setOverviewData(); void setOverviewData();
bool isOverviewActive(); bool isOverviewActive();
/** /**
@ -312,16 +319,18 @@ private:
* @return true for debug specific widgets, false for all other including common dock widgets. * @return true for debug specific widgets, false for all other including common dock widgets.
*/ */
bool isDebugWidget(QDockWidget *dock) const; bool isDebugWidget(QDockWidget *dock) const;
bool isExtraMemoryWidget(QDockWidget *dock) const;
MemoryWidgetType getMemoryWidgetTypeToRestore(); MemoryWidgetType getMemoryWidgetTypeToRestore();
/** /**
* @brief Map from a widget type (e.g. DisassemblyWidget::getWidgetType()) to the respective contructor of the widget * @brief Map from a widget type (e.g. DisassemblyWidget::getWidgetType()) to the respective contructor of the widget
*/ */
QMap<QString, std::function<CutterDockWidget*(MainWindow*, QAction*)>> widgetTypeToConstructorMap; QMap<QString, std::function<CutterDockWidget*(MainWindow*)>> widgetTypeToConstructorMap;
MemoryDockWidget* lastSyncMemoryWidget = nullptr; MemoryDockWidget* lastSyncMemoryWidget = nullptr;
MemoryDockWidget* lastMemoryWidget = nullptr; MemoryDockWidget* lastMemoryWidget = nullptr;
int functionDockWidthToRestore = 0;
}; };
#endif // MAINWINDOW_H #endif // MAINWINDOW_H

View File

@ -102,6 +102,13 @@
<addaction name="actionGrouped_dock_dragging"/> <addaction name="actionGrouped_dock_dragging"/>
<addaction name="separator"/> <addaction name="separator"/>
<addaction name="menuZoom"/> <addaction name="menuZoom"/>
<addaction name="actionSaveLayout"/>
<widget class="QMenu" name="menuLayouts">
<property name="title">
<string>Layouts</string>
</property>
</widget>
<addaction name="menuLayouts"/>
</widget> </widget>
<widget class="QMenu" name="menuHelp"> <widget class="QMenu" name="menuHelp">
<property name="title"> <property name="title">
@ -133,55 +140,20 @@
<property name="title"> <property name="title">
<string>Info...</string> <string>Info...</string>
</property> </property>
<addaction name="actionClasses"/>
<addaction name="actionEntrypoints"/>
<addaction name="actionExports"/>
<addaction name="actionFlags"/>
<addaction name="actionHeaders"/>
<addaction name="actionImports"/>
<addaction name="actionRelocs"/>
<addaction name="actionResources"/>
<addaction name="actionSDBBrowser"/>
<addaction name="actionSections"/>
<addaction name="actionSegments"/>
<addaction name="actionSymbols"/>
<addaction name="actionVTables"/>
<addaction name="actionZignatures"/>
</widget> </widget>
<widget class="QMenu" name="menuAddDebugWidgets"> <widget class="QMenu" name="menuAddDebugWidgets">
<property name="title"> <property name="title">
<string>Debug...</string> <string>Debug...</string>
</property> </property>
<addaction name="actionBacktrace"/>
<addaction name="actionBreakpoint"/>
<addaction name="actionThreads"/>
<addaction name="actionProcesses"/>
<addaction name="actionMemoryMap"/>
<addaction name="actionRegisters"/>
<addaction name="actionRegisterRefs"/>
<addaction name="actionStack"/>
</widget> </widget>
<addaction name="actionDashboard"/>
<addaction name="separator"/>
<addaction name="actionFunctions"/>
<addaction name="actionDecompiler"/>
<addaction name="actionOverview"/>
<addaction name="separator"/>
<addaction name="actionSearch"/>
<addaction name="actionStrings"/>
<addaction name="actionTypes"/>
<addaction name="separator"/>
<addaction name="actionExtraDisassembly"/> <addaction name="actionExtraDisassembly"/>
<addaction name="actionExtraGraph"/> <addaction name="actionExtraGraph"/>
<addaction name="actionExtraHexdump"/> <addaction name="actionExtraHexdump"/>
<addaction name="separator"/> <addaction name="separator"/>
<addaction name="menuAddInfoWidgets"/> <addaction name="menuAddInfoWidgets"/>
<addaction name="menuAddDebugWidgets"/> <addaction name="menuAddDebugWidgets"/>
<addaction name="separator"/>
<addaction name="actionComments"/>
<addaction name="actionConsole"/>
<addaction name="separator"/>
<addaction name="menuPlugins"/> <addaction name="menuPlugins"/>
<addaction name="separator"/>
</widget> </widget>
<widget class="QMenu" name="menuDebug"> <widget class="QMenu" name="menuDebug">
<property name="title"> <property name="title">
@ -231,10 +203,7 @@
</widget> </widget>
<action name="actionDefault"> <action name="actionDefault">
<property name="text"> <property name="text">
<string>Reset Layout</string> <string>Reset to default layout</string>
</property>
<property name="toolTip">
<string>Reset layout</string>
</property> </property>
</action> </action>
<action name="actionZen"> <action name="actionZen">
@ -399,105 +368,6 @@
<string>Lock/Unlock</string> <string>Lock/Unlock</string>
</property> </property>
</action> </action>
<action name="actionStrings">
<property name="checkable">
<bool>true</bool>
</property>
<property name="text">
<string>Strings</string>
</property>
<property name="toolTip">
<string>Show/Hide Strings panel</string>
</property>
</action>
<action name="actionSections">
<property name="checkable">
<bool>true</bool>
</property>
<property name="text">
<string>Sections</string>
</property>
<property name="toolTip">
<string>Show/Hide Sections panel</string>
</property>
</action>
<action name="actionSegments">
<property name="checkable">
<bool>true</bool>
</property>
<property name="text">
<string>Segments</string>
</property>
<property name="toolTip">
<string>Show/Hide Segments panel</string>
</property>
</action>
<action name="actionFunctions">
<property name="checkable">
<bool>true</bool>
</property>
<property name="text">
<string>Functions</string>
</property>
<property name="toolTip">
<string>Show/Hide Functions panel</string>
</property>
</action>
<action name="actionImports">
<property name="checkable">
<bool>true</bool>
</property>
<property name="text">
<string>Imports</string>
</property>
<property name="toolTip">
<string>Show/Hide Imports panel</string>
</property>
</action>
<action name="actionSymbols">
<property name="checkable">
<bool>true</bool>
</property>
<property name="text">
<string>Symbols</string>
</property>
<property name="toolTip">
<string>Show/Hide Symbols panel</string>
</property>
</action>
<action name="actionRelocs">
<property name="checkable">
<bool>true</bool>
</property>
<property name="text">
<string>Relocs</string>
</property>
<property name="toolTip">
<string>Show/Hide Relocs panel</string>
</property>
</action>
<action name="actionFlags">
<property name="checkable">
<bool>true</bool>
</property>
<property name="text">
<string>Flags</string>
</property>
<property name="toolTip">
<string>Show/Hide Flags panel</string>
</property>
</action>
<action name="actionMem">
<property name="checkable">
<bool>false</bool>
</property>
<property name="text">
<string>Memory</string>
</property>
<property name="toolTip">
<string>Show/Hide Memory panel</string>
</property>
</action>
<action name="actionTheme"> <action name="actionTheme">
<property name="checkable"> <property name="checkable">
<bool>false</bool> <bool>false</bool>
@ -537,17 +407,6 @@
<string>Refresh</string> <string>Refresh</string>
</property> </property>
</action> </action>
<action name="actionComments">
<property name="checkable">
<bool>true</bool>
</property>
<property name="text">
<string>Comments</string>
</property>
<property name="toolTip">
<string>Show/Hide comments</string>
</property>
</action>
<action name="actionTabs_on_Top"> <action name="actionTabs_on_Top">
<property name="checkable"> <property name="checkable">
<bool>true</bool> <bool>true</bool>
@ -776,30 +635,11 @@
<string>Show/Hide bottom pannel</string> <string>Show/Hide bottom pannel</string>
</property> </property>
</action> </action>
<action name="actionSDBBrowser">
<property name="checkable">
<bool>true</bool>
</property>
<property name="text">
<string>SDB Browser</string>
</property>
</action>
<action name="actionRun_Script"> <action name="actionRun_Script">
<property name="text"> <property name="text">
<string>Run Script</string> <string>Run Script</string>
</property> </property>
</action> </action>
<action name="actionDashboard">
<property name="checkable">
<bool>true</bool>
</property>
<property name="text">
<string>Dashboard</string>
</property>
<property name="toolTip">
<string>Show/Hide Dashboard panel</string>
</property>
</action>
<action name="actionReset_settings"> <action name="actionReset_settings">
<property name="text"> <property name="text">
<string>Reset Settings</string> <string>Reset Settings</string>
@ -851,14 +691,6 @@
<string>Show pseudocode rather than assembly</string> <string>Show pseudocode rather than assembly</string>
</property> </property>
</action> </action>
<action name="actionEntrypoints">
<property name="checkable">
<bool>true</bool>
</property>
<property name="text">
<string>Entry Points</string>
</property>
</action>
<action name="actionDisplay_Offsets"> <action name="actionDisplay_Offsets">
<property name="checkable"> <property name="checkable">
<bool>true</bool> <bool>true</bool>
@ -888,102 +720,6 @@
<string>Graph</string> <string>Graph</string>
</property> </property>
</action> </action>
<action name="actionOverview">
<property name="checkable">
<bool>true</bool>
</property>
<property name="text">
<string>Graph Overview</string>
</property>
</action>
<action name="actionDecompiler">
<property name="checkable">
<bool>true</bool>
</property>
<property name="text">
<string>Decompiler</string>
</property>
</action>
<action name="actionConsole">
<property name="checkable">
<bool>true</bool>
</property>
<property name="text">
<string>Console</string>
</property>
</action>
<action name="actionStack">
<property name="checkable">
<bool>true</bool>
</property>
<property name="text">
<string>Stack</string>
</property>
</action>
<action name="actionRegisters">
<property name="checkable">
<bool>true</bool>
</property>
<property name="text">
<string>Registers</string>
</property>
</action>
<action name="actionBacktrace">
<property name="checkable">
<bool>true</bool>
</property>
<property name="text">
<string>Backtrace</string>
</property>
</action>
<action name="actionThreads">
<property name="checkable">
<bool>true</bool>
</property>
<property name="text">
<string>Threads</string>
</property>
</action>
<action name="actionProcesses">
<property name="checkable">
<bool>true</bool>
</property>
<property name="text">
<string>Processes</string>
</property>
</action>
<action name="actionMemoryMap">
<property name="checkable">
<bool>true</bool>
</property>
<property name="text">
<string>Memory map</string>
</property>
</action>
<action name="actionBreakpoint">
<property name="checkable">
<bool>true</bool>
</property>
<property name="text">
<string>Breakpoints</string>
</property>
</action>
<action name="actionRegisterRefs">
<property name="checkable">
<bool>true</bool>
</property>
<property name="text">
<string>Register References</string>
</property>
</action>
<action name="actionClasses">
<property name="checkable">
<bool>true</bool>
</property>
<property name="text">
<string>Classes</string>
</property>
</action>
<action name="actionImportPDB"> <action name="actionImportPDB">
<property name="text"> <property name="text">
<string>Import PDB</string> <string>Import PDB</string>
@ -994,69 +730,6 @@
<string>Analyze</string> <string>Analyze</string>
</property> </property>
</action> </action>
<action name="actionResources">
<property name="checkable">
<bool>true</bool>
</property>
<property name="text">
<string>Resources</string>
</property>
</action>
<action name="actionVTables">
<property name="checkable">
<bool>true</bool>
</property>
<property name="text">
<string>VTables</string>
</property>
<property name="toolTip">
<string>Show/Hide VTables panel</string>
</property>
</action>
<action name="actionTypes">
<property name="checkable">
<bool>true</bool>
</property>
<property name="text">
<string>Types</string>
</property>
<property name="toolTip">
<string>Show/Hide Types panel</string>
</property>
</action>
<action name="actionSearch">
<property name="checkable">
<bool>true</bool>
</property>
<property name="text">
<string>Search</string>
</property>
<property name="toolTip">
<string>Show/Hide Search panel</string>
</property>
</action>
<action name="actionHeaders">
<property name="checkable">
<bool>true</bool>
</property>
<property name="text">
<string>Headers</string>
</property>
<property name="toolTip">
<string>Show/Hide Headers panel</string>
</property>
</action>
<action name="actionZignatures">
<property name="checkable">
<bool>true</bool>
</property>
<property name="text">
<string>Zignatures</string>
</property>
<property name="toolTip">
<string>Show/Hide Zignatures panel</string>
</property>
</action>
<action name="actionExport_as_code"> <action name="actionExport_as_code">
<property name="text"> <property name="text">
<string>Export as code</string> <string>Export as code</string>
@ -1126,6 +799,11 @@
<string>Commit changes</string> <string>Commit changes</string>
</property> </property>
</action> </action>
<action name="actionSaveLayout">
<property name="text">
<string>Save layout</string>
</property>
</action>
</widget> </widget>
<layoutdefault spacing="6" margin="11"/> <layoutdefault spacing="6" margin="11"/>
<resources> <resources>

View File

@ -14,14 +14,12 @@ void CutterSamplePlugin::setupPlugin()
void CutterSamplePlugin::setupInterface(MainWindow *main) void CutterSamplePlugin::setupInterface(MainWindow *main)
{ {
QAction *action = new QAction("Sample C++ Plugin", main); CutterSamplePluginWidget *widget = new CutterSamplePluginWidget(main);
action->setCheckable(true); main->addPluginDockWidget(widget);
CutterSamplePluginWidget *widget = new CutterSamplePluginWidget(main, action);
main->addPluginDockWidget(widget, action);
} }
CutterSamplePluginWidget::CutterSamplePluginWidget(MainWindow *main, QAction *action) : CutterSamplePluginWidget::CutterSamplePluginWidget(MainWindow *main) :
CutterDockWidget(main, action) CutterDockWidget(main)
{ {
this->setObjectName("CutterSamplePluginWidget"); this->setObjectName("CutterSamplePluginWidget");
this->setWindowTitle("Sample C++ Plugin"); this->setWindowTitle("Sample C++ Plugin");

View File

@ -2,12 +2,12 @@
import cutter import cutter
from PySide2.QtCore import Qt from PySide2.QtCore import Qt
from PySide2.QtWidgets import QAction, QVBoxLayout, QLabel, QWidget, QSizePolicy, QPushButton from PySide2.QtWidgets import QVBoxLayout, QLabel, QWidget, QSizePolicy, QPushButton
class FortuneWidget(cutter.CutterDockWidget): class FortuneWidget(cutter.CutterDockWidget):
def __init__(self, parent, action): def __init__(self, parent):
super(FortuneWidget, self).__init__(parent, action) super(FortuneWidget, self).__init__(parent)
self.setObjectName("FancyDockWidgetFromCoolPlugin") self.setObjectName("FancyDockWidgetFromCoolPlugin")
self.setWindowTitle("Sample Python Plugin") self.setWindowTitle("Sample Python Plugin")
@ -62,10 +62,8 @@ class CutterSamplePlugin(cutter.CutterPlugin):
def setupInterface(self, main): def setupInterface(self, main):
# Dock widget # Dock widget
action = QAction("Sample Python Plugin", main) widget = FortuneWidget(main)
action.setCheckable(True) main.addPluginDockWidget(widget)
widget = FortuneWidget(main, action)
main.addPluginDockWidget(widget, action)
# Dissassembly context menu # Dissassembly context menu
menu = main.getContextMenuExtensions(cutter.MainWindow.ContextMenuType.Disassembly) menu = main.getContextMenuExtensions(cutter.MainWindow.ContextMenuType.Disassembly)

View File

@ -5,8 +5,8 @@
#include "core/MainWindow.h" #include "core/MainWindow.h"
BacktraceWidget::BacktraceWidget(MainWindow *main, QAction *action) : BacktraceWidget::BacktraceWidget(MainWindow *main) :
CutterDockWidget(main, action), CutterDockWidget(main),
ui(new Ui::BacktraceWidget) ui(new Ui::BacktraceWidget)
{ {
ui->setupUi(this); ui->setupUi(this);

View File

@ -19,7 +19,7 @@ class BacktraceWidget : public CutterDockWidget
Q_OBJECT Q_OBJECT
public: public:
explicit BacktraceWidget(MainWindow *main, QAction *action = nullptr); explicit BacktraceWidget(MainWindow *main);
~BacktraceWidget(); ~BacktraceWidget();
private slots: private slots:

View File

@ -173,8 +173,8 @@ BreakpointProxyModel::BreakpointProxyModel(BreakpointModel *sourceModel, QObject
this->setSortRole(Qt::EditRole); this->setSortRole(Qt::EditRole);
} }
BreakpointWidget::BreakpointWidget(MainWindow *main, QAction *action) : BreakpointWidget::BreakpointWidget(MainWindow *main) :
CutterDockWidget(main, action), CutterDockWidget(main),
ui(new Ui::BreakpointWidget) ui(new Ui::BreakpointWidget)
{ {
ui->setupUi(this); ui->setupUi(this);

View File

@ -69,7 +69,7 @@ class BreakpointWidget : public CutterDockWidget
Q_OBJECT Q_OBJECT
public: public:
explicit BreakpointWidget(MainWindow *main, QAction *action = nullptr); explicit BreakpointWidget(MainWindow *main);
~BreakpointWidget(); ~BreakpointWidget();
private slots: private slots:

View File

@ -575,8 +575,8 @@ bool ClassesSortFilterProxyModel::hasChildren(const QModelIndex &parent) const
ClassesWidget::ClassesWidget(MainWindow *main, QAction *action) : ClassesWidget::ClassesWidget(MainWindow *main) :
CutterDockWidget(main, action), CutterDockWidget(main),
ui(new Ui::ClassesWidget) ui(new Ui::ClassesWidget)
{ {
ui->setupUi(this); ui->setupUi(this);

View File

@ -175,7 +175,7 @@ class ClassesWidget : public CutterDockWidget
Q_OBJECT Q_OBJECT
public: public:
explicit ClassesWidget(MainWindow *main, QAction *action = nullptr); explicit ClassesWidget(MainWindow *main);
~ClassesWidget(); ~ClassesWidget();
private slots: private slots:

View File

@ -229,8 +229,8 @@ bool CommentsProxyModel::lessThan(const QModelIndex &left, const QModelIndex &ri
return false; return false;
} }
CommentsWidget::CommentsWidget(MainWindow *main, QAction *action) : CommentsWidget::CommentsWidget(MainWindow *main) :
ListDockWidget(main, action), ListDockWidget(main),
actionHorizontal(tr("Horizontal"), this), actionHorizontal(tr("Horizontal"), this),
actionVertical(tr("Vertical"), this) actionVertical(tr("Vertical"), this)
{ {

View File

@ -75,7 +75,7 @@ class CommentsWidget : public ListDockWidget
Q_OBJECT Q_OBJECT
public: public:
explicit CommentsWidget(MainWindow *main, QAction *action = nullptr); explicit CommentsWidget(MainWindow *main);
~CommentsWidget() override; ~CommentsWidget() override;
private slots: private slots:

View File

@ -37,8 +37,8 @@ static const int invalidHistoryPos = -1;
static const char *consoleWrapSettingsKey = "console.wrap"; static const char *consoleWrapSettingsKey = "console.wrap";
ConsoleWidget::ConsoleWidget(MainWindow *main, QAction *action) : ConsoleWidget::ConsoleWidget(MainWindow *main) :
CutterDockWidget(main, action), CutterDockWidget(main),
ui(new Ui::ConsoleWidget), ui(new Ui::ConsoleWidget),
debugOutputEnabled(true), debugOutputEnabled(true),
maxHistoryEntries(100), maxHistoryEntries(100),

View File

@ -24,7 +24,7 @@ class ConsoleWidget : public CutterDockWidget
Q_OBJECT Q_OBJECT
public: public:
explicit ConsoleWidget(MainWindow *main, QAction *action = nullptr); explicit ConsoleWidget(MainWindow *main);
~ConsoleWidget(); ~ConsoleWidget();

View File

@ -1,26 +1,22 @@
#include "CutterDockWidget.h" #include "CutterDockWidget.h"
#include "core/MainWindow.h" #include "core/MainWindow.h"
#include <QAction>
#include <QEvent> #include <QEvent>
#include <QtWidgets/QShortcut> #include <QtWidgets/QShortcut>
CutterDockWidget::CutterDockWidget(MainWindow *parent, QAction *action) : CutterDockWidget::CutterDockWidget(MainWindow *parent, QAction *)
QDockWidget(parent), : CutterDockWidget(parent)
mainWindow(parent),
action(action)
{ {
if (action) { }
addAction(action);
connect(action, &QAction::triggered, this, &CutterDockWidget::toggleDockWidget);
}
if (parent) {
parent->addWidget(this);
}
CutterDockWidget::CutterDockWidget(MainWindow *parent) :
QDockWidget(parent),
mainWindow(parent)
{
// Install event filter to catch redraw widgets when needed // Install event filter to catch redraw widgets when needed
installEventFilter(this); installEventFilter(this);
updateIsVisibleToUser(); updateIsVisibleToUser();
connect(toggleViewAction(), &QAction::triggered, this, &QWidget::raise);
} }
CutterDockWidget::~CutterDockWidget() = default; CutterDockWidget::~CutterDockWidget() = default;
@ -38,6 +34,15 @@ bool CutterDockWidget::eventFilter(QObject *object, QEvent *event)
return QDockWidget::eventFilter(object, event); return QDockWidget::eventFilter(object, event);
} }
QVariantMap CutterDockWidget::serializeViewProprties()
{
return {};
}
void CutterDockWidget::deserializeViewProperties(const QVariantMap &)
{
}
void CutterDockWidget::toggleDockWidget(bool show) void CutterDockWidget::toggleDockWidget(bool show)
{ {
if (!show) { if (!show) {
@ -68,25 +73,21 @@ void CutterDockWidget::updateIsVisibleToUser()
void CutterDockWidget::closeEvent(QCloseEvent *event) void CutterDockWidget::closeEvent(QCloseEvent *event)
{ {
if (action) {
this->action->setChecked(false);
}
QDockWidget::closeEvent(event); QDockWidget::closeEvent(event);
if (isTransient) { if (isTransient) {
if (mainWindow) { if (mainWindow) {
mainWindow->removeWidget(this); mainWindow->removeWidget(this);
} }
// remove parent, otherwise dock layout may still decide to use this widget which is about to be deleted
setParent(nullptr);
deleteLater(); deleteLater();
} }
emit closed(); emit closed();
} }
QAction *CutterDockWidget::getBoundAction() const
{
return action;
}
QString CutterDockWidget::getDockNumber() QString CutterDockWidget::getDockNumber()
{ {
auto name = this->objectName(); auto name = this->objectName();

View File

@ -1,10 +1,11 @@
#ifndef CUTTERWIDGET_H #ifndef CUTTERWIDGET_H
#define CUTTERWIDGET_H #define CUTTERWIDGET_H
#include <QDockWidget> #include "CutterCommon.h"
#include "common/RefreshDeferrer.h" #include "common/RefreshDeferrer.h"
#include <QDockWidget>
class MainWindow; class MainWindow;
class CutterDockWidget : public QDockWidget class CutterDockWidget : public QDockWidget
@ -12,7 +13,10 @@ class CutterDockWidget : public QDockWidget
Q_OBJECT Q_OBJECT
public: public:
explicit CutterDockWidget(MainWindow *parent, QAction *action = nullptr); CUTTER_DEPRECATED("Action will be ignored. Use CutterDockWidget(MainWindow*) instead.")
CutterDockWidget(MainWindow *parent, QAction *action);
explicit CutterDockWidget(MainWindow *parent);
~CutterDockWidget() override; ~CutterDockWidget() override;
bool eventFilter(QObject *object, QEvent *event) override; bool eventFilter(QObject *object, QEvent *event) override;
bool isVisibleToUser() { return isVisibleToUserCurrent; } bool isVisibleToUser() { return isVisibleToUserCurrent; }
@ -54,7 +58,34 @@ public:
}); });
return deferrer; return deferrer;
} }
/**
* @brief Serialize dock properties for saving as part of layout.
*
* Override this function for saving dock specific view properties. Use
* in situations where it makes sense to have different properties for
* multiple instances of widget. Don't use for options that are more suitable
* as global settings and should be applied equally to all widgets or all
* widgets of this kind.
*
* Keep synchrononized with deserializeViewProperties. When modifying add
* project upgrade step in SettingsUpgrade.cpp if necessary.
*
* @return Dictionary of current dock properties.
* @see CutterDockWidget#deserializeViewProperties
*/
virtual QVariantMap serializeViewProprties();
/**
* @brief Deserialization half of serialize view properties.
*
* When a property is not specified in property map dock should reset it
* to default value instead of leaving it umodified. Empty map should reset
* all properties controlled by serializeViewProprties/deserializeViewProperties
* mechanism.
*
* @param properties to modify for current widget
* @see CutterDockWidget#serializeViewProprties
*/
virtual void deserializeViewProperties(const QVariantMap &properties);
signals: signals:
void becameVisibleToUser(); void becameVisibleToUser();
void closed(); void closed();
@ -66,14 +97,11 @@ protected:
virtual QWidget* widgetToFocusOnRaise(); virtual QWidget* widgetToFocusOnRaise();
void closeEvent(QCloseEvent *event) override; void closeEvent(QCloseEvent *event) override;
QAction *getBoundAction() const;
QString getDockNumber(); QString getDockNumber();
MainWindow *mainWindow; MainWindow *mainWindow;
private: private:
QAction *action;
bool isTransient = false; bool isTransient = false;
bool isVisibleToUserCurrent = false; bool isVisibleToUserCurrent = false;

View File

@ -21,8 +21,8 @@
#include <QDialog> #include <QDialog>
#include <QTreeWidget> #include <QTreeWidget>
Dashboard::Dashboard(MainWindow *main, QAction *action) : Dashboard::Dashboard(MainWindow *main) :
CutterDockWidget(main, action), CutterDockWidget(main),
ui(new Ui::Dashboard) ui(new Ui::Dashboard)
{ {
ui->setupUi(this); ui->setupUi(this);

View File

@ -21,7 +21,7 @@ class Dashboard : public CutterDockWidget
Q_OBJECT Q_OBJECT
public: public:
explicit Dashboard(MainWindow *main, QAction *action = nullptr); explicit Dashboard(MainWindow *main);
~Dashboard(); ~Dashboard();
private slots: private slots:

View File

@ -15,8 +15,8 @@
#include <QObject> #include <QObject>
#include <QTextBlockUserData> #include <QTextBlockUserData>
DecompilerWidget::DecompilerWidget(MainWindow *main, QAction *action) : DecompilerWidget::DecompilerWidget(MainWindow *main) :
MemoryDockWidget(MemoryWidgetType::Decompiler, main, action), MemoryDockWidget(MemoryWidgetType::Decompiler, main),
mCtxMenu(new DisassemblyContextMenu(this, main)), mCtxMenu(new DisassemblyContextMenu(this, main)),
ui(new Ui::DecompilerWidget) ui(new Ui::DecompilerWidget)
{ {

View File

@ -25,7 +25,7 @@ protected:
DisassemblyContextMenu *mCtxMenu; DisassemblyContextMenu *mCtxMenu;
public: public:
explicit DecompilerWidget(MainWindow *main, QAction *action = nullptr); explicit DecompilerWidget(MainWindow *main);
~DecompilerWidget(); ~DecompilerWidget();
public slots: public slots:
void showDisasContextMenu(const QPoint &pt); void showDisasContextMenu(const QPoint &pt);

View File

@ -38,8 +38,8 @@ static DisassemblyTextBlockUserData *getUserData(const QTextBlock &block)
return static_cast<DisassemblyTextBlockUserData *>(userData); return static_cast<DisassemblyTextBlockUserData *>(userData);
} }
DisassemblyWidget::DisassemblyWidget(MainWindow *main, QAction *action) DisassemblyWidget::DisassemblyWidget(MainWindow *main)
: MemoryDockWidget(MemoryWidgetType::Disassembly, main, action) : MemoryDockWidget(MemoryWidgetType::Disassembly, main)
, mCtxMenu(new DisassemblyContextMenu(this, main)) , mCtxMenu(new DisassemblyContextMenu(this, main))
, mDisasScrollArea(new DisassemblyScrollArea(this)) , mDisasScrollArea(new DisassemblyScrollArea(this))
, mDisasTextEdit(new DisassemblyTextEdit(this)) , mDisasTextEdit(new DisassemblyTextEdit(this))

View File

@ -22,7 +22,7 @@ class DisassemblyWidget : public MemoryDockWidget
{ {
Q_OBJECT Q_OBJECT
public: public:
explicit DisassemblyWidget(MainWindow *main, QAction *action = nullptr); explicit DisassemblyWidget(MainWindow *main);
QWidget *getTextWidget(); QWidget *getTextWidget();
static QString getWidgetType(); static QString getWidgetType();

View File

@ -12,8 +12,8 @@
* Entrypoint Widget * Entrypoint Widget
*/ */
EntrypointWidget::EntrypointWidget(MainWindow *main, QAction *action) : EntrypointWidget::EntrypointWidget(MainWindow *main) :
CutterDockWidget(main, action), CutterDockWidget(main),
ui(new Ui::EntrypointWidget) ui(new Ui::EntrypointWidget)
{ {
ui->setupUi(this); ui->setupUi(this);

View File

@ -19,7 +19,7 @@ class EntrypointWidget : public CutterDockWidget
Q_OBJECT Q_OBJECT
public: public:
explicit EntrypointWidget(MainWindow *main, QAction *action = nullptr); explicit EntrypointWidget(MainWindow *main);
~EntrypointWidget(); ~EntrypointWidget();
private slots: private slots:

View File

@ -125,8 +125,8 @@ bool ExportsProxyModel::lessThan(const QModelIndex &left, const QModelIndex &rig
return leftExp.vaddr < rightExp.vaddr; return leftExp.vaddr < rightExp.vaddr;
} }
ExportsWidget::ExportsWidget(MainWindow *main, QAction *action) : ExportsWidget::ExportsWidget(MainWindow *main) :
ListDockWidget(main, action) ListDockWidget(main)
{ {
setWindowTitle(tr("Exports")); setWindowTitle(tr("Exports"));
setObjectName("ExportsWidget"); setObjectName("ExportsWidget");
@ -139,7 +139,6 @@ ExportsWidget::ExportsWidget(MainWindow *main, QAction *action) :
QShortcut *toggle_shortcut = new QShortcut(widgetShortcuts["ExportsWidget"], main); QShortcut *toggle_shortcut = new QShortcut(widgetShortcuts["ExportsWidget"], main);
connect(toggle_shortcut, &QShortcut::activated, this, [=] (){ connect(toggle_shortcut, &QShortcut::activated, this, [=] (){
toggleDockWidget(true); toggleDockWidget(true);
main->updateDockActionChecked(action);
} ); } );
connect(Core(), &CutterCore::codeRebased, this, &ExportsWidget::refreshExports); connect(Core(), &CutterCore::codeRebased, this, &ExportsWidget::refreshExports);

View File

@ -60,7 +60,7 @@ class ExportsWidget : public ListDockWidget
Q_OBJECT Q_OBJECT
public: public:
explicit ExportsWidget(MainWindow *main, QAction *action = nullptr); explicit ExportsWidget(MainWindow *main);
~ExportsWidget(); ~ExportsWidget();
private slots: private slots:

View File

@ -137,8 +137,8 @@ bool FlagsSortFilterProxyModel::lessThan(const QModelIndex &left, const QModelIn
} }
FlagsWidget::FlagsWidget(MainWindow *main, QAction *action) : FlagsWidget::FlagsWidget(MainWindow *main) :
CutterDockWidget(main, action), CutterDockWidget(main),
ui(new Ui::FlagsWidget), ui(new Ui::FlagsWidget),
main(main), main(main),
tree(new CutterTreeWidget(this)) tree(new CutterTreeWidget(this))

View File

@ -69,7 +69,7 @@ class FlagsWidget : public CutterDockWidget
Q_OBJECT Q_OBJECT
public: public:
explicit FlagsWidget(MainWindow *main, QAction *action = nullptr); explicit FlagsWidget(MainWindow *main);
~FlagsWidget(); ~FlagsWidget();
private slots: private slots:

View File

@ -424,8 +424,8 @@ bool FunctionSortFilterProxyModel::lessThan(const QModelIndex &left, const QMode
} }
} }
FunctionsWidget::FunctionsWidget(MainWindow *main, QAction *action) : FunctionsWidget::FunctionsWidget(MainWindow *main) :
ListDockWidget(main, action), ListDockWidget(main),
actionRename(tr("Rename"), this), actionRename(tr("Rename"), this),
actionUndefine(tr("Undefine"), this), actionUndefine(tr("Undefine"), this),
actionHorizontal(tr("Horizontal"), this), actionHorizontal(tr("Horizontal"), this),

View File

@ -92,7 +92,7 @@ class FunctionsWidget : public ListDockWidget
Q_OBJECT Q_OBJECT
public: public:
explicit FunctionsWidget(MainWindow *main, QAction *action = nullptr); explicit FunctionsWidget(MainWindow *main);
~FunctionsWidget() override; ~FunctionsWidget() override;
void changeSizePolicy(QSizePolicy::Policy hor, QSizePolicy::Policy ver); void changeSizePolicy(QSizePolicy::Policy hor, QSizePolicy::Policy ver);

View File

@ -4,8 +4,8 @@
#include "WidgetShortcuts.h" #include "WidgetShortcuts.h"
#include <QVBoxLayout> #include <QVBoxLayout>
GraphWidget::GraphWidget(MainWindow *main, QAction *action) : GraphWidget::GraphWidget(MainWindow *main) :
MemoryDockWidget(MemoryWidgetType::Graph, main, action) MemoryDockWidget(MemoryWidgetType::Graph, main)
{ {
setObjectName(main setObjectName(main
? main->getUniqueObjectName(getWidgetType()) ? main->getUniqueObjectName(getWidgetType())

View File

@ -12,7 +12,7 @@ class GraphWidget : public MemoryDockWidget
Q_OBJECT Q_OBJECT
public: public:
explicit GraphWidget(MainWindow *main, QAction *action = nullptr); explicit GraphWidget(MainWindow *main);
~GraphWidget() override {} ~GraphWidget() override {}
DisassemblerGraphView *getGraphView() const; DisassemblerGraphView *getGraphView() const;

View File

@ -109,8 +109,8 @@ bool HeadersProxyModel::lessThan(const QModelIndex &left, const QModelIndex &rig
return leftHeader.vaddr < rightHeader.vaddr; return leftHeader.vaddr < rightHeader.vaddr;
} }
HeadersWidget::HeadersWidget(MainWindow *main, QAction *action) : HeadersWidget::HeadersWidget(MainWindow *main) :
ListDockWidget(main, action) ListDockWidget(main)
{ {
setWindowTitle(tr("Headers")); setWindowTitle(tr("Headers"));
setObjectName("HeadersWidget"); setObjectName("HeadersWidget");

View File

@ -68,7 +68,7 @@ class HeadersWidget : public ListDockWidget
Q_OBJECT Q_OBJECT
public: public:
explicit HeadersWidget(MainWindow *main, QAction *action = nullptr); explicit HeadersWidget(MainWindow *main);
~HeadersWidget(); ~HeadersWidget();
private slots: private slots:

View File

@ -147,7 +147,7 @@ public:
bool copy(void *out, uint64_t addr, size_t len) override { bool copy(void *out, uint64_t addr, size_t len) override {
if (addr < m_firstBlockAddr || addr > m_lastValidAddr || if (addr < m_firstBlockAddr || addr > m_lastValidAddr ||
(m_lastValidAddr - addr + 1) < len /* do not merge with last check to handle overflows */) { (m_lastValidAddr - addr + 1) < len /* do not merge with last check to handle overflows */ || m_blocks.isEmpty()) {
return false; return false;
} }

View File

@ -17,8 +17,8 @@
#include <QInputDialog> #include <QInputDialog>
#include <QShortcut> #include <QShortcut>
HexdumpWidget::HexdumpWidget(MainWindow *main, QAction *action) : HexdumpWidget::HexdumpWidget(MainWindow *main) :
MemoryDockWidget(MemoryWidgetType::Hexdump, main, action), MemoryDockWidget(MemoryWidgetType::Hexdump, main),
ui(new Ui::HexdumpWidget) ui(new Ui::HexdumpWidget)
{ {
ui->setupUi(this); ui->setupUi(this);

View File

@ -31,7 +31,7 @@ class HexdumpWidget : public MemoryDockWidget
{ {
Q_OBJECT Q_OBJECT
public: public:
explicit HexdumpWidget(MainWindow *main, QAction *action = nullptr); explicit HexdumpWidget(MainWindow *main);
~HexdumpWidget() override; ~HexdumpWidget() override;
Highlighter *highlighter; Highlighter *highlighter;

View File

@ -154,8 +154,8 @@ bool ImportsProxyModel::lessThan(const QModelIndex &left, const QModelIndex &rig
* Imports Widget * Imports Widget
*/ */
ImportsWidget::ImportsWidget(MainWindow *main, QAction *action) : ImportsWidget::ImportsWidget(MainWindow *main) :
ListDockWidget(main, action), ListDockWidget(main),
importsModel(new ImportsModel(&imports, this)), importsModel(new ImportsModel(&imports, this)),
importsProxyModel(new ImportsProxyModel(importsModel, this)) importsProxyModel(new ImportsProxyModel(importsModel, this))
{ {
@ -168,7 +168,6 @@ ImportsWidget::ImportsWidget(MainWindow *main, QAction *action) :
QShortcut *toggle_shortcut = new QShortcut(widgetShortcuts["ImportsWidget"], main); QShortcut *toggle_shortcut = new QShortcut(widgetShortcuts["ImportsWidget"], main);
connect(toggle_shortcut, &QShortcut::activated, this, [=] (){ connect(toggle_shortcut, &QShortcut::activated, this, [=] (){
toggleDockWidget(true); toggleDockWidget(true);
main->updateDockActionChecked(action);
} ); } );
connect(Core(), &CutterCore::codeRebased, this, &ImportsWidget::refreshImports); connect(Core(), &CutterCore::codeRebased, this, &ImportsWidget::refreshImports);

View File

@ -77,7 +77,7 @@ class ImportsWidget : public ListDockWidget
Q_OBJECT Q_OBJECT
public: public:
explicit ImportsWidget(MainWindow *main, QAction *action); explicit ImportsWidget(MainWindow *main);
~ImportsWidget(); ~ImportsWidget();
private slots: private slots:

View File

@ -8,8 +8,8 @@
#include <QResizeEvent> #include <QResizeEvent>
#include <QShortcut> #include <QShortcut>
ListDockWidget::ListDockWidget(MainWindow *main, QAction *action, SearchBarPolicy searchBarPolicy) : ListDockWidget::ListDockWidget(MainWindow *main, SearchBarPolicy searchBarPolicy) :
CutterDockWidget(main, action), CutterDockWidget(main),
ui(new Ui::ListDockWidget), ui(new Ui::ListDockWidget),
tree(new CutterTreeWidget(this)), tree(new CutterTreeWidget(this)),
searchBarPolicy(searchBarPolicy) searchBarPolicy(searchBarPolicy)

View File

@ -32,7 +32,7 @@ public:
Hide, Hide,
}; };
explicit ListDockWidget(MainWindow *main, QAction *action = nullptr, SearchBarPolicy searchBarPolicy = SearchBarPolicy::ShowByDefault); explicit ListDockWidget(MainWindow *main, SearchBarPolicy searchBarPolicy = SearchBarPolicy::ShowByDefault);
~ListDockWidget() override; ~ListDockWidget() override;
void showCount(bool show); void showCount(bool show);

View File

@ -6,8 +6,8 @@
#include <QMenu> #include <QMenu>
#include <QContextMenuEvent> #include <QContextMenuEvent>
MemoryDockWidget::MemoryDockWidget(MemoryWidgetType type, MainWindow *parent, QAction *action) MemoryDockWidget::MemoryDockWidget(MemoryWidgetType type, MainWindow *parent)
: CutterDockWidget(parent, action) : CutterDockWidget(parent)
, mType(type) , mType(type)
, seekable(new CutterSeekable(this)) , seekable(new CutterSeekable(this))
, syncAction(tr("Sync/unsync offset"), this) , syncAction(tr("Sync/unsync offset"), this)
@ -40,11 +40,6 @@ bool MemoryDockWidget::tryRaiseMemoryWidget()
void MemoryDockWidget::raiseMemoryWidget() void MemoryDockWidget::raiseMemoryWidget()
{ {
if (getBoundAction()) {
getBoundAction()->setChecked(true);
}
show(); show();
raise(); raise();
widgetToFocusOnRaise()->setFocus(Qt::FocusReason::TabFocusReason); widgetToFocusOnRaise()->setFocus(Qt::FocusReason::TabFocusReason);
@ -58,6 +53,19 @@ bool MemoryDockWidget::eventFilter(QObject *object, QEvent *event)
return CutterDockWidget::eventFilter(object, event); return CutterDockWidget::eventFilter(object, event);
} }
QVariantMap MemoryDockWidget::serializeViewProprties()
{
auto result = CutterDockWidget::serializeViewProprties();
result["synchronized"] = seekable->isSynchronized();
return result;
}
void MemoryDockWidget::deserializeViewProperties(const QVariantMap &properties)
{
QVariant synchronized = properties.value("synchronized", true);
seekable->setSynchronization(synchronized.toBool());
}
void MemoryDockWidget::updateWindowTitle() void MemoryDockWidget::updateWindowTitle()
{ {
QString name = getWindowTitle(); QString name = getWindowTitle();

View File

@ -15,7 +15,7 @@ class MemoryDockWidget : public CutterDockWidget
{ {
Q_OBJECT Q_OBJECT
public: public:
MemoryDockWidget(MemoryWidgetType type, MainWindow *parent, QAction *action = nullptr); MemoryDockWidget(MemoryWidgetType type, MainWindow *parent);
~MemoryDockWidget() override {} ~MemoryDockWidget() override {}
CutterSeekable *getSeekable() const; CutterSeekable *getSeekable() const;
@ -27,6 +27,9 @@ public:
return mType; return mType;
} }
bool eventFilter(QObject *object, QEvent *event) override; bool eventFilter(QObject *object, QEvent *event) override;
QVariantMap serializeViewProprties() override;
void deserializeViewProperties(const QVariantMap &properties) override;
private: private:
MemoryWidgetType mType; MemoryWidgetType mType;

View File

@ -111,8 +111,8 @@ bool MemoryProxyModel::lessThan(const QModelIndex &left, const QModelIndex &righ
return leftMemMap.addrStart < rightMemMap.addrStart; return leftMemMap.addrStart < rightMemMap.addrStart;
} }
MemoryMapWidget::MemoryMapWidget(MainWindow *main, QAction *action) : MemoryMapWidget::MemoryMapWidget(MainWindow *main) :
ListDockWidget(main, action, ListDockWidget::SearchBarPolicy::HideByDefault) ListDockWidget(main, ListDockWidget::SearchBarPolicy::HideByDefault)
{ {
setWindowTitle(tr("Memory Map")); setWindowTitle(tr("Memory Map"));
setObjectName("MemoryMapWidget"); setObjectName("MemoryMapWidget");

View File

@ -67,7 +67,7 @@ class MemoryMapWidget : public ListDockWidget
Q_OBJECT Q_OBJECT
public: public:
explicit MemoryMapWidget(MainWindow *main, QAction *action = nullptr); explicit MemoryMapWidget(MainWindow *main);
~MemoryMapWidget(); ~MemoryMapWidget();
private slots: private slots:

View File

@ -3,8 +3,8 @@
#include "GraphWidget.h" #include "GraphWidget.h"
#include "OverviewView.h" #include "OverviewView.h"
OverviewWidget::OverviewWidget(MainWindow *main, QAction *action) : OverviewWidget::OverviewWidget(MainWindow *main) :
CutterDockWidget(main, action) CutterDockWidget(main)
{ {
setWindowTitle("Graph Overview"); setWindowTitle("Graph Overview");
setObjectName("Graph Overview"); setObjectName("Graph Overview");

View File

@ -12,7 +12,7 @@ class OverviewWidget : public CutterDockWidget
Q_OBJECT Q_OBJECT
public: public:
explicit OverviewWidget(MainWindow *main, QAction *action = nullptr); explicit OverviewWidget(MainWindow *main);
~OverviewWidget(); ~OverviewWidget();
private: private:

View File

@ -16,8 +16,8 @@ enum ColumnIndex {
COLUMN_PATH, COLUMN_PATH,
}; };
ProcessesWidget::ProcessesWidget(MainWindow *main, QAction *action) : ProcessesWidget::ProcessesWidget(MainWindow *main) :
CutterDockWidget(main, action), CutterDockWidget(main),
ui(new Ui::ProcessesWidget) ui(new Ui::ProcessesWidget)
{ {
ui->setupUi(this); ui->setupUi(this);

View File

@ -31,7 +31,7 @@ class ProcessesWidget : public CutterDockWidget
Q_OBJECT Q_OBJECT
public: public:
explicit ProcessesWidget(MainWindow *main, QAction *action = nullptr); explicit ProcessesWidget(MainWindow *main);
~ProcessesWidget(); ~ProcessesWidget();
private slots: private slots:

View File

@ -111,8 +111,8 @@ bool RegisterRefProxyModel::lessThan(const QModelIndex &left, const QModelIndex
return leftRegRef.reg < rightRegRef.reg; return leftRegRef.reg < rightRegRef.reg;
} }
RegisterRefsWidget::RegisterRefsWidget(MainWindow *main, QAction *action) : RegisterRefsWidget::RegisterRefsWidget(MainWindow *main) :
CutterDockWidget(main, action), CutterDockWidget(main),
ui(new Ui::RegisterRefsWidget), ui(new Ui::RegisterRefsWidget),
tree(new CutterTreeWidget(this)), tree(new CutterTreeWidget(this)),
addressableItemContextMenu(this, main) addressableItemContextMenu(this, main)

View File

@ -69,7 +69,7 @@ class RegisterRefsWidget : public CutterDockWidget
Q_OBJECT Q_OBJECT
public: public:
explicit RegisterRefsWidget(MainWindow *main, QAction *action = nullptr); explicit RegisterRefsWidget(MainWindow *main);
~RegisterRefsWidget(); ~RegisterRefsWidget();
private slots: private slots:

View File

@ -8,8 +8,8 @@
#include <QLabel> #include <QLabel>
#include <QLineEdit> #include <QLineEdit>
RegistersWidget::RegistersWidget(MainWindow *main, QAction *action) : RegistersWidget::RegistersWidget(MainWindow *main) :
CutterDockWidget(main, action), CutterDockWidget(main),
ui(new Ui::RegistersWidget), ui(new Ui::RegistersWidget),
addressContextMenu(this, main) addressContextMenu(this, main)
{ {

View File

@ -21,7 +21,7 @@ class RegistersWidget : public CutterDockWidget
Q_OBJECT Q_OBJECT
public: public:
explicit RegistersWidget(MainWindow *main, QAction *action = nullptr); explicit RegistersWidget(MainWindow *main);
~RegistersWidget(); ~RegistersWidget();
private slots: private slots:

View File

@ -113,8 +113,8 @@ bool RelocsProxyModel::lessThan(const QModelIndex &left, const QModelIndex &righ
return false; return false;
} }
RelocsWidget::RelocsWidget(MainWindow *main, QAction *action) : RelocsWidget::RelocsWidget(MainWindow *main) :
ListDockWidget(main, action), ListDockWidget(main),
relocsModel(new RelocsModel(&relocs, this)), relocsModel(new RelocsModel(&relocs, this)),
relocsProxyModel(new RelocsProxyModel(relocsModel, this)) relocsProxyModel(new RelocsProxyModel(relocsModel, this))
{ {

View File

@ -54,7 +54,7 @@ class RelocsWidget : public ListDockWidget
Q_OBJECT Q_OBJECT
public: public:
explicit RelocsWidget(MainWindow *main, QAction *action = nullptr); explicit RelocsWidget(MainWindow *main);
~RelocsWidget(); ~RelocsWidget();
private slots: private slots:

View File

@ -79,8 +79,8 @@ RVA ResourcesModel::address(const QModelIndex &index) const
return res.vaddr; return res.vaddr;
} }
ResourcesWidget::ResourcesWidget(MainWindow *main, QAction *action) : ResourcesWidget::ResourcesWidget(MainWindow *main) :
ListDockWidget(main, action, ListDockWidget::SearchBarPolicy::HideByDefault) ListDockWidget(main, ListDockWidget::SearchBarPolicy::HideByDefault)
{ {
setObjectName("ResourcesWidget"); setObjectName("ResourcesWidget");

View File

@ -45,7 +45,7 @@ private:
QList<ResourcesDescription> resources; QList<ResourcesDescription> resources;
public: public:
explicit ResourcesWidget(MainWindow *main, QAction *action = nullptr); explicit ResourcesWidget(MainWindow *main);
private slots: private slots:
void refreshResources(); void refreshResources();

View File

@ -8,8 +8,8 @@
#include <QTreeWidget> #include <QTreeWidget>
SdbWidget::SdbWidget(MainWindow *main, QAction *action) : SdbWidget::SdbWidget(MainWindow *main) :
CutterDockWidget(main, action), CutterDockWidget(main),
ui(new Ui::SdbWidget) ui(new Ui::SdbWidget)
{ {
ui->setupUi(this); ui->setupUi(this);

View File

@ -17,7 +17,7 @@ class SdbWidget : public CutterDockWidget
Q_OBJECT Q_OBJECT
public: public:
explicit SdbWidget(MainWindow *main, QAction *action = nullptr); explicit SdbWidget(MainWindow *main);
~SdbWidget(); ~SdbWidget();
private slots: private slots:

View File

@ -167,8 +167,8 @@ bool SearchSortFilterProxyModel::lessThan(const QModelIndex &left, const QModelI
} }
SearchWidget::SearchWidget(MainWindow *main, QAction *action) : SearchWidget::SearchWidget(MainWindow *main) :
CutterDockWidget(main, action), CutterDockWidget(main),
ui(new Ui::SearchWidget) ui(new Ui::SearchWidget)
{ {
ui->setupUi(this); ui->setupUi(this);

View File

@ -64,7 +64,7 @@ class SearchWidget : public CutterDockWidget
Q_OBJECT Q_OBJECT
public: public:
explicit SearchWidget(MainWindow *main, QAction *action = nullptr); explicit SearchWidget(MainWindow *main);
~SearchWidget(); ~SearchWidget();
private slots: private slots:

View File

@ -154,8 +154,8 @@ bool SectionsProxyModel::lessThan(const QModelIndex &left, const QModelIndex &ri
} }
} }
SectionsWidget::SectionsWidget(MainWindow *main, QAction *action) : SectionsWidget::SectionsWidget(MainWindow *main) :
ListDockWidget(main, action) ListDockWidget(main)
{ {
setObjectName("SectionsWidget"); setObjectName("SectionsWidget");
setWindowTitle(QStringLiteral("Sections")); setWindowTitle(QStringLiteral("Sections"));

View File

@ -67,7 +67,7 @@ class SectionsWidget : public ListDockWidget
Q_OBJECT Q_OBJECT
public: public:
explicit SectionsWidget(MainWindow *main, QAction *action = nullptr); explicit SectionsWidget(MainWindow *main);
~SectionsWidget(); ~SectionsWidget();
private slots: private slots:

View File

@ -130,8 +130,8 @@ bool SegmentsProxyModel::lessThan(const QModelIndex &left, const QModelIndex &ri
return false; return false;
} }
SegmentsWidget::SegmentsWidget(MainWindow *main, QAction *action) : SegmentsWidget::SegmentsWidget(MainWindow *main) :
ListDockWidget(main, action) ListDockWidget(main)
{ {
setObjectName("SegmentsWidget"); setObjectName("SegmentsWidget");
setWindowTitle(QStringLiteral("Segments")); setWindowTitle(QStringLiteral("Segments"));

View File

@ -53,7 +53,7 @@ class SegmentsWidget : public ListDockWidget
Q_OBJECT Q_OBJECT
public: public:
explicit SegmentsWidget(MainWindow *main, QAction *action = nullptr); explicit SegmentsWidget(MainWindow *main);
~SegmentsWidget(); ~SegmentsWidget();
private slots: private slots:

View File

@ -8,8 +8,8 @@
#include "QHeaderView" #include "QHeaderView"
#include "QMenu" #include "QMenu"
StackWidget::StackWidget(MainWindow *main, QAction *action) : StackWidget::StackWidget(MainWindow *main) :
CutterDockWidget(main, action), CutterDockWidget(main),
ui(new Ui::StackWidget), ui(new Ui::StackWidget),
menuText(this), menuText(this),
addressableItemContextMenu(this, main) addressableItemContextMenu(this, main)

View File

@ -52,7 +52,7 @@ class StackWidget : public CutterDockWidget
Q_OBJECT Q_OBJECT
public: public:
explicit StackWidget(MainWindow *main, QAction *action = nullptr); explicit StackWidget(MainWindow *main);
~StackWidget(); ~StackWidget();
private slots: private slots:

View File

@ -137,8 +137,8 @@ bool StringsProxyModel::lessThan(const QModelIndex &left, const QModelIndex &rig
return leftStr->vaddr < rightStr->vaddr; return leftStr->vaddr < rightStr->vaddr;
} }
StringsWidget::StringsWidget(MainWindow *main, QAction *action) : StringsWidget::StringsWidget(MainWindow *main) :
CutterDockWidget(main, action), CutterDockWidget(main),
ui(new Ui::StringsWidget), ui(new Ui::StringsWidget),
tree(new CutterTreeWidget(this)) tree(new CutterTreeWidget(this))
{ {
@ -154,8 +154,7 @@ StringsWidget::StringsWidget(MainWindow *main, QAction *action) :
QShortcut *toggle_shortcut = new QShortcut(widgetShortcuts["StringsWidget"], main); QShortcut *toggle_shortcut = new QShortcut(widgetShortcuts["StringsWidget"], main);
connect(toggle_shortcut, &QShortcut::activated, this, [ = ] () { connect(toggle_shortcut, &QShortcut::activated, this, [ = ] () {
toggleDockWidget(true); toggleDockWidget(true);
main->updateDockActionChecked(action); });
} );
connect(ui->actionCopy_String, SIGNAL(triggered()), this, SLOT(on_actionCopy())); connect(ui->actionCopy_String, SIGNAL(triggered()), this, SLOT(on_actionCopy()));

View File

@ -70,7 +70,7 @@ class StringsWidget : public CutterDockWidget
Q_OBJECT Q_OBJECT
public: public:
explicit StringsWidget(MainWindow *main, QAction *action = nullptr); explicit StringsWidget(MainWindow *main);
~StringsWidget(); ~StringsWidget();
private slots: private slots:

View File

@ -113,8 +113,8 @@ bool SymbolsProxyModel::lessThan(const QModelIndex &left, const QModelIndex &rig
return false; return false;
} }
SymbolsWidget::SymbolsWidget(MainWindow *main, QAction *action) : SymbolsWidget::SymbolsWidget(MainWindow *main) :
ListDockWidget(main, action) ListDockWidget(main)
{ {
setWindowTitle(tr("Symbols")); setWindowTitle(tr("Symbols"));
setObjectName("SymbolsWidget"); setObjectName("SymbolsWidget");

View File

@ -58,7 +58,7 @@ class SymbolsWidget : public ListDockWidget
Q_OBJECT Q_OBJECT
public: public:
explicit SymbolsWidget(MainWindow *main, QAction *action = nullptr); explicit SymbolsWidget(MainWindow *main);
~SymbolsWidget(); ~SymbolsWidget();
private slots: private slots:

View File

@ -15,8 +15,8 @@ enum ColumnIndex {
COLUMN_PATH, COLUMN_PATH,
}; };
ThreadsWidget::ThreadsWidget(MainWindow *main, QAction *action) : ThreadsWidget::ThreadsWidget(MainWindow *main) :
CutterDockWidget(main, action), CutterDockWidget(main),
ui(new Ui::ThreadsWidget) ui(new Ui::ThreadsWidget)
{ {
ui->setupUi(this); ui->setupUi(this);

View File

@ -31,7 +31,7 @@ class ThreadsWidget : public CutterDockWidget
Q_OBJECT Q_OBJECT
public: public:
explicit ThreadsWidget(MainWindow *main, QAction *action = nullptr); explicit ThreadsWidget(MainWindow *main);
~ThreadsWidget(); ~ThreadsWidget();
private slots: private slots:

View File

@ -127,8 +127,8 @@ bool TypesSortFilterProxyModel::lessThan(const QModelIndex &left, const QModelIn
TypesWidget::TypesWidget(MainWindow *main, QAction *action) : TypesWidget::TypesWidget(MainWindow *main) :
CutterDockWidget(main, action), CutterDockWidget(main),
ui(new Ui::TypesWidget), ui(new Ui::TypesWidget),
tree(new CutterTreeWidget(this)) tree(new CutterTreeWidget(this))
{ {

View File

@ -72,7 +72,7 @@ class TypesWidget : public CutterDockWidget
Q_OBJECT Q_OBJECT
public: public:
explicit TypesWidget(MainWindow *main, QAction *action = nullptr); explicit TypesWidget(MainWindow *main);
~TypesWidget(); ~TypesWidget();
private slots: private slots:

View File

@ -127,8 +127,8 @@ bool VTableSortFilterProxyModel::filterAcceptsRow(int source_row,
} }
VTablesWidget::VTablesWidget(MainWindow *main, QAction *action) : VTablesWidget::VTablesWidget(MainWindow *main) :
CutterDockWidget(main, action), CutterDockWidget(main),
ui(new Ui::VTablesWidget), ui(new Ui::VTablesWidget),
tree(new CutterTreeWidget(this)) tree(new CutterTreeWidget(this))
{ {

View File

@ -56,7 +56,7 @@ class VTablesWidget : public CutterDockWidget
Q_OBJECT Q_OBJECT
public: public:
explicit VTablesWidget(MainWindow *main, QAction *action = nullptr); explicit VTablesWidget(MainWindow *main);
~VTablesWidget(); ~VTablesWidget();
private slots: private slots:

View File

@ -113,8 +113,8 @@ bool ZignaturesProxyModel::lessThan(const QModelIndex &left, const QModelIndex &
return leftZignature.offset < rightZignature.offset; return leftZignature.offset < rightZignature.offset;
} }
ZignaturesWidget::ZignaturesWidget(MainWindow *main, QAction *action) : ZignaturesWidget::ZignaturesWidget(MainWindow *main) :
CutterDockWidget(main, action), CutterDockWidget(main),
ui(new Ui::ZignaturesWidget) ui(new Ui::ZignaturesWidget)
{ {
ui->setupUi(this); ui->setupUi(this);

View File

@ -61,7 +61,7 @@ class ZignaturesWidget : public CutterDockWidget
Q_OBJECT Q_OBJECT
public: public:
explicit ZignaturesWidget(MainWindow *main, QAction *action = nullptr); explicit ZignaturesWidget(MainWindow *main);
~ZignaturesWidget(); ~ZignaturesWidget();
private slots: private slots: