diff --git a/src/Main.cpp b/src/Main.cpp index faed23cd..7440afad 100644 --- a/src/Main.cpp +++ b/src/Main.cpp @@ -70,7 +70,7 @@ static void initializeSettings() qInfo() << "Migrating Settings to Version" << v; switch (v) { case 1: - migrateSettingsTo1(settings); + migrateSettingsTo1(settings); break; default: break; } diff --git a/src/common/CutterSeekable.cpp b/src/common/CutterSeekable.cpp index 0be56ee3..34cd7978 100644 --- a/src/common/CutterSeekable.cpp +++ b/src/common/CutterSeekable.cpp @@ -13,6 +13,13 @@ CutterSeekable::CutterSeekable(QObject *parent) CutterSeekable::~CutterSeekable() {} +void CutterSeekable::setSynchronization(bool sync) +{ + synchronized = sync; + onCoreSeekChanged(Core()->getOffset()); + emit syncChanged(); +} + void CutterSeekable::onCoreSeekChanged(RVA addr) { if (synchronized && widgetOffset != addr) { @@ -48,8 +55,7 @@ RVA CutterSeekable::getOffset() void CutterSeekable::toggleSynchronization() { - synchronized = !synchronized; - onCoreSeekChanged(Core()->getOffset()); + setSynchronization(!synchronized); } bool CutterSeekable::isSynchronized() diff --git a/src/common/CutterSeekable.h b/src/common/CutterSeekable.h index 617cd262..fbb55be7 100644 --- a/src/common/CutterSeekable.h +++ b/src/common/CutterSeekable.h @@ -23,10 +23,10 @@ public: void seek(RVA addr) { updateSeek(addr, false); } /** - * @brief toggleSyncWithCore toggles + * @brief setSynchronization sets * Core seek synchronization. */ - void toggleSynchronization(); + void setSynchronization(bool sync); /** * @brief getOffset returns the seekable offset. @@ -50,6 +50,12 @@ public slots: */ void seekPrev(); + /** + * @brief toggleSyncWithCore toggles + * Core seek synchronization. + */ + void toggleSynchronization(); + private slots: /** * @brief onCoreSeekChanged @@ -82,5 +88,5 @@ private: signals: void seekableSeekChanged(RVA addr); - + void syncChanged(); }; diff --git a/src/core/MainWindow.cpp b/src/core/MainWindow.cpp index 44ba2167..92eb4e24 100644 --- a/src/core/MainWindow.cpp +++ b/src/core/MainWindow.cpp @@ -106,6 +106,9 @@ #include #include +template +T* getNewInstance(MainWindow *m, QAction *a) { return new T(m, a); } + MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), core(Core()), @@ -124,9 +127,21 @@ void MainWindow::initUI() { ui->setupUi(this); + connect(ui->actionExtraGraph, &QAction::triggered, this, &MainWindow::addExtraGraph); + connect(ui->actionExtraDisassembly, &QAction::triggered, this, &MainWindow::addExtraDisassembly); + connect(ui->actionExtraHexdump, &QAction::triggered, this, &MainWindow::addExtraHexdump); + + classNameToConstructorAndActionMap.insert(GraphWidget::getWidgetType(), + {getNewInstance, ui->actionGraph}); + classNameToConstructorAndActionMap.insert(DisassemblyWidget::getWidgetType(), + {getNewInstance, ui->actionDisassembly}); + classNameToConstructorAndActionMap.insert(HexdumpWidget::getWidgetType(), + {getNewInstance, ui->actionHexdump}); + initToolBar(); initDocks(); + emptyState = saveState(); /* * Some global shortcuts */ @@ -251,8 +266,6 @@ void MainWindow::initToolBar() void MainWindow::initDocks() { dockWidgets.reserve(20); - disassemblyDock = new DisassemblyWidget(this, ui->actionDisassembly); - hexdumpDock = new HexdumpWidget(this, ui->actionHexdump); pseudocodeDock = new PseudocodeWidget(this, ui->actionPseudocode); consoleDock = new ConsoleWidget(this, ui->actionConsole); @@ -271,7 +284,6 @@ void MainWindow::initDocks() }); ui->actionOverview->setChecked(overviewDock->getUserOpened()); - graphDock = new GraphWidget(this, ui->actionGraph); sectionsDock = new SectionsWidget(this, ui->actionSections); segmentsDock = new SegmentsWidget(this, ui->actionSegments); entrypointDock = new EntrypointWidget(this, ui->actionEntrypoints); @@ -298,6 +310,38 @@ void MainWindow::initDocks() classesDock = new ClassesWidget(this, ui->actionClasses); resourcesDock = new ResourcesWidget(this, ui->actionResources); vTablesDock = new VTablesWidget(this, ui->actionVTables); + + QSettings s; + QStringList docks = s.value("docks").toStringList(); + + // Restore all extra widgets + QString className; + for (const auto &it : docks) { + if (std::none_of(dockWidgets.constBegin(), dockWidgets.constEnd(), + [&it](QDockWidget *w) { return w->objectName() == it; })) { + className = it.split(';').at(0); + if (classNameToConstructorAndActionMap.contains(className)) { + auto widget = classNameToConstructorAndActionMap[className] + .first(this, classNameToConstructorAndActionMap[className].second); + widget->setObjectName(it); + addExtraWidget(widget); + } + } + } + + updateMemberPointers(); + + if (!disassemblyDock) { + addExtraDisassembly(); + } + if (!graphDock) { + addExtraGraph(); + } + if (!hexdumpDock) { + addExtraHexdump(); + } + + updateMemberPointers(); } void MainWindow::initLayout() @@ -306,10 +350,8 @@ void MainWindow::initLayout() enableDebugWidgetsMenu(false); // Restore saved settings readSettingsOrDefault(); - // 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); + + initCorners(); } void MainWindow::toggleOverview(bool visibility, GraphWidget *targetGraph) @@ -328,28 +370,31 @@ void MainWindow::updateTasksIndicator() tasksProgressIndicator->setProgressIndicatorVisible(running); } -void MainWindow::on_actionExtraGraph_triggered() +void MainWindow::addExtraGraph() { - auto *extraDock = new GraphWidget(this, nullptr); + auto *extraDock = new GraphWidget(this, ui->actionGraph); + extraDock->setObjectName(getUniqueObjectName(extraDock->getWidgetType())); addExtraWidget(extraDock); } -void MainWindow::on_actionExtraHexdump_triggered() +void MainWindow::addExtraHexdump() { - auto *extraDock = new HexdumpWidget(this, nullptr); + auto *extraDock = new HexdumpWidget(this, ui->actionHexdump); + extraDock->setObjectName(getUniqueObjectName(extraDock->getWidgetType())); addExtraWidget(extraDock); } -void MainWindow::on_actionExtraDisassembly_triggered() +void MainWindow::addExtraDisassembly() { - auto *extraDock = new DisassemblyWidget(this, nullptr); + auto *extraDock = new DisassemblyWidget(this, ui->actionDisassembly); + extraDock->setObjectName(getUniqueObjectName(extraDock->getWidgetType())); addExtraWidget(extraDock); } void MainWindow::addExtraWidget(CutterDockWidget *extraDock) { extraDock->setTransient(true); - addDockWidget(Qt::TopDockWidgetArea, extraDock); + addDockWidget(Qt::TopDockWidgetArea, extraDock, Qt::Orientation::Horizontal); auto restoreExtraDock = qhelpers::forceWidth(extraDock->widget(), 600); qApp->processEvents(); restoreExtraDock.restoreWidth(extraDock->widget()); @@ -385,7 +430,8 @@ QMenu *MainWindow::getMenuByType(MenuType type) void MainWindow::addPluginDockWidget(QDockWidget *dockWidget, QAction *action) { addDockWidget(Qt::TopDockWidgetArea, dockWidget); - addDockWidgetAction(dockWidget, action); + dockWidget->addAction(action); + addWidget(dockWidget); ui->menuPlugins->addAction(action); addDockWidget(Qt::DockWidgetArea::TopDockWidgetArea, dockWidget); updateDockActionChecked(action); @@ -494,6 +540,14 @@ void MainWindow::finalizeOpen() showMaximized(); + QSettings s; + QStringList unsync = s.value("unsync").toStringList(); + for (auto it : dockWidgets) { + auto w = qobject_cast(it); + if (w) { + w->getSeekable()->setSynchronization(!unsync.contains(it->objectName())); + } + } // Set focus to disasm or graph widget @@ -606,7 +660,7 @@ void MainWindow::readSettingsOrDefault() * Check if saved settings exist * If not, then read the default layout */ - if (!geo.length() && !state.length()) { + if (!geo.length() || !state.length()) { resetToDefaultLayout(); return; } @@ -642,6 +696,22 @@ void MainWindow::readSettingsOrDefault() void MainWindow::saveSettings() { QSettings settings; + + QStringList docks; + const QStringList syncable = QStringList() + << HexdumpWidget::getWidgetType() + << DisassemblyWidget::getWidgetType() + << GraphWidget::getWidgetType(); + QStringList unsync; + for (const auto &it : dockWidgets) { + docks.append(it->objectName()); + if (syncable.contains(it->metaObject()->className()) && + !qobject_cast(it)->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()); @@ -728,22 +798,7 @@ void MainWindow::restoreDocks() addDockWidget(Qt::TopDockWidgetArea, overviewDock); // Function | Dashboard - splitDockWidget(overviewDock, dashboardDock, Qt::Horizontal); - splitDockWidget(functionsDock, overviewDock, Qt::Vertical); - - // In the lower half the console is the first widget - addDockWidget(Qt::BottomDockWidgetArea, consoleDock); - - // Console | Sections - splitDockWidget(consoleDock, sectionsDock, Qt::Horizontal); - splitDockWidget(consoleDock, segmentsDock, Qt::Horizontal); - - // Tabs for center (must be applied after splitDockWidget()) - tabifyDockWidget(sectionsDock, commentsDock); - tabifyDockWidget(segmentsDock, commentsDock); - tabifyDockWidget(dashboardDock, disassemblyDock); - tabifyDockWidget(dashboardDock, graphDock); - tabifyDockWidget(dashboardDock, hexdumpDock); + splitDockWidget(functionsDock, dashboardDock, Qt::Horizontal); tabifyDockWidget(dashboardDock, pseudocodeDock); tabifyDockWidget(dashboardDock, entrypointDock); tabifyDockWidget(dashboardDock, flagsDock); @@ -760,17 +815,34 @@ void MainWindow::restoreDocks() tabifyDockWidget(dashboardDock, resourcesDock); tabifyDockWidget(dashboardDock, vTablesDock); tabifyDockWidget(dashboardDock, sdbDock); + tabifyDockWidget(dashboardDock, memoryMapDock); + tabifyDockWidget(dashboardDock, breakpointDock); + tabifyDockWidget(dashboardDock, registerRefsDock); + for (const auto &it : dockWidgets) { + // Check whether or not current widgets is graph, hexdump or disasm + if (qobject_cast(it) || + qobject_cast(it) || + qobject_cast(it)) { + tabifyDockWidget(dashboardDock, it); + } + } + + splitDockWidget(functionsDock, overviewDock, Qt::Vertical); + + // In the lower half the console is the first widget + addDockWidget(Qt::BottomDockWidgetArea, consoleDock); + + // Console | Sections + splitDockWidget(consoleDock, sectionsDock, Qt::Horizontal); + splitDockWidget(consoleDock, segmentsDock, Qt::Horizontal); + + tabifyDockWidget(sectionsDock, commentsDock); // Add Stack, Registers and Backtrace vertically stacked addDockWidget(Qt::TopDockWidgetArea, stackDock); splitDockWidget(stackDock, registersDock, Qt::Vertical); tabifyDockWidget(stackDock, backtraceDock); - // MemoryMap/Breakpoint/RegRefs widget goes in the center tabs - tabifyDockWidget(dashboardDock, memoryMapDock); - tabifyDockWidget(dashboardDock, breakpointDock); - tabifyDockWidget(dashboardDock, registerRefsDock); - updateDockActionsChecked(); } @@ -784,14 +856,88 @@ void MainWindow::hideAllDocks() void MainWindow::updateDockActionsChecked() { - for (auto i = dockWidgetActions.constBegin(); i != dockWidgetActions.constEnd(); i++) { - i.key()->setChecked(!i.value()->isHidden()); + for (auto i = dockWidgetsOfAction.constBegin(); i != dockWidgetsOfAction.constEnd(); i++) { + updateDockActionChecked(i.key()); + } +} + +QString MainWindow::getUniqueObjectName(const QString& widgetType) const +{ + QStringList docks; + docks.reserve(dockWidgets.size()); + QString name; + for (const auto &it : dockWidgets) { + name = it->objectName(); + if (name.split(';').at(0) == widgetType && name != "Graph Overview") { + docks.push_back(name); + } + } + + if (docks.isEmpty()) { + return widgetType; + } + + int id = 0; + while (docks.contains(widgetType + ";" + QString::number(id))) { + id++; + } + + return widgetType + ";" + QString::number(id); +} + +void MainWindow::initCorners() +{ + // TODO: Allow the user to select this option visually in the GUI settings + // Adjust the DockWidget areas + + setCorner(Qt::TopLeftCorner, Qt::LeftDockWidgetArea); + setCorner(Qt::BottomLeftCorner, Qt::LeftDockWidgetArea); + + setCorner(Qt::BottomRightCorner, Qt::BottomDockWidgetArea); + setCorner(Qt::TopRightCorner, Qt::RightDockWidgetArea); +} + +void MainWindow::updateMemberPointers() +{ + QString className; + for (auto it : dockWidgets) { + if (!graphDock) { + graphDock = qobject_cast(it); + } + if (!disassemblyDock) { + disassemblyDock = qobject_cast(it); + } + if (!hexdumpDock) { + hexdumpDock = qobject_cast(it); + } + } +} + +void MainWindow::addWidget(QDockWidget* widget) +{ + dockWidgets.push_back(widget); + for (auto action : widget->actions()) { + dockWidgetsOfAction.insert(action, widget); + connect(qobject_cast(widget), &CutterDockWidget::closed, + this, [this]() { + QDockWidget *widget = qobject_cast(sender()); + dockWidgets.removeOne(widget); + for (auto action : widget->actions()) { + dockWidgetsOfAction.remove(action, widget); + } + updateMemberPointers(); + updateDockActionsChecked(); + }); } } void MainWindow::updateDockActionChecked(QAction *action) { - action->setChecked(!dockWidgetActions[action]->isHidden()); + auto actions = dockWidgetsOfAction.values(action); + action->setChecked(!std::accumulate(actions.begin(), actions.end(), false, + [](bool a, QDockWidget* w) -> bool { + return a || w->isHidden(); + })); } void MainWindow::showZenDocks() @@ -799,17 +945,20 @@ void MainWindow::showZenDocks() const QList zenDocks = { functionsDock, dashboardDock, stringsDock, - graphDock, - disassemblyDock, - hexdumpDock, searchDock, - importsDock, + importsDock }; + int width = functionsDock->maximumWidth(); + functionsDock->setMaximumWidth(200); for (auto w : dockWidgets) { - if (zenDocks.contains(w)) { + if (zenDocks.contains(w) || + qobject_cast(w) || + qobject_cast(w) || + qobject_cast(w)) { w->show(); } } + functionsDock->setMaximumWidth(width); updateDockActionsChecked(); } @@ -817,9 +966,6 @@ void MainWindow::showDebugDocks() { const QList debugDocks = { functionsDock, stringsDock, - graphDock, - disassemblyDock, - hexdumpDock, searchDock, stackDock, registersDock, @@ -827,11 +973,17 @@ void MainWindow::showDebugDocks() memoryMapDock, breakpointDock }; + int width = functionsDock->maximumWidth(); + functionsDock->setMaximumWidth(200); for (auto w : dockWidgets) { - if (debugDocks.contains(w)) { + if (debugDocks.contains(w) || + qobject_cast(w) || + qobject_cast(w) || + qobject_cast(w)) { w->show(); } } + functionsDock->setMaximumWidth(width); updateDockActionsChecked(); } @@ -877,6 +1029,23 @@ void MainWindow::restoreDebugLayout() } } +void MainWindow::resetDockWidgetList() +{ + QStringList isLeft; + QList toClose; + for (auto it : dockWidgets) { + if (isLeft.contains(it->metaObject()->className())) { + toClose.append(it); + } else if (QRegExp("\\w+ \\d+").exactMatch(it->objectName())) { + isLeft.append(it->metaObject()->className()); + } + } + for (auto it : toClose) { + it->close(); + } + updateMemberPointers(); +} + void MainWindow::on_actionLock_triggered() { panelLock = !panelLock; @@ -908,9 +1077,21 @@ void MainWindow::on_actionFunctionsRename_triggered() void MainWindow::on_actionDefault_triggered() { + QSettings s; + restoreState(emptyState); + + initCorners(); + resetDockWidgetList(); + if (core->currentlyDebugging) { + resetToDefaultLayout(); + saveSettings(); + resetToDebugLayout(); } else { + resetToDebugLayout(); + saveDebugSettings(); + resetToDefaultLayout(); } } @@ -1207,16 +1388,6 @@ bool MainWindow::eventFilter(QObject *, QEvent *event) return false; } -void MainWindow::addToDockWidgetList(QDockWidget *dockWidget) -{ - this->dockWidgets.push_back(dockWidget); -} - -void MainWindow::addDockWidgetAction(QDockWidget *dockWidget, QAction *action) -{ - this->dockWidgetActions[action] = dockWidget; -} - /** * @brief Show a warning message box. * diff --git a/src/core/MainWindow.h b/src/core/MainWindow.h index 6fa4c303..b77716b3 100644 --- a/src/core/MainWindow.h +++ b/src/core/MainWindow.h @@ -94,8 +94,7 @@ public: void setFilename(const QString &fn); void refreshOmniBar(const QStringList &flags); - void addToDockWidgetList(QDockWidget *dockWidget); - void addDockWidgetAction(QDockWidget *dockWidget, QAction *action); + void addWidget(QDockWidget* widget); void addExtraWidget(CutterDockWidget *extraDock); void addPluginDockWidget(QDockWidget *dockWidget, QAction *action); @@ -138,9 +137,9 @@ public slots: private slots: void on_actionAbout_triggered(); void on_actionIssue_triggered(); - void on_actionExtraGraph_triggered(); - void on_actionExtraHexdump_triggered(); - void on_actionExtraDisassembly_triggered(); + void addExtraGraph(); + void addExtraHexdump(); + void addExtraDisassembly(); void on_actionRefresh_Panels_triggered(); @@ -204,11 +203,12 @@ private: VisualNavbar *visualNavbar; Omnibar *omnibar; ProgressIndicator *tasksProgressIndicator; + QByteArray emptyState; Configuration *configuration; QList dockWidgets; - QMap dockWidgetActions; + QMultiMap dockWidgetsOfAction; DisassemblyWidget *disassemblyDock = nullptr; HexdumpWidget *hexdumpDock = nullptr; PseudocodeWidget *pseudocodeDock = nullptr; @@ -250,12 +250,15 @@ private: void initToolBar(); void initDocks(); void initLayout(); + void initCorners(); void displayInitialOptionsDialog(const InitialOptions &options = InitialOptions(), bool skipOptionsDialog = false); void resetToDefaultLayout(); void resetToDebugLayout(); void restoreDebugLayout(); + void updateMemberPointers(); + void resetDockWidgetList(); void restoreDocks(); void hideAllDocks(); void showZenDocks(); @@ -267,6 +270,14 @@ private: void updateDockActionsChecked(); void setOverviewData(); bool isOverviewActive(); + + /** + * @brief Map, where key is class name an value is pair of + * pointer to class constructor and action that passed to this constructor. + */ + QMap, QAction*>> classNameToConstructorAndActionMap; + + QString getUniqueObjectName(const QString &widgetType) const; }; #endif // MAINWINDOW_H diff --git a/src/widgets/CutterDockWidget.cpp b/src/widgets/CutterDockWidget.cpp index 8f4d5822..6b925be2 100644 --- a/src/widgets/CutterDockWidget.cpp +++ b/src/widgets/CutterDockWidget.cpp @@ -8,12 +8,12 @@ CutterDockWidget::CutterDockWidget(MainWindow *parent, QAction *action) : QDockWidget(parent), action(action) { + if (action) { + addAction(action); + connect(action, &QAction::triggered, this, &CutterDockWidget::toggleDockWidget); + } if (parent) { - parent->addToDockWidgetList(this); - if (action) { - parent->addDockWidgetAction(this, action); - connect(action, &QAction::triggered, this, &CutterDockWidget::toggleDockWidget); - } + parent->addWidget(this); } // Install event filter to catch redraw widgets when needed @@ -39,7 +39,7 @@ bool CutterDockWidget::eventFilter(QObject *object, QEvent *event) void CutterDockWidget::toggleDockWidget(bool show) { if (!show) { - this->close(); + this->hide(); } else { this->show(); this->raise(); @@ -73,6 +73,8 @@ void CutterDockWidget::closeEvent(QCloseEvent *event) if (isTransient) { deleteLater(); } + + emit closed(); } QAction *CutterDockWidget::getBoundAction() const diff --git a/src/widgets/CutterDockWidget.h b/src/widgets/CutterDockWidget.h index 8793a4c2..96a9bfdb 100644 --- a/src/widgets/CutterDockWidget.h +++ b/src/widgets/CutterDockWidget.h @@ -57,6 +57,7 @@ public: signals: void becameVisibleToUser(); + void closed(); public slots: void toggleDockWidget(bool show); diff --git a/src/widgets/DisassemblerGraphView.cpp b/src/widgets/DisassemblerGraphView.cpp index 180284a2..89bd3057 100644 --- a/src/widgets/DisassemblerGraphView.cpp +++ b/src/widgets/DisassemblerGraphView.cpp @@ -29,12 +29,12 @@ #include -DisassemblerGraphView::DisassemblerGraphView(QWidget *parent) +DisassemblerGraphView::DisassemblerGraphView(QWidget *parent, CutterSeekable* seekable) : GraphView(parent), mFontMetrics(nullptr), blockMenu(new DisassemblyContextMenu(this)), contextMenu(new QMenu(this)), - seekable(new CutterSeekable(this)) + seekable(seekable) { highlight_token = nullptr; auto *layout = new QVBoxLayout(this); @@ -107,7 +107,7 @@ DisassemblerGraphView::DisassemblerGraphView(QWidget *parent) actionExportGraph.setText(tr("Export Graph")); connect(&actionExportGraph, SIGNAL(triggered(bool)), this, SLOT(on_actionExportGraph_triggered())); actionSyncOffset.setText(tr("Sync/unsync offset")); - connect(&actionSyncOffset, SIGNAL(triggered(bool)), this, SLOT(toggleSync())); + connect(&actionSyncOffset, &QAction::triggered, seekable, &CutterSeekable::toggleSynchronization); // Context menu that applies to everything contextMenu->addAction(&actionExportGraph); @@ -190,16 +190,6 @@ DisassemblerGraphView::~DisassemblerGraphView() } } -void DisassemblerGraphView::toggleSync() -{ - seekable->toggleSynchronization(); - if (seekable->isSynchronized()) { - parentWidget()->setWindowTitle(windowTitle); - } else { - parentWidget()->setWindowTitle(windowTitle + CutterSeekable::tr(" (unsynced)")); - } -} - void DisassemblerGraphView::refreshView() { initFont(); @@ -259,11 +249,7 @@ void DisassemblerGraphView::loadCurrentGraph() } else if (!funcName.isEmpty()) { windowTitle += " (" + funcName + ")"; } - if (!seekable->isSynchronized()) { - parentWidget()->setWindowTitle(windowTitle + CutterSeekable::tr(" (unsynced)")); - } else { - parentWidget()->setWindowTitle(windowTitle); - } + emit nameChanged(windowTitle); RVA entry = func["offset"].toVariant().toULongLong(); diff --git a/src/widgets/DisassemblerGraphView.h b/src/widgets/DisassemblerGraphView.h index f1da4031..dee2ab43 100644 --- a/src/widgets/DisassemblerGraphView.h +++ b/src/widgets/DisassemblerGraphView.h @@ -87,7 +87,7 @@ class DisassemblerGraphView : public GraphView }; public: - DisassemblerGraphView(QWidget *parent); + DisassemblerGraphView(QWidget *parent, CutterSeekable* seekable); ~DisassemblerGraphView() override; std::unordered_map disassembly_blocks; virtual void drawBlock(QPainter &p, GraphView::GraphBlock &block) override; @@ -115,8 +115,6 @@ public slots: void colorsUpdatedSlot(); void fontsUpdatedSlot(); void onSeekChanged(RVA addr); - void toggleSync(); - void zoom(QPointF mouseRelativePos, double velocity); void zoomReset(); @@ -215,6 +213,7 @@ signals: void viewZoomed(); void graphMoved(); void resized(); + void nameChanged(const QString& name); public: bool isGraphEmpty() { return emptyGraph; } diff --git a/src/widgets/DisassemblyWidget.cpp b/src/widgets/DisassemblyWidget.cpp index 5fc4c9f9..d4a2ee17 100644 --- a/src/widgets/DisassemblyWidget.cpp +++ b/src/widgets/DisassemblyWidget.cpp @@ -38,26 +38,15 @@ DisassemblyWidget::DisassemblyWidget(MainWindow *main, QAction *action) , mCtxMenu(new DisassemblyContextMenu(this)) , mDisasScrollArea(new DisassemblyScrollArea(this)) , mDisasTextEdit(new DisassemblyTextEdit(this)) - , seekable(new CutterSeekable(this)) { - /* - * Ugly hack just for the layout issue - * QSettings saves the state with the object names - * By doing this hack, - * you can at least avoid some mess by dismissing all the Extra Widgets - */ - QString name = "Disassembly"; - if (!action) { - name = "Extra Disassembly"; - } - setObjectName(name); + setObjectName(getWidgetType()); topOffset = bottomOffset = RVA_INVALID; cursorLineOffset = 0; cursorCharOffset = 0; seekFromCursor = false; - setWindowTitle(tr("Disassembly")); + setWindowTitle(getWindowTitle()); QVBoxLayout *layout = new QVBoxLayout(); layout->addWidget(mDisasTextEdit); @@ -144,7 +133,7 @@ DisassemblyWidget::DisassemblyWidget(MainWindow *main, QAction *action) mCtxMenu->addSeparator(); syncIt.setText(tr("Sync/unsync offset")); mCtxMenu->addAction(&syncIt); - connect(&syncIt, SIGNAL(triggered(bool)), this, SLOT(toggleSync())); + connect(&syncIt, &QAction::triggered, seekable, &CutterSeekable::toggleSynchronization); connect(seekable, &CutterSeekable::seekableSeekChanged, this, &DisassemblyWidget::on_seekChanged); addActions(mCtxMenu->actions()); @@ -191,17 +180,6 @@ DisassemblyWidget::DisassemblyWidget(MainWindow *main, QAction *action) #undef ADD_ACTION } -void DisassemblyWidget::toggleSync() -{ - QString windowTitle = tr("Disassembly"); - seekable->toggleSynchronization(); - if (seekable->isSynchronized()) { - setWindowTitle(windowTitle); - } else { - setWindowTitle(windowTitle + CutterSeekable::tr(" (unsynced)")); - } -} - void DisassemblyWidget::setPreviewMode(bool previewMode) { mDisasTextEdit->setContextMenuPolicy(previewMode @@ -217,8 +195,8 @@ void DisassemblyWidget::setPreviewMode(bool previewMode) action->setEnabled(!previewMode); } } - if (seekable->isSynchronized() && previewMode) { - toggleSync(); + if (previewMode) { + seekable->setSynchronization(false); } } @@ -227,6 +205,11 @@ QWidget *DisassemblyWidget::getTextWidget() return mDisasTextEdit; } +QString DisassemblyWidget::getWidgetType() +{ + return "Disassembly"; +} + void DisassemblyWidget::refreshDisasm(RVA offset) { if(!disasmRefresh->attemptRefresh(offset == RVA_INVALID ? nullptr : new RVA(offset))) { @@ -655,6 +638,11 @@ bool DisassemblyWidget::eventFilter(QObject *obj, QEvent *event) return CutterDockWidget::eventFilter(obj, event); } +QString DisassemblyWidget::getWindowTitle() const +{ + return tr("Disassembly"); +} + void DisassemblyWidget::on_seekChanged(RVA offset) { if (!seekFromCursor) { diff --git a/src/widgets/DisassemblyWidget.h b/src/widgets/DisassemblyWidget.h index 1ab5787d..78933d89 100644 --- a/src/widgets/DisassemblyWidget.h +++ b/src/widgets/DisassemblyWidget.h @@ -23,13 +23,14 @@ public: explicit DisassemblyWidget(MainWindow *main, QAction *action = nullptr); QWidget *getTextWidget(); + static QString getWidgetType(); + public slots: void highlightCurrentLine(); void showDisasContextMenu(const QPoint &pt); void fontsUpdatedSlot(); void colorsUpdatedSlot(); void seekPrev(); - void toggleSync(); void setPreviewMode(bool previewMode); protected slots: @@ -68,6 +69,7 @@ private: RVA readCurrentDisassemblyOffset(); RVA readDisassemblyOffset(QTextCursor tc); bool eventFilter(QObject *obj, QEvent *event) override; + QString getWindowTitle() const override; QList breakpoints; @@ -82,7 +84,6 @@ private: QList getSameWordsSelections(); QAction syncIt; - CutterSeekable *seekable; }; class DisassemblyScrollArea : public QAbstractScrollArea diff --git a/src/widgets/GraphWidget.cpp b/src/widgets/GraphWidget.cpp index bb1c62b4..c69f4c49 100644 --- a/src/widgets/GraphWidget.cpp +++ b/src/widgets/GraphWidget.cpp @@ -6,19 +6,9 @@ GraphWidget::GraphWidget(MainWindow *main, QAction *action) : MemoryDockWidget(CutterCore::MemoryWidgetType::Graph, main, action) { - /* - * Ugly hack just for the layout issue - * QSettings saves the state with the object names - * By doing this hack, - * you can at least avoid some mess by dismissing all the Extra Widgets - */ - QString name = "Graph"; - if (!action) { - name = "Extra Graph"; - } - setObjectName(name); + setObjectName(getWidgetType()); setAllowedAreas(Qt::AllDockWidgetAreas); - graphView = new DisassemblerGraphView(this); + graphView = new DisassemblerGraphView(this, seekable); setWidget(graphView); // getting the name of the class is implementation defined, and cannot be @@ -30,6 +20,8 @@ GraphWidget::GraphWidget(MainWindow *main, QAction *action) : main->updateDockActionChecked(action); }); + connect(graphView, &DisassemblerGraphView::nameChanged, this, &MemoryDockWidget::updateWindowTitle); + connect(this, &QDockWidget::visibilityChanged, this, [ = ](bool visibility) { main->toggleOverview(visibility, this); if (visibility) { @@ -54,7 +46,17 @@ void GraphWidget::closeEvent(QCloseEvent *event) emit graphClosed(); } +QString GraphWidget::getWindowTitle() const +{ + return graphView->windowTitle; +} + DisassemblerGraphView *GraphWidget::getGraphView() const { return graphView; } + +QString GraphWidget::getWidgetType() +{ + return "Graph"; +} diff --git a/src/widgets/GraphWidget.h b/src/widgets/GraphWidget.h index 010d17e6..bc48b99d 100644 --- a/src/widgets/GraphWidget.h +++ b/src/widgets/GraphWidget.h @@ -12,10 +12,12 @@ class GraphWidget : public MemoryDockWidget public: explicit GraphWidget(MainWindow *main, QAction *action = nullptr); - ~GraphWidget() {} + ~GraphWidget() override {} DisassemblerGraphView *getGraphView() const; + static QString getWidgetType(); + signals: void graphClosed(); @@ -25,6 +27,8 @@ protected: private: void closeEvent(QCloseEvent *event) override; + QString getWindowTitle() const override; + DisassemblerGraphView *graphView; }; diff --git a/src/widgets/HexdumpWidget.cpp b/src/widgets/HexdumpWidget.cpp index 2b142462..74b27256 100644 --- a/src/widgets/HexdumpWidget.cpp +++ b/src/widgets/HexdumpWidget.cpp @@ -17,22 +17,11 @@ HexdumpWidget::HexdumpWidget(MainWindow *main, QAction *action) : MemoryDockWidget(CutterCore::MemoryWidgetType::Hexdump, main, action), - ui(new Ui::HexdumpWidget), - seekable(new CutterSeekable(this)) + ui(new Ui::HexdumpWidget) { ui->setupUi(this); - /* - * Ugly hack just for the layout issue - * QSettings saves the state with the object names - * By doing this hack, - * you can at least avoid some mess by dismissing all the Extra Widgets - */ - QString name = "Hexdump"; - if (!action) { - name = "Extra Hexdump"; - } - setObjectName(name); + setObjectName(getWidgetType()); ui->copyMD5->setIcon(QIcon(":/img/icons/copy.svg")); ui->copySHA1->setIcon(QIcon(":/img/icons/copy.svg")); @@ -75,13 +64,13 @@ HexdumpWidget::HexdumpWidget(MainWindow *main, QAction *action) : " border-color : #3daee9" "}"); - this->setWindowTitle(tr("Hexdump")); + setWindowTitle(getWindowTitle()); refreshDeferrer = createReplacingRefreshDeferrer(false, [this](const RVA *offset) { refresh(offset ? *offset : RVA_INVALID); }); - connect(&syncAction, SIGNAL(triggered(bool)), this, SLOT(toggleSync())); + connect(&syncAction, &QAction::triggered, seekable, &CutterSeekable::toggleSynchronization); syncAction.setText(tr("Sync/unsync offset")); this->ui->hexTextView->addAction(&syncAction); @@ -121,6 +110,11 @@ void HexdumpWidget::onSeekChanged(RVA addr) HexdumpWidget::~HexdumpWidget() {} +QString HexdumpWidget::getWidgetType() +{ + return "Hexdump"; +} + void HexdumpWidget::refresh() { refresh(RVA_INVALID); @@ -169,19 +163,6 @@ void HexdumpWidget::on_parseBitsComboBox_currentTextChanged(const QString &/*arg refreshSelectionInfo(); } - -void HexdumpWidget::toggleSync() -{ - QString windowTitle = tr("Hexdump"); - seekable->toggleSynchronization(); - if (seekable->isSynchronized()) { - setWindowTitle(windowTitle); - } else { - setWindowTitle(windowTitle + CutterSeekable::tr(" (unsynced)")); - } -} - - void HexdumpWidget::setupFonts() { QFont font = Config()->getFont(); @@ -216,6 +197,11 @@ void HexdumpWidget::showSidePanel(bool show) } } +QString HexdumpWidget::getWindowTitle() const +{ + return tr("Hexdump"); +} + void HexdumpWidget::updateParseWindow(RVA start_address, int size) { if (!ui->hexSideTab_2->isVisible()) { diff --git a/src/widgets/HexdumpWidget.h b/src/widgets/HexdumpWidget.h index c0575fdc..b1e54d3b 100644 --- a/src/widgets/HexdumpWidget.h +++ b/src/widgets/HexdumpWidget.h @@ -34,10 +34,11 @@ public: ~HexdumpWidget() override; Highlighter *highlighter; + static QString getWidgetType(); + public slots: void initParsing(); - void toggleSync(); protected: virtual void resizeEvent(QResizeEvent *event) override; QWidget *widgetToFocusOnRaise() override; @@ -59,8 +60,9 @@ private: void clearParseWindow(); void showSidePanel(bool show); + QString getWindowTitle() const override; + QAction syncAction; - CutterSeekable *seekable; private slots: void onSeekChanged(RVA addr); diff --git a/src/widgets/MemoryDockWidget.cpp b/src/widgets/MemoryDockWidget.cpp index 84495643..3f4033d4 100644 --- a/src/widgets/MemoryDockWidget.cpp +++ b/src/widgets/MemoryDockWidget.cpp @@ -1,12 +1,14 @@ #include "MemoryDockWidget.h" +#include "common/CutterSeekable.h" #include MemoryDockWidget::MemoryDockWidget(CutterCore::MemoryWidgetType type, MainWindow *parent, QAction *action) : CutterDockWidget(parent, action) - , mType(type) + , mType(type), seekable(new CutterSeekable(this)) { connect(Core(), &CutterCore::raisePrioritizedMemoryWidget, this, &MemoryDockWidget::handleRaiseMemoryWidget); + connect(seekable, &CutterSeekable::syncChanged, this, &MemoryDockWidget::updateWindowTitle); } void MemoryDockWidget::handleRaiseMemoryWidget(CutterCore::MemoryWidgetType raiseType) @@ -26,3 +28,17 @@ void MemoryDockWidget::handleRaiseMemoryWidget(CutterCore::MemoryWidgetType rais widgetToFocusOnRaise()->setFocus(Qt::FocusReason::TabFocusReason); } } + +void MemoryDockWidget::updateWindowTitle() +{ + if (seekable->isSynchronized()) { + setWindowTitle(getWindowTitle()); + } else { + setWindowTitle(getWindowTitle() + CutterSeekable::tr(" (unsynced)")); + } +} + +CutterSeekable* MemoryDockWidget::getSeekable() const +{ + return seekable; +} diff --git a/src/widgets/MemoryDockWidget.h b/src/widgets/MemoryDockWidget.h index 65cbee05..f5a10bf9 100644 --- a/src/widgets/MemoryDockWidget.h +++ b/src/widgets/MemoryDockWidget.h @@ -4,6 +4,8 @@ #include "CutterDockWidget.h" #include "core/Cutter.h" +class CutterSeekable; + class MemoryDockWidget : public CutterDockWidget { Q_OBJECT @@ -11,10 +13,20 @@ public: MemoryDockWidget(CutterCore::MemoryWidgetType type, MainWindow *parent, QAction *action = nullptr); ~MemoryDockWidget() {} + CutterSeekable* getSeekable() const; + private: void handleRaiseMemoryWidget(CutterCore::MemoryWidgetType raiseType); CutterCore::MemoryWidgetType mType; + +public slots: + void updateWindowTitle(); + +protected: + CutterSeekable *seekable = nullptr; + + virtual QString getWindowTitle() const = 0; }; #endif // MEMORYDOCKWIDGET_H diff --git a/src/widgets/PseudocodeWidget.cpp b/src/widgets/PseudocodeWidget.cpp index d808905f..c39f670f 100644 --- a/src/widgets/PseudocodeWidget.cpp +++ b/src/widgets/PseudocodeWidget.cpp @@ -86,6 +86,11 @@ void PseudocodeWidget::setupFonts() ui->textEdit->setFont(font); } +QString PseudocodeWidget::getWindowTitle() const +{ + return tr("Pseudocode"); +} + void PseudocodeWidget::fontsUpdated() { setupFonts(); diff --git a/src/widgets/PseudocodeWidget.h b/src/widgets/PseudocodeWidget.h index 7202481a..f3d04b59 100644 --- a/src/widgets/PseudocodeWidget.h +++ b/src/widgets/PseudocodeWidget.h @@ -34,6 +34,8 @@ private: void doRefresh(RVA addr); void setupFonts(); + + QString getWindowTitle() const override; }; #endif // PSEUDOCODEWIDGET_H