"Fossies" - the Fresh Open Source Software Archive

Member "cutter-1.10.3/src/core/MainWindow.cpp" (8 May 2020, 53662 Bytes) of package /linux/privat/cutter-1.10.3.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "MainWindow.cpp" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 1.10.2_vs_1.10.3.

    1 #include "core/MainWindow.h"
    2 #include "ui_MainWindow.h"
    3 
    4 // Common Headers
    5 #include "common/BugReporting.h"
    6 #include "common/Highlighter.h"
    7 #include "common/HexAsciiHighlighter.h"
    8 #include "common/Helpers.h"
    9 #include "common/SvgIconEngine.h"
   10 #include "common/ProgressIndicator.h"
   11 #include "common/TempConfig.h"
   12 #include "common/RunScriptTask.h"
   13 #include "common/PythonManager.h"
   14 #include "plugins/PluginManager.h"
   15 #include "CutterConfig.h"
   16 #include "CutterApplication.h"
   17 
   18 // Dialogs
   19 #include "dialogs/WelcomeDialog.h"
   20 #include "dialogs/NewFileDialog.h"
   21 #include "dialogs/InitialOptionsDialog.h"
   22 #include "dialogs/SaveProjectDialog.h"
   23 #include "dialogs/CommentsDialog.h"
   24 #include "dialogs/AboutDialog.h"
   25 #include "dialogs/RenameDialog.h"
   26 #include "dialogs/preferences/PreferencesDialog.h"
   27 #include "dialogs/MapFileDialog.h"
   28 #include "dialogs/AsyncTaskDialog.h"
   29 
   30 // Widgets Headers
   31 #include "widgets/DisassemblerGraphView.h"
   32 #include "widgets/GraphView.h"
   33 #include "widgets/GraphWidget.h"
   34 #include "widgets/OverviewWidget.h"
   35 #include "widgets/OverviewView.h"
   36 #include "widgets/FunctionsWidget.h"
   37 #include "widgets/SectionsWidget.h"
   38 #include "widgets/SegmentsWidget.h"
   39 #include "widgets/CommentsWidget.h"
   40 #include "widgets/ImportsWidget.h"
   41 #include "widgets/ExportsWidget.h"
   42 #include "widgets/TypesWidget.h"
   43 #include "widgets/SearchWidget.h"
   44 #include "widgets/SymbolsWidget.h"
   45 #include "widgets/StringsWidget.h"
   46 #include "widgets/RelocsWidget.h"
   47 #include "widgets/FlagsWidget.h"
   48 #include "widgets/VisualNavbar.h"
   49 #include "widgets/Dashboard.h"
   50 #include "widgets/SdbWidget.h"
   51 #include "widgets/Omnibar.h"
   52 #include "widgets/ConsoleWidget.h"
   53 #include "widgets/EntrypointWidget.h"
   54 #include "widgets/ClassesWidget.h"
   55 #include "widgets/ResourcesWidget.h"
   56 #include "widgets/VTablesWidget.h"
   57 #include "widgets/HeadersWidget.h"
   58 #include "widgets/ZignaturesWidget.h"
   59 #include "widgets/DebugActions.h"
   60 #include "widgets/MemoryMapWidget.h"
   61 #include "widgets/BreakpointWidget.h"
   62 #include "widgets/RegisterRefsWidget.h"
   63 #include "widgets/DisassemblyWidget.h"
   64 #include "widgets/StackWidget.h"
   65 #include "widgets/ThreadsWidget.h"
   66 #include "widgets/ProcessesWidget.h"
   67 #include "widgets/RegistersWidget.h"
   68 #include "widgets/BacktraceWidget.h"
   69 #include "widgets/HexdumpWidget.h"
   70 #include "widgets/DecompilerWidget.h"
   71 #include "widgets/HexWidget.h"
   72 
   73 // Qt Headers
   74 #include <QApplication>
   75 #include <QComboBox>
   76 #include <QCompleter>
   77 #include <QDebug>
   78 #include <QDesktopServices>
   79 #include <QDir>
   80 #include <QDockWidget>
   81 #include <QFile>
   82 #include <QFileDialog>
   83 #include <QFont>
   84 #include <QFontDialog>
   85 #include <QLabel>
   86 #include <QLineEdit>
   87 #include <QList>
   88 #include <QMessageBox>
   89 #include <QProcess>
   90 #include <QPropertyAnimation>
   91 #include <QSysInfo>
   92 #include <QJsonObject>
   93 #include <QJsonArray>
   94 
   95 #include <QScrollBar>
   96 #include <QSettings>
   97 #include <QShortcut>
   98 #include <QStringListModel>
   99 #include <QStyledItemDelegate>
  100 #include <QStyleFactory>
  101 #include <QTextCursor>
  102 #include <QtGlobal>
  103 #include <QToolButton>
  104 #include <QToolTip>
  105 #include <QTreeWidgetItem>
  106 #include <QSvgRenderer>
  107 
  108 // Graphics
  109 #include <QGraphicsEllipseItem>
  110 #include <QGraphicsScene>
  111 #include <QGraphicsView>
  112 
  113 template<class T>
  114 T* getNewInstance(MainWindow *m, QAction *a) { return new T(m, a); }
  115 
  116 MainWindow::MainWindow(QWidget *parent) :
  117     QMainWindow(parent),
  118     core(Core()),
  119     ui(new Ui::MainWindow)
  120 {
  121     panelLock = false;
  122     tabsOnTop = false;
  123     configuration = Config();
  124 
  125     initUI();
  126 }
  127 
  128 MainWindow::~MainWindow()
  129 {
  130 }
  131 
  132 void MainWindow::initUI()
  133 {
  134     ui->setupUi(this);
  135 
  136     // Initialize context menu extensions for plugins
  137     disassemblyContextMenuExtensions = new QMenu(tr("Plugins"), this);
  138     addressableContextMenuExtensions = new QMenu(tr("Plugins"), this);
  139 
  140     connect(ui->actionExtraGraph, &QAction::triggered, this, &MainWindow::addExtraGraph);
  141     connect(ui->actionExtraDisassembly, &QAction::triggered, this, &MainWindow::addExtraDisassembly);
  142     connect(ui->actionExtraHexdump, &QAction::triggered, this, &MainWindow::addExtraHexdump);
  143     connect(ui->actionCommitChanges, &QAction::triggered, this, [this]() {
  144         Core()->commitWriteCache();
  145     });
  146     ui->actionCommitChanges->setEnabled(false);
  147     connect(Core(), &CutterCore::ioCacheChanged, ui->actionCommitChanges, &QAction::setEnabled);
  148 
  149     widgetTypeToConstructorMap.insert(GraphWidget::getWidgetType(), getNewInstance<GraphWidget>);
  150     widgetTypeToConstructorMap.insert(DisassemblyWidget::getWidgetType(), getNewInstance<DisassemblyWidget>);
  151     widgetTypeToConstructorMap.insert(HexdumpWidget::getWidgetType(), getNewInstance<HexdumpWidget>);
  152 
  153     initToolBar();
  154     initDocks();
  155 
  156     emptyState = saveState();
  157     /*
  158      *  Some global shortcuts
  159      */
  160 
  161     // Period goes to command entry
  162     QShortcut *cmd_shortcut = new QShortcut(QKeySequence(Qt::Key_Period), this);
  163     connect(cmd_shortcut, SIGNAL(activated()), consoleDock, SLOT(focusInputLineEdit()));
  164 
  165     // G and S goes to goto entry
  166     QShortcut *goto_shortcut = new QShortcut(QKeySequence(Qt::Key_G), this);
  167     connect(goto_shortcut, SIGNAL(activated()), this->omnibar, SLOT(setFocus()));
  168     QShortcut *seek_shortcut = new QShortcut(QKeySequence(Qt::Key_S), this);
  169     connect(seek_shortcut, SIGNAL(activated()), this->omnibar, SLOT(setFocus()));
  170     QShortcut *seek_to_func_end_shortcut = new QShortcut(QKeySequence(Qt::Key_Dollar), this);
  171     connect(seek_to_func_end_shortcut, SIGNAL(activated()), SLOT(seekToFunctionLastInstruction()));
  172     QShortcut *seek_to_func_start_shortcut = new QShortcut(QKeySequence(Qt::Key_AsciiCircum), this);
  173     connect(seek_to_func_start_shortcut, SIGNAL(activated()), SLOT(seekToFunctionStart()));
  174 
  175     QShortcut *refresh_shortcut = new QShortcut(QKeySequence(QKeySequence::Refresh), this);
  176     connect(refresh_shortcut, SIGNAL(activated()), this, SLOT(refreshAll()));
  177 
  178     connect(ui->actionZoomIn, SIGNAL(triggered()), this, SLOT(onZoomIn()));
  179     connect(ui->actionZoomOut, SIGNAL(triggered()), this, SLOT(onZoomOut()));
  180     connect(ui->actionZoomReset, SIGNAL(triggered()), this, SLOT(onZoomReset()));
  181 
  182     connect(core, SIGNAL(projectSaved(bool, const QString &)), this, SLOT(projectSaved(bool,
  183                                                                                        const QString &)));
  184 
  185     connect(core, &CutterCore::toggleDebugView, this, &MainWindow::toggleDebugView);
  186 
  187     connect(core, SIGNAL(newMessage(const QString &)),
  188             this->consoleDock, SLOT(addOutput(const QString &)));
  189     connect(core, SIGNAL(newDebugMessage(const QString &)),
  190             this->consoleDock, SLOT(addDebugOutput(const QString &)));
  191 
  192     connect(core, &CutterCore::showMemoryWidgetRequested,
  193             this, static_cast<void(MainWindow::*)()>(&MainWindow::showMemoryWidget));
  194 
  195     updateTasksIndicator();
  196     connect(core->getAsyncTaskManager(), &AsyncTaskManager::tasksChanged, this,
  197             &MainWindow::updateTasksIndicator);
  198 
  199     //Undo and redo seek
  200     ui->actionBackward->setShortcut(QKeySequence::Back);
  201     ui->actionForward->setShortcut(QKeySequence::Forward);
  202 
  203     initBackForwardMenu();
  204 
  205     /* Setup plugins interfaces */
  206     for (auto &plugin : Plugins()->getPlugins()) {
  207         plugin->setupInterface(this);
  208     }
  209 
  210 #if QT_VERSION < QT_VERSION_CHECK(5, 7, 0)
  211     ui->actionGrouped_dock_dragging->setVisible(false);
  212 #endif
  213 
  214     initLayout();
  215 }
  216 
  217 void MainWindow::initToolBar()
  218 {
  219     chooseThemeIcons();
  220 
  221     // Sepparator between undo/redo and goto lineEdit
  222     QWidget *spacer3 = new QWidget();
  223     spacer3->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
  224     spacer3->setStyleSheet("background-color: rgba(0,0,0,0)");
  225     spacer3->setMinimumSize(20, 20);
  226     spacer3->setMaximumWidth(100);
  227     ui->mainToolBar->addWidget(spacer3);
  228 
  229     DebugActions *debugActions = new DebugActions(ui->mainToolBar, this);
  230     // Debug menu
  231     auto debugViewAction = ui->menuDebug->addAction(tr("View"));
  232     debugViewAction->setMenu(ui->menuAddDebugWidgets);
  233     ui->menuDebug->addSeparator();
  234     ui->menuDebug->addAction(debugActions->actionStart);
  235     ui->menuDebug->addAction(debugActions->actionStartEmul);
  236     ui->menuDebug->addAction(debugActions->actionAttach);
  237     ui->menuDebug->addAction(debugActions->actionStartRemote);
  238     ui->menuDebug->addSeparator();
  239     ui->menuDebug->addAction(debugActions->actionStep);
  240     ui->menuDebug->addAction(debugActions->actionStepOver);
  241     ui->menuDebug->addAction(debugActions->actionStepOut);
  242     ui->menuDebug->addSeparator();
  243     ui->menuDebug->addAction(debugActions->actionContinue);
  244     ui->menuDebug->addAction(debugActions->actionContinueUntilCall);
  245     ui->menuDebug->addAction(debugActions->actionContinueUntilSyscall);
  246 
  247     // Sepparator between undo/redo and goto lineEdit
  248     QWidget *spacer4 = new QWidget();
  249     spacer4->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
  250     spacer4->setStyleSheet("background-color: rgba(0,0,0,0)");
  251     spacer4->setMinimumSize(10, 10);
  252     spacer4->setMaximumWidth(10);
  253     ui->mainToolBar->addWidget(spacer4);
  254 
  255     // Omnibar LineEdit
  256     this->omnibar = new Omnibar(this);
  257     ui->mainToolBar->addWidget(this->omnibar);
  258 
  259     // Add special separators to the toolbar that expand to separate groups of elements
  260     QWidget *spacer2 = new QWidget();
  261     spacer2->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
  262     spacer2->setStyleSheet("background-color: rgba(0,0,0,0)");
  263     spacer2->setMinimumSize(10, 10);
  264     spacer2->setMaximumWidth(300);
  265     ui->mainToolBar->addWidget(spacer2);
  266 
  267     // Separator between back/forward and undo/redo buttons
  268     QWidget *spacer = new QWidget();
  269     spacer->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Expanding);
  270     spacer->setStyleSheet("background-color: rgba(0,0,0,0)");
  271     spacer->setMinimumSize(20, 20);
  272     ui->mainToolBar->addWidget(spacer);
  273 
  274     tasksProgressIndicator = new ProgressIndicator();
  275     tasksProgressIndicator->setStyleSheet("background-color: rgba(0,0,0,0)");
  276     ui->mainToolBar->addWidget(tasksProgressIndicator);
  277 
  278     QWidget *spacerEnd = new QWidget();
  279     spacerEnd->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Expanding);
  280     spacerEnd->setStyleSheet("background-color: rgba(0,0,0,0)");
  281     spacerEnd->setMinimumSize(4, 0);
  282     spacerEnd->setMaximumWidth(4);
  283     ui->mainToolBar->addWidget(spacerEnd);
  284 
  285     // Visual navigation tool bar
  286     this->visualNavbar = new VisualNavbar(this);
  287     this->visualNavbar->setMovable(false);
  288     addToolBarBreak(Qt::TopToolBarArea);
  289     addToolBar(visualNavbar);
  290     QObject::connect(configuration, &Configuration::colorsUpdated, [this]() {
  291         this->visualNavbar->updateGraphicsScene();
  292     });
  293     QObject::connect(configuration, &Configuration::interfaceThemeChanged, this, &MainWindow::chooseThemeIcons);
  294 }
  295 
  296 void MainWindow::initDocks()
  297 {
  298     dockWidgets.reserve(20);
  299     decompilerDock = new DecompilerWidget(this, ui->actionDecompiler);
  300     consoleDock = new ConsoleWidget(this, ui->actionConsole);
  301 
  302     overviewDock = new OverviewWidget(this, ui->actionOverview);
  303     overviewDock->hide();
  304     connect(overviewDock, &OverviewWidget::isAvailableChanged, this, [this](bool isAvailable) {
  305         ui->actionOverview->setEnabled(isAvailable);
  306     });
  307     ui->actionOverview->setEnabled(overviewDock->getIsAvailable());
  308     connect(ui->actionOverview, &QAction::toggled, [this](bool checked) {
  309         if (checked) {
  310             overviewDock->show();
  311         } else {
  312             overviewDock->hide();
  313         }
  314     });
  315 
  316     ui->actionOverview->setChecked(overviewDock->getUserOpened());
  317     sectionsDock = new SectionsWidget(this, ui->actionSections);
  318     segmentsDock = new SegmentsWidget(this, ui->actionSegments);
  319     entrypointDock = new EntrypointWidget(this, ui->actionEntrypoints);
  320     functionsDock = new FunctionsWidget(this, ui->actionFunctions);
  321     importsDock = new ImportsWidget(this, ui->actionImports);
  322     exportsDock = new ExportsWidget(this, ui->actionExports);
  323     headersDock = new HeadersWidget(this, ui->actionHeaders);
  324     zignaturesDock = new ZignaturesWidget(this, ui->actionZignatures);
  325     typesDock = new TypesWidget(this, ui->actionTypes);
  326     searchDock = new SearchWidget(this, ui->actionSearch);
  327     symbolsDock = new SymbolsWidget(this, ui->actionSymbols);
  328     relocsDock = new RelocsWidget(this, ui->actionRelocs);
  329     commentsDock = new CommentsWidget(this, ui->actionComments);
  330     stringsDock = new StringsWidget(this, ui->actionStrings);
  331     flagsDock = new FlagsWidget(this, ui->actionFlags);
  332     stackDock = new StackWidget(this, ui->actionStack);
  333     threadsDock = new ThreadsWidget(this, ui->actionThreads);
  334     processesDock = new ProcessesWidget(this, ui->actionProcesses);
  335     backtraceDock = new BacktraceWidget(this, ui->actionBacktrace);
  336     registersDock = new RegistersWidget(this, ui->actionRegisters);
  337     memoryMapDock = new MemoryMapWidget(this, ui->actionMemoryMap);
  338     breakpointDock = new BreakpointWidget(this, ui->actionBreakpoint);
  339     registerRefsDock = new RegisterRefsWidget(this, ui->actionRegisterRefs);
  340     dashboardDock = new Dashboard(this, ui->actionDashboard);
  341     sdbDock = new SdbWidget(this, ui->actionSDBBrowser);
  342     classesDock = new ClassesWidget(this, ui->actionClasses);
  343     resourcesDock = new ResourcesWidget(this, ui->actionResources);
  344     vTablesDock = new VTablesWidget(this, ui->actionVTables);
  345 
  346     QSettings s;
  347     QStringList docks = s.value("docks", QStringList {
  348         DisassemblyWidget::getWidgetType(),
  349         GraphWidget::getWidgetType(),
  350         HexdumpWidget::getWidgetType()
  351     }).toStringList();
  352 
  353     // Restore all extra widgets
  354     QString className;
  355     for (const auto &it : docks) {
  356         if (std::none_of(dockWidgets.constBegin(), dockWidgets.constEnd(),
  357         [&it](QDockWidget * w) { return w->objectName() == it; })) {
  358             className = it.split(';').at(0);
  359             if (widgetTypeToConstructorMap.contains(className)) {
  360                 auto widget = widgetTypeToConstructorMap[className](this, nullptr);
  361                 widget->setObjectName(it);
  362                 addExtraWidget(widget);
  363             }
  364         }
  365     }
  366 }
  367 
  368 void MainWindow::initLayout()
  369 {
  370     // Set up dock widgets default layout
  371     enableDebugWidgetsMenu(false);
  372     // Restore saved settings
  373     readSettingsOrDefault();
  374 
  375     initCorners();
  376 }
  377 
  378 void MainWindow::toggleOverview(bool visibility, GraphWidget *targetGraph)
  379 {
  380     if (!overviewDock) {
  381         return;
  382     }
  383     if (visibility) {
  384         overviewDock->setTargetGraphWidget(targetGraph);
  385     }
  386 }
  387 
  388 void MainWindow::updateTasksIndicator()
  389 {
  390     bool running = core->getAsyncTaskManager()->getTasksRunning();
  391     tasksProgressIndicator->setProgressIndicatorVisible(running);
  392 }
  393 
  394 void MainWindow::addExtraGraph()
  395 {
  396     auto *extraDock = new GraphWidget(this, nullptr);
  397     addExtraWidget(extraDock);
  398 }
  399 
  400 void MainWindow::addExtraHexdump()
  401 {
  402     auto *extraDock = new HexdumpWidget(this, nullptr);
  403     addExtraWidget(extraDock);
  404 }
  405 
  406 void MainWindow::addExtraDisassembly()
  407 {
  408     auto *extraDock = new DisassemblyWidget(this, nullptr);
  409     addExtraWidget(extraDock);
  410 }
  411 
  412 void MainWindow::addExtraWidget(CutterDockWidget *extraDock)
  413 {
  414     extraDock->setTransient(true);
  415     addDockWidget(Qt::TopDockWidgetArea, extraDock, Qt::Orientation::Horizontal);
  416     auto restoreExtraDock = qhelpers::forceWidth(extraDock->widget(), 600);
  417     qApp->processEvents();
  418     restoreExtraDock.restoreWidth(extraDock->widget());
  419 }
  420 
  421 QMenu *MainWindow::getMenuByType(MenuType type)
  422 {
  423     switch (type) {
  424     case MenuType::File:
  425         return ui->menuFile;
  426     case MenuType::Edit:
  427         return ui->menuEdit;
  428     case MenuType::View:
  429         return ui->menuView;
  430     case MenuType::Windows:
  431         return ui->menuWindows;
  432     case MenuType::Debug:
  433         return ui->menuDebug;
  434     case MenuType::Help:
  435         return ui->menuHelp;
  436     case MenuType::Plugins:
  437         return ui->menuPlugins;
  438     default:
  439         return nullptr;
  440     }
  441 }
  442 
  443 void MainWindow::addPluginDockWidget(QDockWidget *dockWidget, QAction *action)
  444 {
  445     addDockWidget(Qt::TopDockWidgetArea, dockWidget);
  446     dockWidget->addAction(action);
  447     addWidget(dockWidget);
  448     ui->menuPlugins->addAction(action);
  449     addDockWidget(Qt::DockWidgetArea::TopDockWidgetArea, dockWidget);
  450     updateDockActionChecked(action);
  451 }
  452 
  453 void MainWindow::addMenuFileAction(QAction *action)
  454 {
  455     ui->menuFile->addAction(action);
  456 }
  457 
  458 void MainWindow::openNewFile(InitialOptions &options, bool skipOptionsDialog)
  459 {
  460     setFilename(options.filename);
  461 
  462     /* Prompt to load filename.r2 script */
  463     if (options.script.isEmpty()) {
  464         QString script = QString("%1.r2").arg(this->filename);
  465         if (r_file_exists(script.toStdString().data())) {
  466             QMessageBox mb;
  467             mb.setWindowTitle(tr("Script loading"));
  468             mb.setText(tr("Do you want to load the '%1' script?").arg(script));
  469             mb.setStandardButtons(QMessageBox::Yes | QMessageBox::No);
  470             if (mb.exec() == QMessageBox::Yes) {
  471                 options.script = script;
  472             }
  473         }
  474     }
  475 
  476     /* Show analysis options dialog */
  477     displayInitialOptionsDialog(options, skipOptionsDialog);
  478 }
  479 
  480 void MainWindow::openNewFileFailed()
  481 {
  482     displayNewFileDialog();
  483     QMessageBox mb(this);
  484     mb.setIcon(QMessageBox::Critical);
  485     mb.setStandardButtons(QMessageBox::Ok);
  486     mb.setWindowTitle(tr("Cannot open file!"));
  487     mb.setText(
  488         tr("Could not open the file! Make sure the file exists and that you have the correct permissions."));
  489     mb.exec();
  490 }
  491 
  492 /**
  493  * @brief displays the WelocmeDialog
  494  *
  495  * Upon first execution of Cutter, the WelcomeDialog would be showed to the user.
  496  * The Welcome dialog would be showed after a reset of Cutter's preferences by the user.
  497  */
  498 
  499 void MainWindow::displayWelcomeDialog()
  500 {
  501     WelcomeDialog w;
  502     w.exec();
  503 }
  504 
  505 void MainWindow::displayNewFileDialog()
  506 {
  507     NewFileDialog *n = new NewFileDialog(this);
  508     newFileDialog = n;
  509     n->setAttribute(Qt::WA_DeleteOnClose);
  510     n->show();
  511 }
  512 
  513 void MainWindow::closeNewFileDialog()
  514 {
  515     if (newFileDialog) {
  516         newFileDialog->close();
  517     }
  518     newFileDialog = nullptr;
  519 }
  520 
  521 void MainWindow::displayInitialOptionsDialog(const InitialOptions &options, bool skipOptionsDialog)
  522 {
  523     auto o = new InitialOptionsDialog(this);
  524     o->setAttribute(Qt::WA_DeleteOnClose);
  525     o->loadOptions(options);
  526 
  527     if (skipOptionsDialog) {
  528         o->setupAndStartAnalysis();
  529     } else {
  530         o->show();
  531     }
  532 }
  533 
  534 void MainWindow::openProject(const QString &project_name)
  535 {
  536     QString filename = core->cmdRaw("Pi " + project_name);
  537     setFilename(filename.trimmed());
  538 
  539     core->openProject(project_name);
  540 
  541     finalizeOpen();
  542 }
  543 
  544 void MainWindow::finalizeOpen()
  545 {
  546     core->getOpcodes();
  547     core->updateSeek();
  548     refreshAll();
  549 
  550     // Add fortune message
  551     core->message("\n" + core->cmdRaw("fo"));
  552     showMaximized();
  553     Config()->adjustColorThemeDarkness();
  554 
  555     QSettings s;
  556     QStringList unsync = s.value("unsync").toStringList();
  557     for (auto it : dockWidgets) {
  558         auto w = qobject_cast<MemoryDockWidget*>(it);
  559         if (w) {
  560             w->getSeekable()->setSynchronization(!unsync.contains(it->objectName()));
  561         }
  562     }
  563 
  564     // Set focus to disasm or graph widget
  565 
  566     // Use for loop to cover cases when main disasm/graph
  567     // (MainWindow::disassemblyDock and MainWindow::graphDock)
  568     // widgets are invisible but extra ones are visible
  569 
  570     // Graph with function in it has focus priority over DisasmWidget
  571     // if there are both graph and disasm.
  572     // Otherwise Disasm has focus priority over Graph
  573 
  574     // If there are no graph/disasm widgets focus on MainWindow
  575 
  576     setFocus();
  577     bool graphContainsFunc = false;
  578     for (auto dockWidget : dockWidgets) {
  579         const QString className = dockWidget->metaObject()->className();
  580         auto graphWidget = qobject_cast<GraphWidget*>(dockWidget);
  581         if (graphWidget && !dockWidget->visibleRegion().isNull()) {
  582             graphContainsFunc = !graphWidget->getGraphView()->getBlocks().empty();
  583             if (graphContainsFunc) {
  584                 dockWidget->widget()->setFocus();
  585                 break;
  586             }
  587         }
  588         auto disasmWidget = qobject_cast<DisassemblyWidget*>(dockWidget);
  589         if (disasmWidget && !dockWidget->visibleRegion().isNull()) {
  590             if (!graphContainsFunc) {
  591                 disasmWidget->setFocus();
  592             } else {
  593                 break;
  594             }
  595         }
  596     }
  597 }
  598 
  599 bool MainWindow::saveProject(bool quit)
  600 {
  601     QString projectName = core->getConfig("prj.name");
  602     if (projectName.isEmpty()) {
  603         return saveProjectAs(quit);
  604     } else {
  605         core->saveProject(projectName);
  606         return true;
  607     }
  608 }
  609 
  610 bool MainWindow::saveProjectAs(bool quit)
  611 {
  612     SaveProjectDialog dialog(quit, this);
  613     return SaveProjectDialog::Rejected != dialog.exec();
  614 }
  615 
  616 void MainWindow::refreshOmniBar(const QStringList &flags)
  617 {
  618     omnibar->refresh(flags);
  619 }
  620 
  621 void MainWindow::setFilename(const QString &fn)
  622 {
  623     // Add file name to window title
  624     this->filename = fn;
  625     this->setWindowTitle(APPNAME" – " + fn);
  626 }
  627 
  628 void MainWindow::closeEvent(QCloseEvent *event)
  629 {
  630 
  631     // Check if there are uncommitted changes
  632     if (core->isIOCacheEnabled() && !core->cmdj("wcj").array().isEmpty()) {
  633 
  634         QMessageBox::StandardButton ret = QMessageBox::question(this, APPNAME,
  635                                                                 tr("It seems that you have changes or patches that are not committed to the file.\n"
  636                                                                 "Do you want to commit them now?"),
  637                                                                 (QMessageBox::StandardButtons)(QMessageBox::Save | QMessageBox::Discard | QMessageBox::Cancel));
  638         if (ret == QMessageBox::Cancel) {
  639             event->ignore();
  640             return;
  641         }
  642 
  643         if (ret == QMessageBox::Save) {
  644             core->commitWriteCache();
  645         } 
  646     }
  647 
  648     QMessageBox::StandardButton ret = QMessageBox::question(this, APPNAME,
  649                                                             tr("Do you really want to exit?\nSave your project before closing!"),
  650                                                             (QMessageBox::StandardButtons)(QMessageBox::Save | QMessageBox::Discard | QMessageBox::Cancel));
  651     if (ret == QMessageBox::Cancel) {
  652         event->ignore();
  653         return;
  654     }
  655 
  656     if (ret == QMessageBox::Save && !saveProject(true)) {
  657         event->ignore();
  658         return;
  659     }
  660 
  661     if (!core->currentlyDebugging) {
  662         saveSettings();
  663     } else {
  664         core->stopDebug();
  665     }
  666     QMainWindow::closeEvent(event);
  667 }
  668 
  669 void MainWindow::paintEvent(QPaintEvent *event)
  670 {
  671     QMainWindow::paintEvent(event);
  672     /*
  673      * Dirty hack
  674      * Just to adjust the width of Functions Widget to fixed size
  675      * After everything is drawn, safely make it Preferred size policy
  676      * So that user can change the widget size with the mouse
  677      */
  678     if (functionsDock) {
  679         functionsDock->changeSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
  680     }
  681 }
  682 
  683 void MainWindow::readSettingsOrDefault()
  684 {
  685     QSettings settings;
  686     QByteArray geo = settings.value("geometry", QByteArray()).toByteArray();
  687     QByteArray state = settings.value("state", QByteArray()).toByteArray();
  688     /*
  689      * Check if saved settings exist
  690      * If not, then read the default layout
  691      */
  692     if (!geo.length() || !state.length()) {
  693         resetToDefaultLayout();
  694         return;
  695     }
  696     hideAllDocks();
  697     restoreGeometry(geo);
  698     restoreState(state);
  699 
  700     // make sure all DockWidgets are part of the MainWindow
  701     // also show them, so newly installed plugin widgets are shown right away
  702     for (auto dockWidget : dockWidgets) {
  703         if (dockWidgetArea(dockWidget) == Qt::DockWidgetArea::NoDockWidgetArea &&
  704             !isDebugWidget(dockWidget)) {
  705             addDockWidget(Qt::DockWidgetArea::TopDockWidgetArea, dockWidget);
  706             dockWidget->show();
  707         }
  708     }
  709 
  710     responsive = settings.value("responsive").toBool();
  711     panelLock = settings.value("panelLock").toBool();
  712     setPanelLock();
  713     tabsOnTop = settings.value("tabsOnTop").toBool();
  714     setTabLocation();
  715     bool dockGroupedDragging = settings.value("docksGroupedDragging", false).toBool();
  716     ui->actionGrouped_dock_dragging->setChecked(dockGroupedDragging);
  717     on_actionGrouped_dock_dragging_triggered(dockGroupedDragging);
  718 
  719     QPoint pos = settings.value("pos", QPoint(200, 200)).toPoint();
  720     QSize size = settings.value("size", QSize(400, 400)).toSize();
  721     resize(size);
  722     move(pos);
  723     updateDockActionsChecked();
  724 }
  725 
  726 void MainWindow::saveSettings()
  727 {
  728     QSettings settings;
  729 
  730     QStringList docks;
  731     QStringList unsync;
  732     for (const auto &it : dockWidgets) {
  733         docks.append(it->objectName());
  734         auto memoryDockWidget = qobject_cast<MemoryDockWidget*>(it);
  735         if (memoryDockWidget && !memoryDockWidget->getSeekable()->isSynchronized()) {
  736             unsync.append(it->objectName());
  737         }
  738     }
  739     settings.setValue("docks", docks);
  740     settings.setValue("unsync", unsync);
  741     settings.setValue("geometry", saveGeometry());
  742     settings.setValue("size", size());
  743     settings.setValue("pos", pos());
  744     settings.setValue("state", saveState());
  745     settings.setValue("panelLock", panelLock);
  746     settings.setValue("tabsOnTop", tabsOnTop);
  747     settings.setValue("docksGroupedDragging", ui->actionGrouped_dock_dragging->isChecked());
  748 }
  749 
  750 void MainWindow::readDebugSettings()
  751 {
  752     QSettings settings;
  753     QByteArray geo = settings.value("debug.geometry", QByteArray()).toByteArray();
  754     restoreGeometry(geo);
  755     QByteArray state = settings.value("debug.state", QByteArray()).toByteArray();
  756     restoreState(state);
  757     QPoint pos = settings.value("pos", QPoint(200, 200)).toPoint();
  758     QSize size = settings.value("size", QSize(400, 400)).toSize();
  759     resize(size);
  760     move(pos);
  761     updateDockActionsChecked();
  762 }
  763 
  764 void MainWindow::saveDebugSettings()
  765 {
  766     QSettings settings;
  767     settings.setValue("debug.geometry", saveGeometry());
  768     settings.setValue("debug.state", saveState());
  769     settings.setValue("size", size());
  770     settings.setValue("pos", pos());
  771 }
  772 
  773 void MainWindow::setPanelLock()
  774 {
  775     if (panelLock) {
  776         for (QDockWidget *dockWidget : findChildren<QDockWidget *>()) {
  777             dockWidget->setFeatures(QDockWidget::NoDockWidgetFeatures);
  778         }
  779 
  780         ui->actionLock->setChecked(false);
  781     } else {
  782         for (QDockWidget *dockWidget : findChildren<QDockWidget *>()) {
  783             dockWidget->setFeatures(QDockWidget::AllDockWidgetFeatures);
  784         }
  785 
  786         ui->actionLock->setChecked(true);
  787     }
  788 }
  789 
  790 void MainWindow::setTabLocation()
  791 {
  792     if (tabsOnTop) {
  793         this->setTabPosition(Qt::AllDockWidgetAreas, QTabWidget::North);
  794         ui->actionTabs_on_Top->setChecked(true);
  795     } else {
  796         this->setTabPosition(Qt::AllDockWidgetAreas, QTabWidget::South);
  797         ui->actionTabs_on_Top->setChecked(false);
  798     }
  799 }
  800 
  801 void MainWindow::refreshAll()
  802 {
  803     core->triggerRefreshAll();
  804 }
  805 
  806 void MainWindow::lockUnlock_Docks(bool what)
  807 {
  808     if (what) {
  809         for (QDockWidget *dockWidget : findChildren<QDockWidget *>()) {
  810             dockWidget->setFeatures(QDockWidget::NoDockWidgetFeatures);
  811         }
  812     } else {
  813         for (QDockWidget *dockWidget : findChildren<QDockWidget *>()) {
  814             dockWidget->setFeatures(QDockWidget::AllDockWidgetFeatures);
  815         }
  816     }
  817 
  818 }
  819 
  820 void MainWindow::restoreDocks()
  821 {
  822     // In the upper half the functions are the first widget
  823     addDockWidget(Qt::TopDockWidgetArea, functionsDock);
  824     addDockWidget(Qt::TopDockWidgetArea, overviewDock);
  825 
  826     // Function | Dashboard
  827     splitDockWidget(functionsDock, dashboardDock, Qt::Horizontal);
  828     tabifyDockWidget(dashboardDock, decompilerDock);
  829     tabifyDockWidget(dashboardDock, entrypointDock);
  830     tabifyDockWidget(dashboardDock, flagsDock);
  831     tabifyDockWidget(dashboardDock, stringsDock);
  832     tabifyDockWidget(dashboardDock, relocsDock);
  833     tabifyDockWidget(dashboardDock, importsDock);
  834     tabifyDockWidget(dashboardDock, exportsDock);
  835     tabifyDockWidget(dashboardDock, typesDock);
  836     tabifyDockWidget(dashboardDock, searchDock);
  837     tabifyDockWidget(dashboardDock, headersDock);
  838     tabifyDockWidget(dashboardDock, zignaturesDock);
  839     tabifyDockWidget(dashboardDock, symbolsDock);
  840     tabifyDockWidget(dashboardDock, classesDock);
  841     tabifyDockWidget(dashboardDock, resourcesDock);
  842     tabifyDockWidget(dashboardDock, vTablesDock);
  843     tabifyDockWidget(dashboardDock, sdbDock);
  844     tabifyDockWidget(dashboardDock, memoryMapDock);
  845     tabifyDockWidget(dashboardDock, breakpointDock);
  846     tabifyDockWidget(dashboardDock, registerRefsDock);
  847     for (const auto &it : dockWidgets) {
  848         // Check whether or not current widgets is graph, hexdump or disasm
  849         if (qobject_cast<GraphWidget*>(it) ||
  850             qobject_cast<HexdumpWidget*>(it) ||
  851             qobject_cast<DisassemblyWidget*>(it)) {
  852             tabifyDockWidget(dashboardDock, it);
  853         }
  854     }
  855 
  856     splitDockWidget(functionsDock, overviewDock, Qt::Vertical);
  857 
  858     // In the lower half the console is the first widget
  859     addDockWidget(Qt::BottomDockWidgetArea, consoleDock);
  860 
  861     // Console | Sections
  862     splitDockWidget(consoleDock, sectionsDock, Qt::Horizontal);
  863     splitDockWidget(consoleDock, segmentsDock, Qt::Horizontal);
  864 
  865     tabifyDockWidget(sectionsDock, commentsDock);
  866 
  867     // Add Stack, Registers, Threads and Backtrace vertically stacked
  868     addDockWidget(Qt::TopDockWidgetArea, stackDock);
  869     splitDockWidget(stackDock, registersDock, Qt::Vertical);
  870     tabifyDockWidget(stackDock, backtraceDock);
  871     tabifyDockWidget(backtraceDock, threadsDock);
  872     tabifyDockWidget(threadsDock, processesDock);
  873 
  874     updateDockActionsChecked();
  875 }
  876 
  877 
  878 void MainWindow::hideAllDocks()
  879 {
  880     for (auto w : dockWidgets) {
  881         removeDockWidget(w);
  882     }
  883 }
  884 
  885 void MainWindow::updateDockActionsChecked()
  886 {
  887     for (auto i = dockWidgetsOfAction.constBegin(); i != dockWidgetsOfAction.constEnd(); i++) {
  888         updateDockActionChecked(i.key());
  889     }
  890 }
  891 
  892 bool MainWindow::isDebugWidget(QDockWidget *dock) const
  893 {
  894     return dock == stackDock ||
  895            dock == registersDock ||
  896            dock == backtraceDock ||
  897            dock == threadsDock ||
  898            dock == memoryMapDock ||
  899            dock == breakpointDock ||
  900            dock == processesDock ||
  901            dock == registerRefsDock;
  902 }
  903 
  904 MemoryWidgetType MainWindow::getMemoryWidgetTypeToRestore()
  905 {
  906     if (lastSyncMemoryWidget) {
  907         return lastSyncMemoryWidget->getType();
  908     }
  909     return MemoryWidgetType::Disassembly;
  910 }
  911 
  912 QString MainWindow::getUniqueObjectName(const QString &widgetType) const
  913 {
  914     QStringList docks;
  915     docks.reserve(dockWidgets.size());
  916     QString name;
  917     for (const auto &it : dockWidgets) {
  918         name = it->objectName();
  919         if (name.split(';').at(0) == widgetType) {
  920             docks.push_back(name);
  921         }
  922     }
  923 
  924     if (docks.isEmpty()) {
  925         return widgetType;
  926     }
  927 
  928     int id = 0;
  929     while (docks.contains(widgetType + ";" + QString::number(id))) {
  930         id++;
  931     }
  932 
  933     return widgetType + ";"  + QString::number(id);
  934 }
  935 
  936 void MainWindow::showMemoryWidget()
  937 {
  938     if (lastSyncMemoryWidget) {
  939         if (lastSyncMemoryWidget->tryRaiseMemoryWidget()) {
  940             return;
  941         }
  942     }
  943     showMemoryWidget(MemoryWidgetType::Disassembly);
  944 }
  945 
  946 void MainWindow::showMemoryWidget(MemoryWidgetType type)
  947 {
  948     for (auto &dock : dockWidgets) {
  949         if (auto memoryWidget = qobject_cast<MemoryDockWidget *>(dock)) {
  950             if (memoryWidget->getType() == type && memoryWidget->getSeekable()->isSynchronized()) {
  951                 memoryWidget->tryRaiseMemoryWidget();
  952                 return;
  953             }
  954         }
  955     }
  956     auto memoryDockWidget = addNewMemoryWidget(type, Core()->getOffset());
  957     memoryDockWidget->raiseMemoryWidget();
  958 }
  959 
  960 QMenu *MainWindow::createShowInMenu(QWidget *parent, RVA address)
  961 {
  962     QMenu *menu = new QMenu(parent);
  963     for (auto &dock : dockWidgets) {
  964         if (auto memoryWidget = qobject_cast<MemoryDockWidget *>(dock)) {
  965             QAction *action = new QAction(memoryWidget->windowTitle(), menu);
  966             connect(action, &QAction::triggered, this, [this, memoryWidget, address](){
  967                 memoryWidget->getSeekable()->seek(address);
  968                 memoryWidget->raiseMemoryWidget();
  969             });
  970             menu->addAction(action);
  971         }
  972     }
  973     menu->addSeparator();
  974     auto createAddNewWidgetAction = [this, menu, address](QString label, MemoryWidgetType type) {
  975         QAction *action = new QAction(label, menu);
  976         connect(action, &QAction::triggered, this, [this, address, type](){
  977             addNewMemoryWidget(type, address, false);
  978         });
  979         menu->addAction(action);
  980     };
  981     createAddNewWidgetAction(tr("New disassembly"), MemoryWidgetType::Disassembly);
  982     createAddNewWidgetAction(tr("New graph"), MemoryWidgetType::Graph);
  983     createAddNewWidgetAction(tr("New hexdump"), MemoryWidgetType::Hexdump);
  984 
  985     return menu;
  986 }
  987 
  988 void MainWindow::setCurrentMemoryWidget(MemoryDockWidget *memoryWidget)
  989 {
  990     if (memoryWidget->getSeekable()->isSynchronized()) {
  991         lastSyncMemoryWidget = memoryWidget;
  992     }
  993     lastMemoryWidget = memoryWidget;
  994 }
  995 
  996 MemoryDockWidget *MainWindow::getLastMemoryWidget()
  997 {
  998     return lastMemoryWidget;
  999 }
 1000 
 1001 MemoryDockWidget *MainWindow::addNewMemoryWidget(MemoryWidgetType type, RVA address,
 1002                                                  bool synchronized)
 1003 {
 1004     MemoryDockWidget *memoryWidget = nullptr;
 1005     switch (type) {
 1006     case MemoryWidgetType::Graph:
 1007         memoryWidget = new GraphWidget(this);
 1008         break;
 1009     case MemoryWidgetType::Hexdump:
 1010         memoryWidget = new HexdumpWidget(this);
 1011         break;
 1012     case MemoryWidgetType::Disassembly:
 1013         memoryWidget = new DisassemblyWidget(this);
 1014         break;
 1015     case MemoryWidgetType::Decompiler:
 1016         memoryWidget = new DecompilerWidget(this);
 1017         break;
 1018     }
 1019     auto seekable = memoryWidget->getSeekable();
 1020     seekable->setSynchronization(synchronized);
 1021     seekable->seek(address);
 1022     addExtraWidget(memoryWidget);
 1023     return memoryWidget;
 1024 }
 1025 
 1026 void MainWindow::initCorners()
 1027 {
 1028     // TODO: Allow the user to select this option visually in the GUI settings
 1029     // Adjust the DockWidget areas
 1030 
 1031     setCorner(Qt::TopLeftCorner, Qt::LeftDockWidgetArea);
 1032     setCorner(Qt::BottomLeftCorner, Qt::LeftDockWidgetArea);
 1033 
 1034     setCorner(Qt::BottomRightCorner, Qt::BottomDockWidgetArea);
 1035     setCorner(Qt::TopRightCorner, Qt::RightDockWidgetArea);
 1036 }
 1037 
 1038 void MainWindow::initBackForwardMenu()
 1039 {
 1040     auto prepareButtonMenu = [this](QAction *action) -> QMenu* {
 1041         QToolButton *button = qobject_cast<QToolButton *>(ui->mainToolBar->widgetForAction(action));
 1042         if (!button) {
 1043             return nullptr;
 1044         }
 1045         QMenu *menu = new QMenu(button);
 1046         button->setMenu(menu);
 1047         button->setPopupMode(QToolButton::DelayedPopup);
 1048         button->setContextMenuPolicy(Qt::CustomContextMenu);
 1049         connect(button, &QWidget::customContextMenuRequested, button,
 1050                 [menu, button] (const QPoint &pos) {
 1051             menu->exec(button->mapToGlobal(pos));
 1052         });
 1053 
 1054         QFontMetrics metrics(fontMetrics());
 1055         // Roughly 10-16 lines depending on padding size, no need to calculate more precisely
 1056         menu->setMaximumHeight(metrics.lineSpacing() * 20);
 1057 
 1058         menu->setToolTipsVisible(true);
 1059         return menu;
 1060     };
 1061 
 1062     if (auto menu = prepareButtonMenu(ui->actionBackward)) {
 1063         menu->setObjectName("historyMenu");
 1064         connect(menu, &QMenu::aboutToShow, menu, [this, menu]() {
 1065             updateHistoryMenu(menu, false);
 1066         });
 1067     }
 1068     if (auto menu = prepareButtonMenu(ui->actionForward)) {
 1069         menu->setObjectName("forwardHistoryMenu");
 1070         connect(menu, &QMenu::aboutToShow, menu, [this, menu]() {
 1071             updateHistoryMenu(menu, true);
 1072         });
 1073     }
 1074 }
 1075 
 1076 void MainWindow::updateHistoryMenu(QMenu *menu, bool redo)
 1077 {
 1078     // Not too long so that whole screen doesn't get covered,
 1079     // not too short so that reasonable length c++ names can be seen most of the time
 1080     const int MAX_NAME_LENGTH = 64;
 1081 
 1082     auto hist = Core()->cmdj("sj");
 1083     bool history = true;
 1084     QList<QAction *> actions;
 1085     for (auto item : Core()->cmdj("sj").array()) {
 1086         QJsonObject obj = item.toObject();
 1087         QString name = obj["name"].toString();
 1088         RVA offset = obj["offset"].toVariant().toULongLong();
 1089         bool current = obj["current"].toBool(false);
 1090         if (current) {
 1091             history = false;
 1092         }
 1093         if (history != redo || current) { // Include current in both directions
 1094             QString addressString = RAddressString(offset);
 1095 
 1096             QString toolTip = QString("%1 %2").arg(addressString, name); // show non truncated name in tooltip
 1097 
 1098             name.truncate(MAX_NAME_LENGTH); // TODO:#1904 use common name shortening function
 1099             QString label = QString("%1 (%2)").arg(name, addressString);
 1100             if (current) {
 1101                 label = QString("current position (%1)").arg(addressString);
 1102             }
 1103             QAction *action = new QAction(label, menu);
 1104             action->setToolTip(toolTip);
 1105             actions.push_back(action);
 1106             if (current) {
 1107                 QFont font;
 1108                 font.setBold(true);
 1109                 action->setFont(font);
 1110             }
 1111         }
 1112     }
 1113     if (!redo) {
 1114         std::reverse(actions.begin(), actions.end());
 1115     }
 1116     menu->clear();
 1117     menu->addActions(actions);
 1118     int steps = 0;
 1119     for (QAction *item : menu->actions()) {
 1120         if (redo) {
 1121             connect(item, &QAction::triggered, item, [steps]() {
 1122                 for (int i = 0; i < steps; i++) {
 1123                     Core()->seekNext();
 1124                 }
 1125             });
 1126         } else {
 1127             connect(item, &QAction::triggered, item, [steps]() {
 1128                 for (int i = 0; i < steps; i++) {
 1129                     Core()->seekPrev();
 1130                 }
 1131             });
 1132         }
 1133         ++steps;
 1134     }
 1135 
 1136 }
 1137 
 1138 void MainWindow::addWidget(QDockWidget* widget)
 1139 {
 1140     dockWidgets.push_back(widget);
 1141     for (auto action : widget->actions()) {
 1142         dockWidgetsOfAction.insert(action, widget);
 1143         connect(qobject_cast<CutterDockWidget*>(widget), &CutterDockWidget::closed,
 1144                 this, [this]() {
 1145             QDockWidget *widget = qobject_cast<QDockWidget*>(sender());
 1146             dockWidgets.removeOne(widget);
 1147             for (auto action : widget->actions()) {
 1148                 dockWidgetsOfAction.remove(action, widget);
 1149             }
 1150             updateDockActionsChecked();
 1151         });
 1152     }    
 1153 }
 1154 
 1155 void MainWindow::addMemoryDockWidget(MemoryDockWidget *widget)
 1156 {
 1157     connect(widget, &QDockWidget::visibilityChanged, this, [this, widget](bool visibility) {
 1158         if (visibility) {
 1159             setCurrentMemoryWidget(widget);
 1160         }
 1161     });
 1162 }
 1163 
 1164 void MainWindow::removeWidget(QDockWidget *widget)
 1165 {
 1166     dockWidgets.removeAll(widget);
 1167     if (lastSyncMemoryWidget == widget) {
 1168         lastSyncMemoryWidget = nullptr;
 1169     }
 1170     if (lastMemoryWidget == widget) {
 1171         lastMemoryWidget = nullptr;
 1172     }
 1173 }
 1174 
 1175 void MainWindow::updateDockActionChecked(QAction *action)
 1176 {
 1177     auto actions = dockWidgetsOfAction.values(action);
 1178     action->setChecked(!std::accumulate(actions.begin(), actions.end(), false,
 1179                                        [](bool a, QDockWidget* w) -> bool {
 1180         return a || w->isHidden();
 1181     }));
 1182 }
 1183 
 1184 void MainWindow::showZenDocks()
 1185 {
 1186     const QList<QDockWidget *> zenDocks = { functionsDock,
 1187                                             dashboardDock,
 1188                                             stringsDock,
 1189                                             searchDock,
 1190                                             importsDock
 1191                                           };
 1192     int width = functionsDock->maximumWidth();
 1193     functionsDock->setMaximumWidth(200);
 1194     for (auto w : dockWidgets) {
 1195         if (zenDocks.contains(w) ||
 1196             qobject_cast<GraphWidget*>(w) ||
 1197             qobject_cast<HexdumpWidget*>(w) ||
 1198             qobject_cast<DisassemblyWidget*>(w)) {
 1199             w->show();
 1200         }
 1201     }
 1202     functionsDock->setMaximumWidth(width);
 1203     updateDockActionsChecked();
 1204 }
 1205 
 1206 void MainWindow::showDebugDocks()
 1207 {
 1208     const QList<QDockWidget *> debugDocks = { functionsDock,
 1209                                               stringsDock,
 1210                                               searchDock,
 1211                                               stackDock,
 1212                                               registersDock,
 1213                                               backtraceDock,
 1214                                               threadsDock,
 1215                                               memoryMapDock,
 1216                                               breakpointDock
 1217                                             };
 1218     int width = functionsDock->maximumWidth();
 1219     functionsDock->setMaximumWidth(200);
 1220     for (auto w : dockWidgets) {
 1221         if (debugDocks.contains(w) ||
 1222             qobject_cast<GraphWidget*>(w) ||
 1223             qobject_cast<HexdumpWidget*>(w) ||
 1224             qobject_cast<DisassemblyWidget*>(w)) {
 1225             w->show();
 1226         }
 1227     }
 1228     functionsDock->setMaximumWidth(width);
 1229     updateDockActionsChecked();
 1230 }
 1231 
 1232 void MainWindow::enableDebugWidgetsMenu(bool enable)
 1233 {
 1234     for (QAction *action : ui->menuAddDebugWidgets->actions()) {
 1235         // The breakpoints menu should be available outside of debug
 1236         if (!action->text().contains("Breakpoints")) {
 1237             action->setEnabled(enable);
 1238         }
 1239     }
 1240 }
 1241 
 1242 void MainWindow::resetToDefaultLayout()
 1243 {
 1244     hideAllDocks();
 1245     restoreDocks();
 1246     showZenDocks();
 1247     dashboardDock->raise();
 1248 }
 1249 
 1250 void MainWindow::resetToDebugLayout()
 1251 {
 1252     MemoryWidgetType memType = getMemoryWidgetTypeToRestore();
 1253     hideAllDocks();
 1254     restoreDocks();
 1255     showDebugDocks();
 1256     showMemoryWidget(memType);
 1257 
 1258     auto restoreStackDock = qhelpers::forceWidth(stackDock->widget(), 400);
 1259     qApp->processEvents();
 1260     restoreStackDock.restoreWidth(stackDock->widget());
 1261 }
 1262 
 1263 void MainWindow::restoreDebugLayout()
 1264 {
 1265     MemoryWidgetType memType = getMemoryWidgetTypeToRestore();
 1266     bool isMaxim = isMaximized();
 1267     hideAllDocks();
 1268     restoreDocks();
 1269     showDebugDocks();
 1270     readDebugSettings();
 1271     showMemoryWidget(memType);
 1272     if (isMaxim) {
 1273         showMaximized();
 1274     } else {
 1275         showNormal();
 1276     }
 1277 }
 1278 
 1279 void MainWindow::resetDockWidgetList()
 1280 {
 1281     QStringList isLeft;
 1282     QList<QWidget*> toClose;
 1283     for (auto it : dockWidgets) {
 1284         if (isLeft.contains(it->metaObject()->className())) {
 1285             toClose.append(it);
 1286         } else if (QRegularExpression("\\A(?:\\w+ \\d+)\\z").match(it->objectName()).hasMatch()) {
 1287             isLeft.append(it->metaObject()->className());
 1288         }
 1289     }
 1290     for (auto it : toClose) {
 1291         it->close();
 1292     }
 1293 }
 1294 
 1295 void MainWindow::on_actionLock_triggered()
 1296 {
 1297     panelLock = !panelLock;
 1298     setPanelLock();
 1299 }
 1300 
 1301 void MainWindow::on_actionLockUnlock_triggered()
 1302 {
 1303     if (ui->actionLockUnlock->isChecked()) {
 1304         for (QDockWidget *dockWidget : findChildren<QDockWidget *>()) {
 1305             dockWidget->setFeatures(QDockWidget::NoDockWidgetFeatures);
 1306         }
 1307         ui->actionLockUnlock->setIcon(QIcon(":/lock"));
 1308     } else {
 1309         for (QDockWidget *dockWidget : findChildren<QDockWidget *>()) {
 1310             dockWidget->setFeatures(QDockWidget::AllDockWidgetFeatures);
 1311         }
 1312         ui->actionLockUnlock->setIcon(QIcon(":/unlock"));
 1313     }
 1314 }
 1315 
 1316 void MainWindow::on_actionFunctionsRename_triggered()
 1317 {
 1318     RenameDialog r(this);
 1319     // Get function based on click position
 1320     //r->setFunctionName(fcn_name);
 1321     r.exec();
 1322 }
 1323 
 1324 void MainWindow::on_actionDefault_triggered()
 1325 {
 1326     QSettings s;
 1327     restoreState(emptyState);
 1328 
 1329     initCorners();
 1330     resetDockWidgetList();
 1331 
 1332     if (core->currentlyDebugging) {
 1333         resetToDefaultLayout();
 1334         saveSettings();
 1335 
 1336         resetToDebugLayout();
 1337     } else {
 1338         resetToDebugLayout();
 1339         saveDebugSettings();
 1340 
 1341         resetToDefaultLayout();
 1342     }
 1343 }
 1344 
 1345 
 1346 /**
 1347  * @brief MainWindow::on_actionNew_triggered
 1348  * Open a new Cutter session.
 1349  */
 1350 void MainWindow::on_actionNew_triggered()
 1351 {
 1352     // Create a new Cutter process
 1353     static_cast<CutterApplication*>(qApp)->launchNewInstance();
 1354 }
 1355 
 1356 void MainWindow::on_actionSave_triggered()
 1357 {
 1358     saveProject();
 1359 }
 1360 
 1361 void MainWindow::on_actionSaveAs_triggered()
 1362 {
 1363     saveProjectAs();
 1364 }
 1365 
 1366 void MainWindow::on_actionRun_Script_triggered()
 1367 {
 1368     QFileDialog dialog(this);
 1369     dialog.setFileMode(QFileDialog::ExistingFile);
 1370     dialog.setViewMode(QFileDialog::Detail);
 1371     dialog.setDirectory(QDir::home());
 1372 
 1373     const QString &fileName = QDir::toNativeSeparators(dialog.getOpenFileName(this,
 1374                                                                               tr("Select radare2 script")));
 1375     if (fileName.isEmpty()) // Cancel was pressed
 1376         return;
 1377 
 1378     RunScriptTask *runScriptTask = new RunScriptTask();
 1379     runScriptTask->setFileName(fileName);
 1380 
 1381     AsyncTask::Ptr runScriptTaskPtr(runScriptTask);
 1382 
 1383     AsyncTaskDialog *taskDialog = new AsyncTaskDialog(runScriptTaskPtr, this);
 1384     taskDialog->setInterruptOnClose(true);
 1385     taskDialog->setAttribute(Qt::WA_DeleteOnClose);
 1386     taskDialog->show();
 1387 
 1388     Core()->getAsyncTaskManager()->start(runScriptTaskPtr);
 1389 }
 1390 
 1391 /**
 1392  * @brief MainWindow::on_actionOpen_triggered
 1393  * Open a file as in "load (add) a file in current session".
 1394  */
 1395 void MainWindow::on_actionMap_triggered()
 1396 {
 1397     MapFileDialog dialog(this);
 1398     dialog.exec();
 1399 }
 1400 
 1401 void MainWindow::toggleResponsive(bool maybe)
 1402 {
 1403     this->responsive = maybe;
 1404     // Save options in settings
 1405     QSettings settings;
 1406     settings.setValue("responsive", this->responsive);
 1407 }
 1408 
 1409 void MainWindow::on_actionTabs_on_Top_triggered()
 1410 {
 1411     this->on_actionTabs_triggered();
 1412 }
 1413 
 1414 void MainWindow::on_actionReset_settings_triggered()
 1415 {
 1416     QMessageBox::StandardButton ret =
 1417         (QMessageBox::StandardButton)QMessageBox::question(this, APPNAME,
 1418                                                            tr("Do you really want to clear all settings?"),
 1419                                                            QMessageBox::Ok | QMessageBox::Cancel);
 1420     if (ret == QMessageBox::Ok) {
 1421         Config()->resetAll();
 1422         on_actionDefault_triggered();
 1423     }
 1424 }
 1425 
 1426 void MainWindow::on_actionQuit_triggered()
 1427 {
 1428     close();
 1429 }
 1430 
 1431 void MainWindow::on_actionBackward_triggered()
 1432 {
 1433     core->seekPrev();
 1434 }
 1435 
 1436 void MainWindow::on_actionForward_triggered()
 1437 {
 1438     core->seekNext();
 1439 }
 1440 
 1441 void MainWindow::on_actionDisasAdd_comment_triggered()
 1442 {
 1443     CommentsDialog c(this);
 1444     c.exec();
 1445 }
 1446 
 1447 void MainWindow::on_actionRefresh_contents_triggered()
 1448 {
 1449     refreshAll();
 1450 }
 1451 
 1452 void MainWindow::on_actionPreferences_triggered()
 1453 {
 1454     auto dialog = new PreferencesDialog(this);
 1455     dialog->show();
 1456 }
 1457 
 1458 void MainWindow::on_actionTabs_triggered()
 1459 {
 1460     tabsOnTop = !tabsOnTop;
 1461     setTabLocation();
 1462 }
 1463 
 1464 void MainWindow::on_actionAbout_triggered()
 1465 {
 1466     AboutDialog *a = new AboutDialog(this);
 1467     a->setAttribute(Qt::WA_DeleteOnClose);
 1468     a->open();
 1469 }
 1470 
 1471 void MainWindow::on_actionIssue_triggered()
 1472 {
 1473     openIssue();
 1474 }
 1475 
 1476 void MainWindow::on_actionRefresh_Panels_triggered()
 1477 {
 1478     this->refreshAll();
 1479 }
 1480 
 1481 void MainWindow::on_actionAnalyze_triggered()
 1482 {
 1483     // TODO: implement this, but do NOT open InitialOptionsDialog!!
 1484 }
 1485 
 1486 void MainWindow::on_actionImportPDB_triggered()
 1487 {
 1488     QFileDialog dialog(this);
 1489     dialog.setWindowTitle(tr("Select PDB file"));
 1490     dialog.setNameFilters({ tr("PDB file (*.pdb)"), tr("All files (*)") });
 1491 
 1492     if (!dialog.exec()) {
 1493         return;
 1494     }
 1495 
 1496     const QString &pdbFile = QDir::toNativeSeparators(dialog.selectedFiles().first());
 1497 
 1498     if (!pdbFile.isEmpty()) {
 1499         core->loadPDB(pdbFile);
 1500         core->message(tr("%1 loaded.").arg(pdbFile));
 1501         this->refreshAll();
 1502     }
 1503 }
 1504 
 1505 void MainWindow::on_actionExport_as_code_triggered()
 1506 {
 1507     QStringList filters;
 1508     QMap<QString, QString> cmdMap;
 1509 
 1510     filters << tr("C uin8_t array (*.c)");
 1511     cmdMap[filters.last()] = "pc";
 1512     filters << tr("C uin16_t array (*.c)");
 1513     cmdMap[filters.last()] = "pch";
 1514     filters << tr("C uin32_t array (*.c)");
 1515     cmdMap[filters.last()] = "pcw";
 1516     filters << tr("C uin64_t array (*.c)");
 1517     cmdMap[filters.last()] = "pcd";
 1518     filters << tr("C string (*.c)");
 1519     cmdMap[filters.last()] = "pcs";
 1520     filters << tr("Shell-script that reconstructs the bin (*.sh)");
 1521     cmdMap[filters.last()] = "pcS";
 1522     filters << tr("JSON array (*.json)");
 1523     cmdMap[filters.last()] = "pcj";
 1524     filters << tr("JavaScript array (*.js)");
 1525     cmdMap[filters.last()] = "pcJ";
 1526     filters << tr("Python array (*.py)");
 1527     cmdMap[filters.last()] = "pcp";
 1528     filters << tr("Print 'wx' r2 commands (*.r2)");
 1529     cmdMap[filters.last()] = "pc*";
 1530     filters << tr("GAS .byte blob (*.asm, *.s)");
 1531     cmdMap[filters.last()] = "pca";
 1532     filters << tr(".bytes with instructions in comments (*.txt)");
 1533     cmdMap[filters.last()] = "pcA";
 1534 
 1535     QFileDialog dialog(this, tr("Export as code"));
 1536     dialog.setAcceptMode(QFileDialog::AcceptSave);
 1537     dialog.setFileMode(QFileDialog::AnyFile);
 1538     dialog.setNameFilters(filters);
 1539     dialog.selectFile("dump");
 1540     dialog.setDefaultSuffix("c");
 1541     if (!dialog.exec())
 1542         return;
 1543 
 1544     QFile file(dialog.selectedFiles()[0]);
 1545     if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
 1546         qWarning() << "Can't open file";
 1547         return;
 1548     }
 1549     TempConfig tempConfig;
 1550     tempConfig.set("io.va", false);
 1551     QTextStream fileOut(&file);
 1552     QString &cmd = cmdMap[dialog.selectedNameFilter()];
 1553 
 1554     // Use cmd because cmdRaw would not handle such input
 1555     fileOut << Core()->cmd(cmd + " $s @ 0");
 1556 }
 1557 
 1558 void MainWindow::on_actionGrouped_dock_dragging_triggered(bool checked)
 1559 {
 1560 #if QT_VERSION >= QT_VERSION_CHECK(5, 7, 0)
 1561     auto options = dockOptions();
 1562     options.setFlag(QMainWindow::DockOption::GroupedDragging, checked);
 1563     setDockOptions(options);
 1564 #else
 1565     Q_UNUSED(checked);
 1566 #endif
 1567 }
 1568 
 1569 void MainWindow::seekToFunctionLastInstruction()
 1570 {
 1571     Core()->seek(Core()->getLastFunctionInstruction(Core()->getOffset()));
 1572 }
 1573 
 1574 void MainWindow::seekToFunctionStart()
 1575 {
 1576     Core()->seek(Core()->getFunctionStart(Core()->getOffset()));
 1577 }
 1578 
 1579 void MainWindow::projectSaved(bool successfully, const QString &name)
 1580 {
 1581     if (successfully)
 1582         core->message(tr("Project saved: %1").arg(name));
 1583     else
 1584         core->message(tr("Failed to save project: %1").arg(name));
 1585 }
 1586 
 1587 void MainWindow::toggleDebugView()
 1588 {
 1589     if (Core()->currentlyDebugging) {
 1590         saveSettings();
 1591         restoreDebugLayout();
 1592         enableDebugWidgetsMenu(true);
 1593     } else {
 1594         saveDebugSettings();
 1595         MemoryWidgetType memType = getMemoryWidgetTypeToRestore();
 1596         hideAllDocks();
 1597         restoreDocks();
 1598         readSettingsOrDefault();
 1599         enableDebugWidgetsMenu(false);
 1600         showMemoryWidget(memType);
 1601     }
 1602 }
 1603 
 1604 void MainWindow::mousePressEvent(QMouseEvent *event)
 1605 {
 1606     switch (event->button()) {
 1607     case Qt::BackButton:
 1608         core->seekPrev();
 1609         break;
 1610     case Qt::ForwardButton:
 1611         core->seekNext();
 1612         break;
 1613     default:
 1614         break;
 1615     }
 1616 }
 1617 
 1618 bool MainWindow::eventFilter(QObject *, QEvent *event)
 1619 {
 1620     if (event->type() == QEvent::MouseButtonPress) {
 1621         QMouseEvent *mouseEvent = static_cast<QMouseEvent *>(event);
 1622         if (mouseEvent->button() == Qt::ForwardButton || mouseEvent->button() == Qt::BackButton) {
 1623             mousePressEvent(mouseEvent);
 1624             return true;
 1625         }
 1626     }
 1627     return false;
 1628 }
 1629 
 1630 bool MainWindow::event(QEvent *event)
 1631 {
 1632     if (event->type() == QEvent::FontChange
 1633         || event->type() == QEvent::StyleChange
 1634         || event->type() == QEvent::PaletteChange) {
 1635 #if QT_VERSION < QT_VERSION_CHECK(5,10,0)
 1636         QMetaObject::invokeMethod(Config(), "refreshFont", Qt::ConnectionType::QueuedConnection);
 1637 #else
 1638         QMetaObject::invokeMethod(Config(), &Configuration::refreshFont, Qt::ConnectionType::QueuedConnection);
 1639 #endif
 1640     }
 1641     return QMainWindow::event(event);
 1642 }
 1643 
 1644 /**
 1645  * @brief Show a warning message box.
 1646  *
 1647  * This API can either be used in Cutter internals, or by Python plugins.
 1648  */
 1649 void MainWindow::messageBoxWarning(QString title, QString message)
 1650 {
 1651     QMessageBox mb(this);
 1652     mb.setIcon(QMessageBox::Warning);
 1653     mb.setStandardButtons(QMessageBox::Ok);
 1654     mb.setWindowTitle(title);
 1655     mb.setText(message);
 1656     mb.exec();
 1657 }
 1658 
 1659 /**
 1660  * @brief When theme changed, change icons which have a special version for the theme.
 1661  */
 1662 void MainWindow::chooseThemeIcons()
 1663 {
 1664     // List of QActions which have alternative icons in different themes
 1665     const QList<QPair<void*, QString>> kSupportedIconsNames {
 1666         { ui->actionForward, QStringLiteral("arrow_right.svg") },
 1667         { ui->actionBackward, QStringLiteral("arrow_left.svg") },      
 1668     };
 1669 
 1670 
 1671     // Set the correct icon for the QAction
 1672     qhelpers::setThemeIcons(kSupportedIconsNames, [](void *obj, const QIcon &icon) {
 1673         static_cast<QAction*>(obj)->setIcon(icon);
 1674     });
 1675 }
 1676 
 1677 void MainWindow::onZoomIn()
 1678 {
 1679   Config()->setZoomFactor(Config()->getZoomFactor() + 0.1);
 1680 }
 1681 
 1682 void MainWindow::onZoomOut()
 1683 {
 1684   Config()->setZoomFactor(Config()->getZoomFactor() - 0.1);
 1685 }
 1686 
 1687 void MainWindow::onZoomReset()
 1688 {
 1689   Config()->setZoomFactor(1.0);
 1690 }
 1691 
 1692 QMenu *MainWindow::getContextMenuExtensions(ContextMenuType type)
 1693 {
 1694     switch (type) {
 1695     case ContextMenuType::Disassembly:
 1696         return disassemblyContextMenuExtensions;
 1697     case ContextMenuType::Addressable:
 1698         return addressableContextMenuExtensions;
 1699     default:
 1700         return nullptr;
 1701     }
 1702 }