Improve compatibility with cmake 3.5 and QT 5.5-5.11. (#1805)

This commit is contained in:
karliss 2019-10-06 20:35:44 +03:00 committed by GitHub
parent cbd8984ca7
commit b11ba240d2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
40 changed files with 552 additions and 225 deletions

View File

@ -31,6 +31,24 @@ matrix:
- pyenv global 3.7.1 - pyenv global 3.7.1
- pip install meson - pip install meson
- name: Linux Ubuntu 16.04
os: linux
env: BUILD_SYSTEM=cmake_nodep PATH="/usr/bin:$PATH"
dist: xenial
addons:
apt:
packages:
- ninja-build
- libgraphviz-dev
- qt5-default
- libqt5svg5-dev
- cmake
before_install:
- pyenv install 3.5.2
- pyenv global 3.5.2
- pip install meson
- name: macOS QMake + Deploy - name: macOS QMake + Deploy
os: osx os: osx
osx_image: xcode10.3 osx_image: xcode10.3
@ -82,8 +100,10 @@ addons:
brewfile: scripts/Brewfile brewfile: scripts/Brewfile
install: install:
- scripts/fetch_deps.sh - if [[ "$BUILD_SYSTEM" != "cmake_nodep" ]]; then
- source cutter-deps/env.sh scripts/fetch_deps.sh;
source cutter-deps/env.sh;
fi
- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then
export PATH=/usr/local/opt/llvm/bin:$PATH; export PATH=/usr/local/opt/llvm/bin:$PATH;
source scripts/prepare_breakpad_macos.sh; source scripts/prepare_breakpad_macos.sh;
@ -131,6 +151,12 @@ script:
-DCUTTER_USE_BUNDLED_RADARE2=ON -DCUTTER_USE_BUNDLED_RADARE2=ON
../src && ../src &&
make -j4; make -j4;
elif [[ "$BUILD_SYSTEM" == "cmake_nodep" ]]; then
cmake
-DCMAKE_BUILD_TYPE=Release
-DCUTTER_USE_BUNDLED_RADARE2=ON
../src &&
make -j4;
fi fi
elif [[ "$TRAVIS_OS_NAME" == "osx" ]]; then elif [[ "$TRAVIS_OS_NAME" == "osx" ]]; then
if [[ "$BUILD_SYSTEM" == "qmake" ]]; then if [[ "$BUILD_SYSTEM" == "qmake" ]]; then

View File

@ -13,7 +13,7 @@ alternatives cmake and meson.
In any case, there are obviously some requirements: In any case, there are obviously some requirements:
* Radare2 installed from submodule * Radare2 installed from submodule
* Qt 5.9 or above * Qt 5.9 or above (can compile using 5.5 with some limitations)
* Python3.6 * Python3.6
* Breakpad installed using script (optional, disabled by default) * Breakpad installed using script (optional, disabled by default)
@ -30,6 +30,7 @@ Note that there are three major building options available:
* ``CUTTER_ENABLE_PYTHON`` compile with Python support * ``CUTTER_ENABLE_PYTHON`` compile with Python support
* ``CUTTER_ENABLE_PYTHON_BINDINGS`` automatically generate Python Bindings with Shiboken2, required for Python plugins! * ``CUTTER_ENABLE_PYTHON_BINDINGS`` automatically generate Python Bindings with Shiboken2, required for Python plugins!
* ``CUTTER_ENABLE_CRASH_REPORTS`` is used to compile Cutter with crash handling system enabled (Breakpad) * ``CUTTER_ENABLE_CRASH_REPORTS`` is used to compile Cutter with crash handling system enabled (Breakpad)
* ``CUTTER_USE_BUNDLED_RADARE2`` CMake only, automatically compile Radare2 from submodule.
-------------- --------------
@ -91,7 +92,7 @@ Building with Cmake
Requirements Requirements
~~~~~~~~~~~~ ~~~~~~~~~~~~
- CMake >= 3.1 - CMake >= 3.5 (recommended >= 3.6)
Building on Linux Building on Linux
~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~

View File

@ -1,7 +1,6 @@
cmake_minimum_required(VERSION 3.5)
cmake_minimum_required(VERSION 3.1) if(POLICY CMP0074)
if(${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.12.0")
cmake_policy(SET CMP0074 NEW) cmake_policy(SET CMP0074 NEW)
endif() endif()
@ -103,19 +102,13 @@ else()
set(KSYNTAXHIGHLIGHTING_STATUS OFF) set(KSYNTAXHIGHLIGHTING_STATUS OFF)
endif() endif()
find_package(PkgConfig REQUIRED)
if (CUTTER_ENABLE_GRAPHVIZ) if (CUTTER_ENABLE_GRAPHVIZ)
if (CUTTER_ENABLE_GRAPHVIZ STREQUAL AUTO) if (CUTTER_ENABLE_GRAPHVIZ STREQUAL AUTO)
pkg_check_modules(GVC IMPORTED_TARGET libgvc) find_package(Graphviz)
if (GVC_FOUND)
set(CUTTER_ENABLE_GRAPHVIZ ON)
else()
set(CUTTER_ENABLE_GRAPHVIZ OFF)
endif()
else() else()
pkg_check_modules(GVC REQUIRED IMPORTED_TARGET libgvc) find_package(Graphviz REQUIRED)
endif() endif()
set (CUTTER_ENABLE_GRAPHVIZ ${Graphviz_FOUND})
endif() endif()
message(STATUS "") message(STATUS "")
@ -157,7 +150,7 @@ else()
endif() endif()
if (CUTTER_ENABLE_GRAPHVIZ) if (TARGET Graphviz::GVC)
list(APPEND SOURCE_FILES ${CUTTER_PRO_GRAPHVIZ_SOURCES}) list(APPEND SOURCE_FILES ${CUTTER_PRO_GRAPHVIZ_SOURCES})
list(APPEND HEADER_FILES ${CUTTER_PRO_GRAPHVIZ_HEADERS}) list(APPEND HEADER_FILES ${CUTTER_PRO_GRAPHVIZ_HEADERS})
endif() endif()
@ -173,8 +166,8 @@ endif()
add_executable(Cutter MACOSX_BUNDLE ${UI_FILES} ${QRC_FILES} ${SOURCE_FILES} ${HEADER_FILES} ${BINDINGS_SOURCE}) add_executable(Cutter MACOSX_BUNDLE ${UI_FILES} ${QRC_FILES} ${SOURCE_FILES} ${HEADER_FILES} ${BINDINGS_SOURCE})
set_target_properties(Cutter PROPERTIES MACOSX_BUNDLE_INFO_PLIST "${CMAKE_CURRENT_SOURCE_DIR}/macos/Info.plist") set_target_properties(Cutter PROPERTIES MACOSX_BUNDLE_INFO_PLIST "${CMAKE_CURRENT_SOURCE_DIR}/macos/Info.plist")
if (CUTTER_ENABLE_GRAPHVIZ) if (TARGET Graphviz::GVC)
target_link_libraries(Cutter PkgConfig::GVC) target_link_libraries(Cutter Graphviz::GVC)
target_compile_definitions(Cutter PRIVATE CUTTER_ENABLE_GRAPHVIZ) target_compile_definitions(Cutter PRIVATE CUTTER_ENABLE_GRAPHVIZ)
endif() endif()

View File

@ -148,6 +148,7 @@ int main(int argc, char *argv[])
migrateThemes(); migrateThemes();
if (Config()->getAutoUpdateEnabled()) { if (Config()->getAutoUpdateEnabled()) {
#if CUTTER_UPDATE_WORKER_AVAILABLE
UpdateWorker *updateWorker = new UpdateWorker; UpdateWorker *updateWorker = new UpdateWorker;
QObject::connect(updateWorker, &UpdateWorker::checkComplete, QObject::connect(updateWorker, &UpdateWorker::checkComplete,
[=](const QVersionNumber &version, const QString & error) { [=](const QVersionNumber &version, const QString & error) {
@ -157,6 +158,7 @@ int main(int argc, char *argv[])
updateWorker->deleteLater(); updateWorker->deleteLater();
}); });
updateWorker->checkCurrentVersion(7000); updateWorker->checkCurrentVersion(7000);
#endif
} }
int ret = a.exec(); int ret = a.exec();

View File

@ -26,7 +26,7 @@ set(Radare2_INCLUDE_DIRS "${RADARE2_INSTALL_DIR}/include/libr")
add_library(Radare2 INTERFACE) add_library(Radare2 INTERFACE)
add_dependencies(Radare2 Radare2-Bundled) add_dependencies(Radare2 Radare2-Bundled)
if(${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.13.0") if(NOT (${CMAKE_VERSION} VERSION_LESS "3.13.0"))
target_link_directories(Radare2 INTERFACE "${RADARE2_INSTALL_DIR}/lib") target_link_directories(Radare2 INTERFACE "${RADARE2_INSTALL_DIR}/lib")
else() else()
link_directories("${RADARE2_INSTALL_DIR}/lib") link_directories("${RADARE2_INSTALL_DIR}/lib")

View File

@ -0,0 +1,25 @@
set (_module Graphviz)
find_package(PkgConfig REQUIRED)
if (NOT (CMAKE_VERSION VERSION_LESS "3.6.0"))
pkg_check_modules(GVC IMPORTED_TARGET GLOBAL libgvc)
else()
pkg_check_modules(GVC libgvc)
endif()
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(${_module}
FOUND_VAR ${_module}_FOUND
REQUIRED_VARS GVC_INCLUDE_DIRS)
if (${GVC_FOUND})
if (CMAKE_VERSION VERSION_LESS "3.6.0")
add_library(${_module}::GVC INTERFACE IMPORTED)
set_target_properties(${_module}::GVC PROPERTIES
INTERFACE_INCLUDE_DIRECTORIES "${GVC_INCLUDE_DIRS}")
set_target_properties(${_module}::GVC PROPERTIES
INTERFACE_LINK_LIBRARIES "${GVC_LIBRARIES}")
else()
add_library(${_module}::GVC ALIAS PkgConfig::GVC)
endif()
endif()

View File

@ -208,7 +208,21 @@ QJsonDocument ColorThemeWorker::getTheme(const QString& themeName) const
theme.remove(key); theme.remove(key);
} }
return QJsonDocument(QJsonObject::fromVariantMap(theme)); // manualy converting instead of using QJsonObject::fromVariantMap because
// Qt < 5.6 QJsonValue.fromVariant doesn't expect QVariant to already contain
// QJson values like QJsonArray. https://github.com/qt/qtbase/commit/26237f0a2d8db80024b601f676bbce54d483e672
QJsonObject obj;
for (auto it = theme.begin(); it != theme.end(); it++) {
auto &value = it.value();
if (value.canConvert<QJsonArray>()) {
obj[it.key()] = it.value().value<QJsonArray>();
} else {
obj[it.key()] = QJsonValue::fromVariant(value);
}
}
return QJsonDocument(obj);
} }
QString ColorThemeWorker::deleteTheme(const QString &themeName) const QString ColorThemeWorker::deleteTheme(const QString &themeName) const

View File

@ -252,4 +252,14 @@ void prependQAction(QAction *action, QMenu *menu)
} }
} }
qreal devicePixelRatio(const QPaintDevice *p)
{
#if QT_VERSION >= QT_VERSION_CHECK(5, 6, 0)
return p->devicePixelRatioF();
#else
return p->devicePixelRatio();
#endif
}
} // end namespace } // end namespace

View File

@ -18,6 +18,7 @@ class QWidget;
class QTreeView; class QTreeView;
class QAction; class QAction;
class QMenu; class QMenu;
class QPaintDevice;
namespace qhelpers { namespace qhelpers {
QString formatBytecount(const long bytecount); QString formatBytecount(const long bytecount);
@ -54,6 +55,7 @@ QByteArray applyColorToSvg(const QString &filename, QColor color);
void setThemeIcons(QList<QPair<void*, QString>> supportedIconsNames, std::function<void(void *, const QIcon &)> setter); void setThemeIcons(QList<QPair<void*, QString>> supportedIconsNames, std::function<void(void *, const QIcon &)> setter);
void prependQAction(QAction *action, QMenu *menu); void prependQAction(QAction *action, QMenu *menu);
qreal devicePixelRatio(const QPaintDevice *p);
} // qhelpers } // qhelpers

View File

@ -1,5 +1,6 @@
#include "UpdateWorker.h" #include "UpdateWorker.h"
#if CUTTER_UPDATE_WORKER_AVAILABLE
#include <QUrl> #include <QUrl>
#include <QFile> #include <QFile>
#include <QTimer> #include <QTimer>
@ -19,6 +20,7 @@
#include "common/Configuration.h" #include "common/Configuration.h"
#include "CutterConfig.h" #include "CutterConfig.h"
UpdateWorker::UpdateWorker(QObject *parent) : UpdateWorker::UpdateWorker(QObject *parent) :
QObject(parent), pending(false) QObject(parent), pending(false)
{ {
@ -216,4 +218,5 @@ QString UpdateWorker::getRepositoryFileName() const
QVersionNumber UpdateWorker::currentVersionNumber() QVersionNumber UpdateWorker::currentVersionNumber()
{ {
return QVersionNumber(CUTTER_VERSION_MAJOR, CUTTER_VERSION_MINOR, CUTTER_VERSION_PATCH); return QVersionNumber(CUTTER_VERSION_MAJOR, CUTTER_VERSION_MINOR, CUTTER_VERSION_PATCH);
} }
#endif // CUTTER_UPDATE_WORKER_AVAILABLE

View File

@ -1,10 +1,21 @@
#ifndef UPDATEWORKER_H #ifndef UPDATEWORKER_H
#define UPDATEWORKER_H #define UPDATEWORKER_H
#include <QtGlobal>
#if (QT_VERSION >= QT_VERSION_CHECK(5, 6, 0))
#define CUTTER_UPDATE_WORKER_AVAILABLE 1
#else
#define CUTTER_UPDATE_WORKER_AVAILABLE 0
#endif
#if CUTTER_UPDATE_WORKER_AVAILABLE
#include <QDir> #include <QDir>
#include <QTimer> #include <QTimer>
#include <QObject> #include <QObject>
#include <QtNetwork/QNetworkAccessManager> #include <QtNetwork/QNetworkAccessManager>
#include <QVersionNumber> #include <QVersionNumber>
class QNetworkReply; class QNetworkReply;
@ -126,4 +137,5 @@ private:
QNetworkReply *checkReply; QNetworkReply *checkReply;
}; };
#endif //CUTTER_UPDATE_WORKER_AVAILABLE
#endif // UPDATEWORKER_H #endif // UPDATEWORKER_H

View File

@ -4,6 +4,9 @@
#include <QDir> #include <QDir>
#include <QCoreApplication> #include <QCoreApplication>
#include <cassert>
#include <memory>
#include "common/TempConfig.h" #include "common/TempConfig.h"
#include "common/Configuration.h" #include "common/Configuration.h"
#include "common/AsyncTask.h" #include "common/AsyncTask.h"

View File

@ -187,6 +187,10 @@ void MainWindow::initUI()
plugin->setupInterface(this); plugin->setupInterface(this);
} }
#if QT_VERSION < QT_VERSION_CHECK(5, 7, 0)
ui->actionGrouped_dock_dragging->setVisible(false);
#endif
initLayout(); initLayout();
} }
@ -1402,9 +1406,13 @@ void MainWindow::on_actionExport_as_code_triggered()
void MainWindow::on_actionGrouped_dock_dragging_triggered(bool checked) void MainWindow::on_actionGrouped_dock_dragging_triggered(bool checked)
{ {
#if QT_VERSION >= QT_VERSION_CHECK(5, 7, 0)
auto options = dockOptions(); auto options = dockOptions();
options.setFlag(QMainWindow::DockOption::GroupedDragging, checked); options.setFlag(QMainWindow::DockOption::GroupedDragging, checked);
setDockOptions(options); setDockOptions(options);
#else
Q_UNUSED(checked);
#endif
} }
void MainWindow::seekToFunctionLastInstruction() void MainWindow::seekToFunctionLastInstruction()

View File

@ -53,6 +53,11 @@ AboutDialog::AboutDialog(QWidget *parent) :
QSignalBlocker s(ui->updatesCheckBox); QSignalBlocker s(ui->updatesCheckBox);
ui->updatesCheckBox->setChecked(Config()->getAutoUpdateEnabled()); ui->updatesCheckBox->setChecked(Config()->getAutoUpdateEnabled());
if (!CUTTER_UPDATE_WORKER_AVAILABLE) {
ui->updatesCheckBox->hide();
ui->checkForUpdatesButton->hide();
}
} }
AboutDialog::~AboutDialog() {} AboutDialog::~AboutDialog() {}
@ -80,6 +85,7 @@ void AboutDialog::on_showPluginsButton_clicked()
void AboutDialog::on_checkForUpdatesButton_clicked() void AboutDialog::on_checkForUpdatesButton_clicked()
{ {
#if CUTTER_UPDATE_WORKER_AVAILABLE
UpdateWorker updateWorker; UpdateWorker updateWorker;
QProgressDialog waitDialog; QProgressDialog waitDialog;
@ -105,6 +111,7 @@ void AboutDialog::on_checkForUpdatesButton_clicked()
updateWorker.checkCurrentVersion(7000); updateWorker.checkCurrentVersion(7000);
waitDialog.exec(); waitDialog.exec();
#endif
} }
void AboutDialog::on_updatesCheckBox_stateChanged(int) void AboutDialog::on_updatesCheckBox_stateChanged(int)

View File

@ -345,7 +345,8 @@ void NewFileDialog::fillIOPluginsList()
if (plugin.permissions.contains('d')) { if (plugin.permissions.contains('d')) {
continue; continue;
} }
for (const auto &uri : qAsConst(plugin.uris)) { const auto &uris = plugin.uris;
for (const auto &uri : uris) {
if (uri == "file://") { if (uri == "file://") {
continue; continue;
} }

View File

@ -17,7 +17,9 @@ TypesInteractionDialog::TypesInteractionDialog(QWidget *parent, bool readOnly) :
QFont font = Config()->getFont(); QFont font = Config()->getFont();
ui->plainTextEdit->setFont(font); ui->plainTextEdit->setFont(font);
ui->plainTextEdit->setPlainText(""); ui->plainTextEdit->setPlainText("");
#if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)
ui->plainTextEdit->setTabStopDistance(4 * QFontMetrics(font).horizontalAdvance(' ')); ui->plainTextEdit->setTabStopDistance(4 * QFontMetrics(font).horizontalAdvance(' '));
#endif
syntaxHighLighter = Config()->createSyntaxHighlighter(ui->plainTextEdit->document()); syntaxHighLighter = Config()->createSyntaxHighlighter(ui->plainTextEdit->document());
ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false); ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false);
ui->plainTextEdit->setReadOnly(readOnly); ui->plainTextEdit->setReadOnly(readOnly);

View File

@ -16,7 +16,7 @@ WelcomeDialog::WelcomeDialog(QWidget *parent) :
ui(new Ui::WelcomeDialog) ui(new Ui::WelcomeDialog)
{ {
ui->setupUi(this); ui->setupUi(this);
setWindowFlag(Qt::WindowContextHelpButtonHint, false); setWindowFlags(windowFlags() & (~Qt::WindowContextHelpButtonHint));
ui->logoSvgWidget->load(Config()->getLogoFile()); ui->logoSvgWidget->load(Config()->getLogoFile());
ui->versionLabel->setText("<font color='#a4a9b2'>" + tr("Version ") + CUTTER_VERSION_FULL + "</font>"); ui->versionLabel->setText("<font color='#a4a9b2'>" + tr("Version ") + CUTTER_VERSION_FULL + "</font>");
ui->themeComboBox->setCurrentIndex(Config()->getInterfaceTheme()); ui->themeComboBox->setCurrentIndex(Config()->getInterfaceTheme());

View File

@ -54,9 +54,10 @@ AsmOptionsWidget::AsmOptionsWidget(PreferencesDialog *dialog)
connect(confCheckbox->checkBox, &QCheckBox::stateChanged, [this, val, &cb]() { checkboxEnabler(&cb, val) ;}); connect(confCheckbox->checkBox, &QCheckBox::stateChanged, [this, val, &cb]() { checkboxEnabler(&cb, val) ;});
} }
connect(ui->commentsComboBox, QOverload<int>::of(&QComboBox::currentIndexChanged), this, using indexSignalType = void (QComboBox::*)(int);
connect(ui->commentsComboBox, static_cast<indexSignalType>(&QComboBox::currentIndexChanged), this,
&AsmOptionsWidget::commentsComboBoxChanged); &AsmOptionsWidget::commentsComboBoxChanged);
connect(ui->asmComboBox, QOverload<int>::of(&QComboBox::currentIndexChanged), this, connect(ui->asmComboBox, static_cast<indexSignalType>(&QComboBox::currentIndexChanged), this,
&AsmOptionsWidget::asmComboBoxChanged); &AsmOptionsWidget::asmComboBoxChanged);
connect(Core(), SIGNAL(asmOptionsChanged()), this, SLOT(updateAsmOptionsFromVars())); connect(Core(), SIGNAL(asmOptionsChanged()), this, SLOT(updateAsmOptionsFromVars()));
updateAsmOptionsFromVars(); updateAsmOptionsFromVars();

View File

@ -21,7 +21,7 @@ PreferencesDialog::PreferencesDialog(QWidget *parent)
{ {
setAttribute(Qt::WA_DeleteOnClose); setAttribute(Qt::WA_DeleteOnClose);
ui->setupUi(this); ui->setupUi(this);
setWindowFlag(Qt::WindowContextHelpButtonHint, false); setWindowFlags(windowFlags() & (~Qt::WindowContextHelpButtonHint));
QList<PreferenceCategory> prefs { QList<PreferenceCategory> prefs {

View File

@ -22,7 +22,48 @@ DisassemblyContextMenu::DisassemblyContextMenu(QWidget *parent, MainWindow *main
: QMenu(parent), : QMenu(parent),
offset(0), offset(0),
canCopy(false), canCopy(false),
mainWindow(mainWindow) mainWindow(mainWindow),
actionEditInstruction(this),
actionNopInstruction(this),
actionJmpReverse(this),
actionEditBytes(this),
actionCopy(this),
actionCopyAddr(this),
actionAddComment(this),
actionAddFlag(this),
actionAnalyzeFunction(this),
actionEditFunction(this),
actionRename(this),
actionRenameUsedHere(this),
actionSetFunctionVarTypes(this),
actionXRefs(this),
actionDisplayOptions(this),
actionDeleteComment(this),
actionDeleteFlag(this),
actionDeleteFunction(this),
actionLinkType(this),
actionSetBaseBinary(this),
actionSetBaseOctal(this),
actionSetBaseDecimal(this),
actionSetBaseHexadecimal(this),
actionSetBasePort(this),
actionSetBaseIPAddr(this),
actionSetBaseSyscall(this),
actionSetBaseString(this),
actionSetBits16(this),
actionSetBits32(this),
actionSetBits64(this),
actionContinueUntil(this),
actionAddBreakpoint(this),
actionSetPC(this),
actionSetToCode(this),
actionSetAsString(this),
actionSetToDataEx(this),
actionSetToDataByte(this),
actionSetToDataWord(this),
actionSetToDataDword(this),
actionSetToDataQword(this),
showInSubmenu(this)
{ {
initAction(&actionCopy, tr("Copy"), SLOT(on_actionCopy_triggered()), getCopySequence()); initAction(&actionCopy, tr("Copy"), SLOT(on_actionCopy_triggered()), getCopySequence());
addAction(&actionCopy); addAction(&actionCopy);
@ -209,7 +250,7 @@ void DisassemblyContextMenu::addSetToDataMenu()
SLOT(on_actionSetToDataEx_triggered()), getSetToDataExSequence()); SLOT(on_actionSetToDataEx_triggered()), getSetToDataExSequence());
setToDataMenu->addAction(&actionSetToDataEx); setToDataMenu->addAction(&actionSetToDataEx);
auto switchAction = new QAction(); auto switchAction = new QAction(this);
initAction(switchAction, "Switch Data", initAction(switchAction, "Switch Data",
SLOT(on_actionSetToData_triggered()), getSetToDataSequence()); SLOT(on_actionSetToData_triggered()), getSetToDataSequence());
} }

View File

@ -327,7 +327,7 @@ QColor ColorPicker::getColorAtMouse()
const QPixmap pixmap = QGuiApplication::screens().at(desktop->screenNumber()) const QPixmap pixmap = QGuiApplication::screens().at(desktop->screenNumber())
->grabWindow(desktop->winId(), ->grabWindow(desktop->winId(),
QCursor::pos().x(), QCursor::pos().y(), 1, 1); QCursor::pos().x(), QCursor::pos().y(), 1, 1);
return pixmap.toImage().pixelColor(0, 0); return QColor(pixmap.toImage().pixel(0, 0));
} }
bool ColorPicker::isPickingFromScreen() const bool ColorPicker::isPickingFromScreen() const

View File

@ -14,6 +14,7 @@
#include "common/Configuration.h" #include "common/Configuration.h"
#include "common/ColorThemeWorker.h" #include "common/ColorThemeWorker.h"
#include "common/Helpers.h"
#include "widgets/ColorThemeListView.h" #include "widgets/ColorThemeListView.h"
@ -39,7 +40,7 @@ void ColorOptionDelegate::paint(QPainter *painter,
const QStyleOptionViewItem &option, const QStyleOptionViewItem &option,
const QModelIndex &index) const const QModelIndex &index) const
{ {
int margin = this->margin * painter->device()->devicePixelRatioF(); int margin = this->margin * qhelpers::devicePixelRatio(painter->device());
painter->save(); painter->save();
painter->setFont(option.font); painter->setFont(option.font);
painter->setRenderHint(QPainter::Antialiasing); painter->setRenderHint(QPainter::Antialiasing);
@ -138,7 +139,7 @@ void ColorOptionDelegate::paint(QPainter *painter,
// Create chess-like pattern of black and white squares // Create chess-like pattern of black and white squares
// and fill background of roundedColorRect with it // and fill background of roundedColorRect with it
if (currCO.color.alpha() < 255) { if (currCO.color.alpha() < 255) {
const int c1 = static_cast<int>(8 * painter->device()->devicePixelRatioF()); const int c1 = static_cast<int>(8 * qhelpers::devicePixelRatio(painter->device()));
const int c2 = c1 / 2; const int c2 = c1 / 2;
QPixmap p(c1, c1); QPixmap p(c1, c1);
QPainter paint(&p); QPainter paint(&p);
@ -165,7 +166,7 @@ void ColorOptionDelegate::paint(QPainter *painter,
QSize ColorOptionDelegate::sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const QSize ColorOptionDelegate::sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const
{ {
qreal margin = this->margin * option.widget->devicePixelRatioF(); qreal margin = this->margin * qhelpers::devicePixelRatio(option.widget);
qreal fontHeight = option.fontMetrics.height(); qreal fontHeight = option.fontMetrics.height();
qreal h = QPen().width(); qreal h = QPen().width();
h += fontHeight; // option name h += fontHeight; // option name

View File

@ -176,7 +176,7 @@ void ConsoleWidget::executeCommand(const QString &command)
ui->outputTextEdit->appendHtml(cmd_line + result); ui->outputTextEdit->appendHtml(cmd_line + result);
scrollOutputToEnd(); scrollOutputToEnd();
historyAdd(command); historyAdd(command);
commandTask = nullptr; commandTask.clear();
ui->inputLineEdit->setEnabled(true); ui->inputLineEdit->setEnabled(true);
ui->inputLineEdit->setFocus(); ui->inputLineEdit->setFocus();
if (oldOffset != Core()->getOffset()) { if (oldOffset != Core()->getOffset()) {

View File

@ -9,6 +9,7 @@
#include "common/SyntaxHighlighter.h" #include "common/SyntaxHighlighter.h"
#include "common/BasicBlockHighlighter.h" #include "common/BasicBlockHighlighter.h"
#include "dialogs/MultitypeFileSaveDialog.h" #include "dialogs/MultitypeFileSaveDialog.h"
#include "common/Helpers.h"
#include <QColorDialog> #include <QColorDialog>
#include <QPainter> #include <QPainter>
@ -27,16 +28,19 @@
#include <QStandardPaths> #include <QStandardPaths>
#include <QClipboard> #include <QClipboard>
#include <QApplication> #include <QApplication>
#include <QAction>
#include <cmath> #include <cmath>
DisassemblerGraphView::DisassemblerGraphView(QWidget *parent, CutterSeekable *seekable, DisassemblerGraphView::DisassemblerGraphView(QWidget *parent, CutterSeekable *seekable,
MainWindow *mainWindow) MainWindow *mainWindow, QList<QAction *> additionalMenuActions)
: GraphView(parent), : GraphView(parent),
mFontMetrics(nullptr), mFontMetrics(nullptr),
blockMenu(new DisassemblyContextMenu(this, mainWindow)), blockMenu(new DisassemblyContextMenu(this, mainWindow)),
contextMenu(new QMenu(this)), contextMenu(new QMenu(this)),
seekable(seekable) seekable(seekable),
actionExportGraph(this),
actionUnhighlight(this)
{ {
highlight_token = nullptr; highlight_token = nullptr;
auto *layout = new QVBoxLayout(this); auto *layout = new QVBoxLayout(this);
@ -100,8 +104,6 @@ DisassemblerGraphView::DisassemblerGraphView(QWidget *parent, CutterSeekable *se
// Export Graph menu // Export Graph menu
actionExportGraph.setText(tr("Export Graph")); actionExportGraph.setText(tr("Export Graph"));
connect(&actionExportGraph, SIGNAL(triggered(bool)), this, SLOT(on_actionExportGraph_triggered())); connect(&actionExportGraph, SIGNAL(triggered(bool)), this, SLOT(on_actionExportGraph_triggered()));
actionSyncOffset.setText(tr("Sync/unsync offset"));
connect(&actionSyncOffset, &QAction::triggered, seekable, &CutterSeekable::toggleSynchronization);
// Context menu that applies to everything // Context menu that applies to everything
contextMenu->addAction(&actionExportGraph); contextMenu->addAction(&actionExportGraph);
@ -133,7 +135,7 @@ DisassemblerGraphView::DisassemblerGraphView(QWidget *parent, CutterSeekable *se
} }
layoutMenu->addActions(layoutGroup->actions()); layoutMenu->addActions(layoutGroup->actions());
contextMenu->addSeparator(); contextMenu->addSeparator();
contextMenu->addAction(&actionSyncOffset); contextMenu->addActions(additionalMenuActions);
QAction *highlightBB = new QAction(this); QAction *highlightBB = new QAction(this);
@ -484,7 +486,8 @@ void DisassemblerGraphView::drawBlock(QPainter &p, GraphView::GraphBlock &block,
// Stop rendering text when it's too small // Stop rendering text when it's too small
auto transform = p.combinedTransform(); auto transform = p.combinedTransform();
QRect screenChar = transform.mapRect(QRect(0, 0, charWidth, charHeight)); QRect screenChar = transform.mapRect(QRect(0, 0, charWidth, charHeight));
if (screenChar.width() * p.device()->devicePixelRatioF() < 4) {
if (screenChar.width() * qhelpers::devicePixelRatio(p.device()) < 4) {
return; return;
} }
@ -648,7 +651,7 @@ RVA DisassemblerGraphView::getAddrForMouseEvent(GraphBlock &block, QPoint *point
} }
DisassemblerGraphView::Instr *DisassemblerGraphView::getInstrForMouseEvent( DisassemblerGraphView::Instr *DisassemblerGraphView::getInstrForMouseEvent(
GraphView::GraphBlock &block, QPoint *point) GraphView::GraphBlock &block, QPoint *point, bool force)
{ {
DisassemblyBlock &db = disassembly_blocks[block.entry]; DisassemblyBlock &db = disassembly_blocks[block.entry];
@ -667,6 +670,13 @@ DisassemblerGraphView::Instr *DisassemblerGraphView::getInstrForMouseEvent(
} }
cur_row += instr.text.lines.size(); cur_row += instr.text.lines.size();
} }
if (force && !db.instrs.empty()) {
if (mouse_row <= 0) {
return &db.instrs.front();
} else {
return &db.instrs.back();
}
}
return nullptr; return nullptr;
} }
@ -935,7 +945,7 @@ QPoint DisassemblerGraphView::getInstructionOffset(const DisassemblyBlock &block
void DisassemblerGraphView::blockClicked(GraphView::GraphBlock &block, QMouseEvent *event, void DisassemblerGraphView::blockClicked(GraphView::GraphBlock &block, QMouseEvent *event,
QPoint pos) QPoint pos)
{ {
Instr *instr = getInstrForMouseEvent(block, &pos); Instr *instr = getInstrForMouseEvent(block, &pos, event->button() == Qt::RightButton);
if (!instr) { if (!instr) {
return; return;
} }
@ -952,14 +962,27 @@ void DisassemblerGraphView::blockClicked(GraphView::GraphBlock &block, QMouseEve
if (highlight_token) { if (highlight_token) {
blockMenu->setCurHighlightedWord(highlight_token->content); blockMenu->setCurHighlightedWord(highlight_token->content);
} }
if (event->button() == Qt::RightButton) {
actionUnhighlight.setVisible(Core()->getBBHighlighter()->getBasicBlock(block.entry));
event->accept();
blockMenu->exec(event->globalPos());
}
viewport()->update(); viewport()->update();
} }
void DisassemblerGraphView::blockContextMenuRequested(GraphView::GraphBlock &block,
QContextMenuEvent *event, QPoint pos)
{
actionUnhighlight.setVisible(Core()->getBBHighlighter()->getBasicBlock(block.entry));
event->accept();
blockMenu->exec(event->globalPos());
}
void DisassemblerGraphView::contextMenuEvent(QContextMenuEvent *event)
{
GraphView::contextMenuEvent(event);
if (!event->isAccepted()) {
//TODO: handle opening block menu using keyboard
contextMenu->exec(event->globalPos());
event->accept();
}
}
void DisassemblerGraphView::blockDoubleClicked(GraphView::GraphBlock &block, QMouseEvent *event, void DisassemblerGraphView::blockDoubleClicked(GraphView::GraphBlock &block, QMouseEvent *event,
QPoint pos) QPoint pos)
{ {
@ -1112,16 +1135,13 @@ void DisassemblerGraphView::exportR2GraphvizGraph(QString filePath, QString type
{ {
TempConfig tempConfig; TempConfig tempConfig;
tempConfig.set("graph.gv.format", type); tempConfig.set("graph.gv.format", type);
qWarning() << Core()->cmdRaw(QString("agfw \"%1\" @ 0x%2").arg(filePath).arg(currentFcnAddr, 0, 16)); qWarning() << Core()->cmdRaw(QString("agfw \"%1\" @ 0x%2")
.arg(filePath).arg(currentFcnAddr, 0, 16));
} }
void DisassemblerGraphView::mousePressEvent(QMouseEvent *event) void DisassemblerGraphView::mousePressEvent(QMouseEvent *event)
{ {
GraphView::mousePressEvent(event); GraphView::mousePressEvent(event);
if (!event->isAccepted() && event->button() == Qt::RightButton) {
contextMenu->exec(event->globalPos());
event->accept();
}
emit graphMoved(); emit graphMoved();
} }

View File

@ -87,7 +87,8 @@ class DisassemblerGraphView : public GraphView
}; };
public: public:
DisassemblerGraphView(QWidget *parent, CutterSeekable *seekable, MainWindow *mainWindow); DisassemblerGraphView(QWidget *parent, CutterSeekable *seekable, MainWindow *mainWindow,
QList<QAction *> additionalMenuAction);
~DisassemblerGraphView() override; ~DisassemblerGraphView() override;
std::unordered_map<ut64, DisassemblyBlock> disassembly_blocks; std::unordered_map<ut64, DisassemblyBlock> disassembly_blocks;
virtual void drawBlock(QPainter &p, GraphView::GraphBlock &block, bool interactive) override; virtual void drawBlock(QPainter &p, GraphView::GraphBlock &block, bool interactive) override;
@ -146,6 +147,9 @@ protected:
void resizeEvent(QResizeEvent *event) override; void resizeEvent(QResizeEvent *event) override;
void paintEvent(QPaintEvent *event) override; void paintEvent(QPaintEvent *event) override;
void blockContextMenuRequested(GraphView::GraphBlock &block, QContextMenuEvent *event,
QPoint pos) override;
void contextMenuEvent(QContextMenuEvent *event) override;
private slots: private slots:
void on_actionExportGraph_triggered(); void on_actionExportGraph_triggered();
@ -175,7 +179,7 @@ private:
QPoint getTextOffset(int line) const; QPoint getTextOffset(int line) const;
QPoint getInstructionOffset(const DisassemblyBlock &block, int line) const; QPoint getInstructionOffset(const DisassemblyBlock &block, int line) const;
RVA getAddrForMouseEvent(GraphBlock &block, QPoint *point); RVA getAddrForMouseEvent(GraphBlock &block, QPoint *point);
Instr *getInstrForMouseEvent(GraphBlock &block, QPoint *point); Instr *getInstrForMouseEvent(GraphBlock &block, QPoint *point, bool force = false);
/** /**
* @brief Get instructions placement and size relative to block. * @brief Get instructions placement and size relative to block.
* Inefficient don't use this function when iterating over all instructions. * Inefficient don't use this function when iterating over all instructions.
@ -216,7 +220,6 @@ private:
QAction actionExportGraph; QAction actionExportGraph;
QAction actionUnhighlight; QAction actionUnhighlight;
QAction actionSyncOffset;
QLabel *emptyText = nullptr; QLabel *emptyText = nullptr;
signals: signals:

View File

@ -169,9 +169,7 @@ DisassemblyWidget::DisassemblyWidget(MainWindow *main, QAction *action)
connect(mCtxMenu, SIGNAL(copy()), mDisasTextEdit, SLOT(copy())); connect(mCtxMenu, SIGNAL(copy()), mDisasTextEdit, SLOT(copy()));
mCtxMenu->addSeparator(); mCtxMenu->addSeparator();
syncIt.setText(tr("Sync/unsync offset")); mCtxMenu->addAction(&syncAction);
mCtxMenu->addAction(&syncIt);
connect(&syncIt, &QAction::triggered, seekable, &CutterSeekable::toggleSynchronization);
connect(seekable, &CutterSeekable::seekableSeekChanged, this, &DisassemblyWidget::on_seekChanged); connect(seekable, &CutterSeekable::seekableSeekChanged, this, &DisassemblyWidget::on_seekChanged);
addActions(mCtxMenu->actions()); addActions(mCtxMenu->actions());
@ -907,10 +905,11 @@ void DisassemblyLeftPanel::paintEvent(QPaintEvent *event)
} }
const RVA currOffset = disas->getSeekable()->getOffset(); const RVA currOffset = disas->getSeekable()->getOffset();
qreal pixelRatio = qhelpers::devicePixelRatio(p.device());
// Draw the lines // Draw the lines
for (const auto& l : lines) { for (const auto& l : lines) {
int lineOffset = int((distanceBetweenLines * arrowInfo[l.offset].second + distanceBetweenLines) * int lineOffset = int((distanceBetweenLines * arrowInfo[l.offset].second + distanceBetweenLines) *
p.device()->devicePixelRatioF()); pixelRatio);
// Skip until we reach a line that jumps to a destination // Skip until we reach a line that jumps to a destination
if (l.arrow == RVA_INVALID) { if (l.arrow == RVA_INVALID) {
continue; continue;

View File

@ -88,8 +88,6 @@ private:
void connectCursorPositionChanged(bool disconnect); void connectCursorPositionChanged(bool disconnect);
void moveCursorRelative(bool up, bool page); void moveCursorRelative(bool up, bool page);
QAction syncIt;
}; };
class DisassemblyScrollArea : public QAbstractScrollArea class DisassemblyScrollArea : public QAbstractScrollArea

View File

@ -4,6 +4,7 @@
#ifdef CUTTER_ENABLE_GRAPHVIZ #ifdef CUTTER_ENABLE_GRAPHVIZ
#include "GraphvizLayout.h" #include "GraphvizLayout.h"
#endif #endif
#include "Helpers.h"
#include <vector> #include <vector>
#include <QPainter> #include <QPainter>
@ -12,7 +13,7 @@
#include <QPropertyAnimation> #include <QPropertyAnimation>
#include <QSvgGenerator> #include <QSvgGenerator>
#ifndef QT_NO_OPENGL #ifndef CUTTER_NO_OPENGL_GRAPH
#include <QOpenGLContext> #include <QOpenGLContext>
#include <QOpenGLWidget> #include <QOpenGLWidget>
#include <QOpenGLPaintDevice> #include <QOpenGLPaintDevice>
@ -22,12 +23,12 @@
GraphView::GraphView(QWidget *parent) GraphView::GraphView(QWidget *parent)
: QAbstractScrollArea(parent) : QAbstractScrollArea(parent)
, useGL(false) , useGL(false)
#ifndef QT_NO_OPENGL #ifndef CUTTER_NO_OPENGL_GRAPH
, cacheTexture(0) , cacheTexture(0)
, cacheFBO(0) , cacheFBO(0)
#endif #endif
{ {
#ifndef QT_NO_OPENGL #ifndef CUTTER_NO_OPENGL_GRAPH
if (useGL) { if (useGL) {
glWidget = new QOpenGLWidget(this); glWidget = new QOpenGLWidget(this);
setViewport(glWidget); setViewport(glWidget);
@ -76,20 +77,11 @@ void GraphView::blockHelpEvent(GraphView::GraphBlock &block, QHelpEvent *event,
bool GraphView::helpEvent(QHelpEvent *event) bool GraphView::helpEvent(QHelpEvent *event)
{ {
int x = event->pos().x() + offset.x(); auto p = viewToLogicalCoordinates(event->pos());
int y = event->pos().y() - offset.y(); if (auto block = getBlockContaining(p)) {
blockHelpEvent(*block, event, p - QPoint(block->x, block->y));
for (auto &blockIt : blocks) { return true;
GraphBlock &block = blockIt.second;
if ((block.x <= x) && (block.y <= y) &&
(x <= block.x + block.width) & (y <= block.y + block.height)) {
QPoint pos = QPoint(x - block.x, y - block.y);
blockHelpEvent(block, event, pos);
return true;
}
} }
return false; return false;
} }
@ -111,6 +103,10 @@ GraphView::EdgeConfiguration GraphView::edgeConfiguration(GraphView::GraphBlock
return ec; return ec;
} }
void GraphView::blockContextMenuRequested(GraphView::GraphBlock &, QContextMenuEvent *, QPoint)
{
}
bool GraphView::event(QEvent *event) bool GraphView::event(QEvent *event)
{ {
if (event->type() == QEvent::ToolTip) { if (event->type() == QEvent::ToolTip) {
@ -122,6 +118,17 @@ bool GraphView::event(QEvent *event)
return QAbstractScrollArea::event(event); return QAbstractScrollArea::event(event);
} }
void GraphView::contextMenuEvent(QContextMenuEvent *event)
{
event->ignore();
if (event->reason() == QContextMenuEvent::Mouse) {
QPoint p = viewToLogicalCoordinates(event->pos());
if (auto block = getBlockContaining(p)) {
blockContextMenuRequested(*block, event, p);
}
}
}
// This calculates the full graph starting at block entry. // This calculates the full graph starting at block entry.
void GraphView::computeGraph(ut64 entry) void GraphView::computeGraph(ut64 entry)
{ {
@ -154,7 +161,7 @@ void GraphView::setViewScale(qreal scale)
QSize GraphView::getCacheSize() QSize GraphView::getCacheSize()
{ {
return return
#ifndef QT_NO_OPENGL #ifndef CUTTER_NO_OPENGL_GRAPH
useGL ? cacheSize : useGL ? cacheSize :
#endif #endif
pixmap.size(); pixmap.size();
@ -163,33 +170,29 @@ QSize GraphView::getCacheSize()
qreal GraphView::getCacheDevicePixelRatioF() qreal GraphView::getCacheDevicePixelRatioF()
{ {
return return
#ifndef QT_NO_OPENGL #ifndef CUTTER_NO_OPENGL_GRAPH
useGL ? 1.0 : useGL ? 1.0 :
#endif #endif
pixmap.devicePixelRatioF(); qhelpers::devicePixelRatio(&pixmap);
} }
QSize GraphView::getRequiredCacheSize() QSize GraphView::getRequiredCacheSize()
{ {
return return viewport()->size() * getRequiredCacheDevicePixelRatioF();
#ifndef QT_NO_OPENGL
useGL ? viewport()->size() :
#endif
viewport()->size() * devicePixelRatioF();
} }
qreal GraphView::getRequiredCacheDevicePixelRatioF() qreal GraphView::getRequiredCacheDevicePixelRatioF()
{ {
return return
#ifndef QT_NO_OPENGL #ifndef CUTTER_NO_OPENGL_GRAPH
useGL ? 1.0f : useGL ? 1.0f :
#endif #endif
devicePixelRatioF(); qhelpers::devicePixelRatio(this);
} }
void GraphView::paintEvent(QPaintEvent *) void GraphView::paintEvent(QPaintEvent *)
{ {
#ifndef QT_NO_OPENGL #ifndef CUTTER_NO_OPENGL_GRAPH
if (useGL) { if (useGL) {
glWidget->makeCurrent(); glWidget->makeCurrent();
} }
@ -206,7 +209,7 @@ void GraphView::paintEvent(QPaintEvent *)
} }
if (useGL) { if (useGL) {
#ifndef QT_NO_OPENGL #ifndef CUTTER_NO_OPENGL_GRAPH
auto gl = glWidget->context()->extraFunctions(); auto gl = glWidget->context()->extraFunctions();
gl->glBindFramebuffer(GL_READ_FRAMEBUFFER, cacheFBO); gl->glBindFramebuffer(GL_READ_FRAMEBUFFER, cacheFBO);
gl->glBindFramebuffer(GL_DRAW_FRAMEBUFFER, glWidget->defaultFramebufferObject()); gl->glBindFramebuffer(GL_DRAW_FRAMEBUFFER, glWidget->defaultFramebufferObject());
@ -249,12 +252,12 @@ void GraphView::addViewOffset(QPoint move, bool emitSignal)
void GraphView::paintGraphCache() void GraphView::paintGraphCache()
{ {
#ifndef QT_NO_OPENGL #ifndef CUTTER_NO_OPENGL_GRAPH
QOpenGLPaintDevice *paintDevice = nullptr; QOpenGLPaintDevice *paintDevice = nullptr;
#endif #endif
QPainter p; QPainter p;
if (useGL) { if (useGL) {
#ifndef QT_NO_OPENGL #ifndef CUTTER_NO_OPENGL_GRAPH
auto gl = QOpenGLContext::currentContext()->functions(); auto gl = QOpenGLContext::currentContext()->functions();
bool resizeTex = false; bool resizeTex = false;
@ -289,7 +292,7 @@ void GraphView::paintGraphCache()
p.begin(paintDevice); p.begin(paintDevice);
#endif #endif
} else { } else {
auto dpr = devicePixelRatioF(); auto dpr = qhelpers::devicePixelRatio(this);
pixmap = QPixmap(getRequiredCacheSize()); pixmap = QPixmap(getRequiredCacheSize());
pixmap.setDevicePixelRatio(dpr); pixmap.setDevicePixelRatio(dpr);
p.begin(&pixmap); p.begin(&pixmap);
@ -299,7 +302,7 @@ void GraphView::paintGraphCache()
paint(p, offset, this->viewport()->rect(), current_scale); paint(p, offset, this->viewport()->rect(), current_scale);
p.end(); p.end();
#ifndef QT_NO_OPENGL #ifndef CUTTER_NO_OPENGL_GRAPH
delete paintDevice; delete paintDevice;
#endif #endif
} }
@ -488,6 +491,25 @@ void GraphView::showRectangle(const QRect &block, bool anywhere)
viewport()->update(); viewport()->update();
} }
GraphView::GraphBlock *GraphView::getBlockContaining(QPoint p)
{
// Check if a block was clicked
for (auto &blockIt : blocks) {
GraphBlock &block = blockIt.second;
QRect rec(block.x, block.y, block.width, block.height);
if (rec.contains(p)) {
return &block;
}
}
return nullptr;
}
QPoint GraphView::viewToLogicalCoordinates(QPoint p)
{
return p / current_scale + offset;
}
void GraphView::setGraphLayout(GraphView::Layout layout) void GraphView::setGraphLayout(GraphView::Layout layout)
{ {
graphLayout = layout; graphLayout = layout;
@ -550,21 +572,14 @@ void GraphView::mousePressEvent(QMouseEvent *event)
return; return;
} }
int x = event->pos().x() / current_scale + offset.x(); QPoint pos = viewToLogicalCoordinates(event->pos());
int y = event->pos().y() / current_scale + offset.y();
// Check if a block was clicked // Check if a block was clicked
for (auto &blockIt : blocks) { if (auto block = getBlockContaining(pos)) {
GraphBlock &block = blockIt.second; blockClicked(*block, event, pos - QPoint(block->x, block->y));
// Don't do anything else here! blockClicked might seek and
if ((block.x <= x) && (block.y <= y) && // all our data is invalid then.
(x <= block.x + block.width) & (y <= block.y + block.height)) { return;
QPoint pos = QPoint(x - block.x, y - block.y);
blockClicked(block, event, pos);
// Don't do anything else here! blockClicked might seek and
// all our data is invalid then.
return;
}
} }
// Check if a line beginning/end was clicked // Check if a line beginning/end was clicked
@ -577,13 +592,13 @@ void GraphView::mousePressEvent(QMouseEvent *event)
} }
QPointF start = edge.polyline.first(); QPointF start = edge.polyline.first();
QPointF end = edge.polyline.last(); QPointF end = edge.polyline.last();
if (checkPointClicked(start, x, y)) { if (checkPointClicked(start, pos.x(), pos.y())) {
showBlock(blocks[edge.target]); showBlock(blocks[edge.target]);
// TODO: Callback to child // TODO: Callback to child
return; return;
break; break;
} }
if (checkPointClicked(end, x, y, true)) { if (checkPointClicked(end, pos.x(), pos.y(), true)) {
showBlock(block); showBlock(block);
// TODO: Callback to child // TODO: Callback to child
return; return;
@ -615,19 +630,9 @@ void GraphView::mouseMoveEvent(QMouseEvent *event)
void GraphView::mouseDoubleClickEvent(QMouseEvent *event) void GraphView::mouseDoubleClickEvent(QMouseEvent *event)
{ {
int x = event->pos().x() / current_scale + offset.x(); auto p = viewToLogicalCoordinates(event->pos());
int y = event->pos().y() / current_scale + offset.y(); if (auto block = getBlockContaining(p)) {
blockDoubleClicked(*block, event, p - QPoint(block->x, block->y));
// Check if a block was clicked
for (auto &blockIt : blocks) {
GraphBlock &block = blockIt.second;
if ((block.x <= x) && (block.y <= y) &&
(x <= block.x + block.width) & (y <= block.y + block.height)) {
QPoint pos = QPoint(x - block.x, y - block.y);
blockDoubleClicked(block, event, pos);
return;
}
} }
} }

View File

@ -17,7 +17,12 @@
#include "core/Cutter.h" #include "core/Cutter.h"
#include "widgets/GraphLayout.h" #include "widgets/GraphLayout.h"
#ifndef QT_NO_OPENGL #if defined(QT_NO_OPENGL) || QT_VERSION < QT_VERSION_CHECK(5, 6, 0)
// QOpenGLExtraFunctions were introduced in 5.6
#define CUTTER_NO_OPENGL_GRAPH
#endif
#ifndef CUTTER_NO_OPENGL_GRAPH
class QOpenGLWidget; class QOpenGLWidget;
#endif #endif
@ -64,6 +69,13 @@ public:
* @param anywhere - set to true for minimizing movement * @param anywhere - set to true for minimizing movement
*/ */
void showRectangle(const QRect &rect, bool anywhere = false); void showRectangle(const QRect &rect, bool anywhere = false);
/**
* @brief Get block containing specified point logical coordinates.
* @param p positionin graph logical coordinates
* @return Block or nullptr if position is outside all blocks.
*/
GraphView::GraphBlock *getBlockContaining(QPoint p);
QPoint viewToLogicalCoordinates(QPoint p);
void setGraphLayout(Layout layout); void setGraphLayout(Layout layout);
Layout getGraphLayout() const { return graphLayout; } Layout getGraphLayout() const { return graphLayout; }
@ -101,8 +113,11 @@ protected:
virtual void wheelEvent(QWheelEvent *event) override; virtual void wheelEvent(QWheelEvent *event) override;
virtual EdgeConfiguration edgeConfiguration(GraphView::GraphBlock &from, GraphView::GraphBlock *to, virtual EdgeConfiguration edgeConfiguration(GraphView::GraphBlock &from, GraphView::GraphBlock *to,
bool interactive = true); bool interactive = true);
virtual void blockContextMenuRequested(GraphView::GraphBlock &block, QContextMenuEvent *event,
QPoint pos);
bool event(QEvent *event) override; bool event(QEvent *event) override;
void contextMenuEvent(QContextMenuEvent *event) override;
// Mouse events // Mouse events
void mousePressEvent(QMouseEvent *event) override; void mousePressEvent(QMouseEvent *event) override;
@ -153,7 +168,7 @@ private:
*/ */
QPixmap pixmap; QPixmap pixmap;
#ifndef QT_NO_OPENGL #ifndef CUTTER_NO_OPENGL_GRAPH
uint32_t cacheTexture; uint32_t cacheTexture;
uint32_t cacheFBO; uint32_t cacheFBO;
QSize cacheSize; QSize cacheSize;

View File

@ -23,7 +23,7 @@ GraphWidget::GraphWidget(MainWindow *main, QAction *action) :
header->setReadOnly(true); header->setReadOnly(true);
layout->addWidget(header); layout->addWidget(header);
graphView = new DisassemblerGraphView(layoutWidget, seekable, main); graphView = new DisassemblerGraphView(layoutWidget, seekable, main, {&syncAction});
layout->addWidget(graphView); layout->addWidget(graphView);
// Title needs to get set after graphView is defined // Title needs to get set after graphView is defined

View File

@ -194,9 +194,9 @@ void GraphvizLayout::CalculateLayout(std::unordered_map<ut64, GraphBlock> &block
if (edge.target == block.entry && edge.polyline.first().y() < edge.polyline.last().y()) { if (edge.target == block.entry && edge.polyline.first().y() < edge.polyline.last().y()) {
std::reverse(edge.polyline.begin(), edge.polyline.end()); std::reverse(edge.polyline.begin(), edge.polyline.end());
} }
auto it = edge.polyline.rbegin(); auto it = std::prev(edge.polyline.end());
QPointF direction = *it; QPointF direction = *it;
direction -= *(++it); direction -= *(--it);
edge.arrow = getArrowDirection(direction, lineType == LineType::Polyline); edge.arrow = getArrowDirection(direction, lineType == LineType::Polyline);
} else { } else {

View File

@ -817,7 +817,7 @@ void HexWidget::fillSelectionBackground(QPainter &painter, bool ascii)
return; return;
} }
const auto parts = rangePolygons(selection.start(), selection.end(), ascii); const auto parts = rangePolygons(selection.start(), selection.end(), ascii);
for (const auto &shape : qAsConst(parts)) { for (const auto &shape : parts) {
QColor highlightColor = palette().color(QPalette::Highlight); QColor highlightColor = palette().color(QPalette::Highlight);
if (ascii == cursorOnAscii) { if (ascii == cursorOnAscii) {
painter.setBrush(highlightColor); painter.setBrush(highlightColor);
@ -1060,6 +1060,30 @@ const QColor HexWidget::itemColor(uint8_t byte)
return color; return color;
} }
template<class T>
static T fromBigEndian(const void * src)
{
#if QT_VERSION >= QT_VERSION_CHECK(5, 12, 0)
return qFromBigEndian<T>(src);
#else
T result;
memcpy(&result, src, sizeof(T));
return qFromBigEndian<T>(result);
#endif
}
template<class T>
static T fromLittleEndian(const void * src)
{
#if QT_VERSION >= QT_VERSION_CHECK(5, 12, 0)
return qFromLittleEndian<T>(src);
#else
T result;
memcpy(&result, src, sizeof(T));
return qFromLittleEndian<T>(result);
#endif
}
QVariant HexWidget::readItem(int offset, QColor *color) QVariant HexWidget::readItem(int offset, QColor *color)
{ {
quint8 byte; quint8 byte;
@ -1082,9 +1106,9 @@ QVariant HexWidget::readItem(int offset, QColor *color)
return QVariant(static_cast<qint64>(static_cast<qint8>(byte))); return QVariant(static_cast<qint64>(static_cast<qint8>(byte)));
case 2: case 2:
if (itemBigEndian) if (itemBigEndian)
word = qFromBigEndian<quint16>(dataPtr); word = fromBigEndian<quint16>(dataPtr);
else else
word = qFromLittleEndian<quint16>(dataPtr); word = fromLittleEndian<quint16>(dataPtr);
if (color) if (color)
*color = defColor; *color = defColor;
if (!signedItem) if (!signedItem)
@ -1092,9 +1116,9 @@ QVariant HexWidget::readItem(int offset, QColor *color)
return QVariant(static_cast<qint64>(static_cast<qint16>(word))); return QVariant(static_cast<qint64>(static_cast<qint16>(word)));
case 4: case 4:
if (itemBigEndian) if (itemBigEndian)
dword = qFromBigEndian<quint32>(dataPtr); dword = fromBigEndian<quint32>(dataPtr);
else else
dword = qFromLittleEndian<quint32>(dataPtr); dword = fromLittleEndian<quint32>(dataPtr);
if (color) if (color)
*color = defColor; *color = defColor;
if (itemFormat == ItemFormatFloat) { if (itemFormat == ItemFormatFloat) {
@ -1106,9 +1130,9 @@ QVariant HexWidget::readItem(int offset, QColor *color)
return QVariant(static_cast<qint64>(static_cast<qint32>(dword))); return QVariant(static_cast<qint64>(static_cast<qint32>(dword)));
case 8: case 8:
if (itemBigEndian) if (itemBigEndian)
qword = qFromBigEndian<quint64>(dataPtr); qword = fromBigEndian<quint64>(dataPtr);
else else
qword = qFromLittleEndian<quint64>(dataPtr); qword = fromLittleEndian<quint64>(dataPtr);
if (color) if (color)
*color = defColor; *color = defColor;
if (itemFormat == ItemFormatFloat) { if (itemFormat == ItemFormatFloat) {

View File

@ -73,8 +73,6 @@ HexdumpWidget::HexdumpWidget(MainWindow *main, QAction *action) :
refresh(offset ? *offset : RVA_INVALID); refresh(offset ? *offset : RVA_INVALID);
}); });
connect(&syncAction, &QAction::triggered, seekable, &CutterSeekable::toggleSynchronization);
syncAction.setText(tr("Sync/unsync offset"));
this->ui->hexTextView->addAction(&syncAction); this->ui->hexTextView->addAction(&syncAction);
connect(Config(), SIGNAL(fontsUpdated()), this, SLOT(fontsUpdated())); connect(Config(), SIGNAL(fontsUpdated()), this, SLOT(fontsUpdated()));

View File

@ -62,8 +62,6 @@ private:
QString getWindowTitle() const override; QString getWindowTitle() const override;
QAction syncAction;
private slots: private slots:
void onSeekChanged(RVA addr); void onSeekChanged(RVA addr);

View File

@ -3,15 +3,25 @@
#include "MainWindow.h" #include "MainWindow.h"
#include <QAction> #include <QAction>
#include <QEvent> #include <QEvent>
#include <QMenu>
#include <QContextMenuEvent>
MemoryDockWidget::MemoryDockWidget(MemoryWidgetType type, MainWindow *parent, QAction *action) MemoryDockWidget::MemoryDockWidget(MemoryWidgetType type, MainWindow *parent, QAction *action)
: CutterDockWidget(parent, action) : CutterDockWidget(parent, action)
, mType(type), seekable(new CutterSeekable(this)) , mType(type)
, seekable(new CutterSeekable(this))
, syncAction(tr("Sync/unsync offset"), this)
{ {
if (parent) { if (parent) {
parent->addMemoryDockWidget(this); parent->addMemoryDockWidget(this);
} }
connect(seekable, &CutterSeekable::syncChanged, this, &MemoryDockWidget::updateWindowTitle); connect(seekable, &CutterSeekable::syncChanged, this, &MemoryDockWidget::updateWindowTitle);
connect(&syncAction, &QAction::triggered, seekable, &CutterSeekable::toggleSynchronization);
dockMenu = new QMenu(this);
dockMenu->addAction(&syncAction);
setContextMenuPolicy(Qt::ContextMenuPolicy::DefaultContextMenu);
} }
bool MemoryDockWidget::tryRaiseMemoryWidget() bool MemoryDockWidget::tryRaiseMemoryWidget()
@ -61,7 +71,13 @@ void MemoryDockWidget::updateWindowTitle()
setWindowTitle(name); setWindowTitle(name);
} }
CutterSeekable* MemoryDockWidget::getSeekable() const void MemoryDockWidget::contextMenuEvent(QContextMenuEvent *event)
{
event->accept();
dockMenu->exec(mapToGlobal(event->pos()));
}
CutterSeekable *MemoryDockWidget::getSeekable() const
{ {
return seekable; return seekable;
} }

View File

@ -4,6 +4,8 @@
#include "CutterDockWidget.h" #include "CutterDockWidget.h"
#include "core/Cutter.h" #include "core/Cutter.h"
#include <QAction>
class CutterSeekable; class CutterSeekable;
/* Disassembly/Graph/Hexdump/Decompiler view priority */ /* Disassembly/Graph/Hexdump/Decompiler view priority */
@ -14,9 +16,9 @@ class MemoryDockWidget : public CutterDockWidget
Q_OBJECT Q_OBJECT
public: public:
MemoryDockWidget(MemoryWidgetType type, MainWindow *parent, QAction *action = nullptr); MemoryDockWidget(MemoryWidgetType type, MainWindow *parent, QAction *action = nullptr);
~MemoryDockWidget() {} ~MemoryDockWidget() override {}
CutterSeekable* getSeekable() const; CutterSeekable *getSeekable() const;
bool tryRaiseMemoryWidget(); bool tryRaiseMemoryWidget();
void raiseMemoryWidget(); void raiseMemoryWidget();
@ -24,7 +26,7 @@ public:
{ {
return mType; return mType;
} }
bool eventFilter(QObject *object, QEvent *event); bool eventFilter(QObject *object, QEvent *event) override;
private: private:
MemoryWidgetType mType; MemoryWidgetType mType;
@ -34,8 +36,11 @@ public slots:
protected: protected:
CutterSeekable *seekable = nullptr; CutterSeekable *seekable = nullptr;
QAction syncAction;
QMenu *dockMenu = nullptr;
virtual QString getWindowTitle() const = 0; virtual QString getWindowTitle() const = 0;
void contextMenuEvent(QContextMenuEvent *event) override;
}; };
#endif // MEMORYDOCKWIDGET_H #endif // MEMORYDOCKWIDGET_H

View File

@ -196,8 +196,8 @@ SearchWidget::SearchWidget(MainWindow *main, QAction *action) :
refreshSearch(); refreshSearch();
}); });
connect(ui->searchspaceCombo, QOverload<int>::of(&QComboBox::currentIndexChanged), connect(ui->searchspaceCombo, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged),
[ = ](int index) { updatePlaceholderText(index);}); this, [this](int index) { updatePlaceholderText(index);});
QString currentSearchBoundary = Core()->getConfig("search.in"); QString currentSearchBoundary = Core()->getConfig("search.in");
ui->searchInCombo->setCurrentIndex(ui->searchInCombo->findData(currentSearchBoundary)); ui->searchInCombo->setCurrentIndex(ui->searchInCombo->findData(currentSearchBoundary));

View File

@ -8,23 +8,15 @@
#include "QHeaderView" #include "QHeaderView"
#include "QMenu" #include "QMenu"
enum ColumnIndex {
COLUMN_OFFSET = 0,
COLUMN_VALUE,
COLUMN_DESCRIPTION
};
StackWidget::StackWidget(MainWindow *main, QAction *action) : StackWidget::StackWidget(MainWindow *main, QAction *action) :
CutterDockWidget(main, action), CutterDockWidget(main, action),
ui(new Ui::StackWidget), ui(new Ui::StackWidget),
menuText(this),
addressableItemContextMenu(this, main) addressableItemContextMenu(this, main)
{ {
ui->setupUi(this); ui->setupUi(this);
// Setup stack model // Setup stack model
modelStack->setHorizontalHeaderItem(COLUMN_OFFSET, new QStandardItem(tr("Offset")));
modelStack->setHorizontalHeaderItem(COLUMN_VALUE, new QStandardItem(tr("Value")));
modelStack->setHorizontalHeaderItem(COLUMN_DESCRIPTION, new QStandardItem(tr("Reference")));
viewStack->setFont(Config()->getFont()); viewStack->setFont(Config()->getFont());
viewStack->setModel(modelStack); viewStack->setModel(modelStack);
viewStack->verticalHeader()->hide(); viewStack->verticalHeader()->hide();
@ -51,7 +43,6 @@ StackWidget::StackWidget(MainWindow *main, QAction *action) :
connect(editAction, &QAction::triggered, this, &StackWidget::editStack); connect(editAction, &QAction::triggered, this, &StackWidget::editStack);
connect(viewStack->selectionModel(), &QItemSelectionModel::currentChanged, connect(viewStack->selectionModel(), &QItemSelectionModel::currentChanged,
this, &StackWidget::onCurrentChanged); this, &StackWidget::onCurrentChanged);
connect(modelStack, &QStandardItemModel::itemChanged, this, &StackWidget::onItemChanged);
addressableItemContextMenu.addAction(editAction); addressableItemContextMenu.addAction(editAction);
addActions(addressableItemContextMenu.actions()); addActions(addressableItemContextMenu.actions());
@ -73,47 +64,8 @@ void StackWidget::updateContents()
void StackWidget::setStackGrid() void StackWidget::setStackGrid()
{ {
updatingData = true; modelStack->reload();
QJsonArray stackValues = Core()->getStack().array();
int i = 0;
for (const QJsonValue &value : stackValues) {
QJsonObject stackItem = value.toObject();
QString addr = RAddressString(stackItem["addr"].toVariant().toULongLong());
QString valueStack = RAddressString(stackItem["value"].toVariant().toULongLong());
QStandardItem *rowOffset = new QStandardItem(addr);
rowOffset->setEditable(false);
QStandardItem *rowValue = new QStandardItem(valueStack);
modelStack->setItem(i, COLUMN_OFFSET, rowOffset);
modelStack->setItem(i, COLUMN_VALUE, rowValue);
QJsonValue refObject = stackItem["ref"];
if (!refObject.isUndefined()) { // check that the key exists
QString ref = refObject.toString();
if (ref.contains("ascii") && ref.count("-->") == 1) {
ref = Core()->cmd("psz @ [" + addr + "]");
}
QStandardItem *rowRef = new QStandardItem(ref);
rowRef->setEditable(false);
QModelIndex cell = modelStack->index(i, COLUMN_DESCRIPTION);
modelStack->setItem(i, COLUMN_DESCRIPTION, rowRef);
if (refObject.toString().contains("ascii") && refObject.toString().count("-->") == 1) {
modelStack->setData(cell, QVariant(QColor(243, 156, 17)),
Qt::ForegroundRole); // orange
} else if (ref.contains("program R X") && ref.count("-->") == 0) {
modelStack->setData(cell, QVariant(QColor(Qt::red)),
Qt::ForegroundRole);
} else if (ref.contains("stack") && ref.count("-->") == 0) {
modelStack->setData(cell, QVariant(QColor(Qt::cyan)),
Qt::ForegroundRole);
} else if (ref.contains("library") && ref.count("-->") == 0) {
modelStack->setData(cell, QVariant(QColor(Qt::green)),
Qt::ForegroundRole);
}
}
i++;
}
viewStack->setModel(modelStack);
viewStack->resizeColumnsToContents(); viewStack->resizeColumnsToContents();
updatingData = false;
} }
void StackWidget::fontsUpdatedSlot() void StackWidget::fontsUpdatedSlot()
@ -127,10 +79,10 @@ void StackWidget::onDoubleClicked(const QModelIndex &index)
return; return;
// Check if we are clicking on the offset or value columns and seek if it is the case // Check if we are clicking on the offset or value columns and seek if it is the case
int column = index.column(); int column = index.column();
if (column <= COLUMN_VALUE) { if (column <= StackModel::ValueColumn) {
QString item = index.data().toString(); QString item = index.data().toString();
Core()->seek(item); Core()->seek(item);
if (column == COLUMN_OFFSET) { if (column == StackModel::OffsetColumn) {
mainWindow->showMemoryWidget(MemoryWidgetType::Hexdump); mainWindow->showMemoryWidget(MemoryWidgetType::Hexdump);
} else { } else {
Core()->showMemoryWidget(); Core()->showMemoryWidget();
@ -148,11 +100,11 @@ void StackWidget::editStack()
bool ok; bool ok;
int row = viewStack->selectionModel()->currentIndex().row(); int row = viewStack->selectionModel()->currentIndex().row();
auto model = viewStack->model(); auto model = viewStack->model();
QString offset = model->index(row, COLUMN_OFFSET).data().toString(); QString offset = model->index(row, StackModel::OffsetColumn).data().toString();
EditInstructionDialog e(EDIT_NONE, this); EditInstructionDialog e(EDIT_NONE, this);
e.setWindowTitle(tr("Edit stack at %1").arg(offset)); e.setWindowTitle(tr("Edit stack at %1").arg(offset));
QString oldBytes = model->index(row, COLUMN_VALUE).data().toString(); QString oldBytes = model->index(row, StackModel::ValueColumn).data().toString();
e.setInstruction(oldBytes); e.setInstruction(oldBytes);
if (e.exec()) { if (e.exec()) {
@ -169,38 +121,149 @@ void StackWidget::onCurrentChanged(const QModelIndex &current, const QModelIndex
Q_UNUSED(previous) Q_UNUSED(previous)
auto currentIndex = viewStack->selectionModel()->currentIndex(); auto currentIndex = viewStack->selectionModel()->currentIndex();
QString offsetString; QString offsetString;
if (currentIndex.column() != COLUMN_DESCRIPTION) { if (currentIndex.column() != StackModel::DescriptionColumn) {
offsetString = currentIndex.data().toString(); offsetString = currentIndex.data().toString();
} else { } else {
offsetString = currentIndex.sibling(currentIndex.row(), COLUMN_VALUE).data().toString(); offsetString = currentIndex.sibling(currentIndex.row(), StackModel::ValueColumn).data().toString();
} }
RVA offset = Core()->math(offsetString); RVA offset = Core()->math(offsetString);
addressableItemContextMenu.setTarget(offset); addressableItemContextMenu.setTarget(offset);
if (currentIndex.column() == COLUMN_OFFSET) { if (currentIndex.column() == StackModel::OffsetColumn) {
menuText.setText(tr("Stack position")); menuText.setText(tr("Stack position"));
} else { } else {
menuText.setText(tr("Pointed memory")); menuText.setText(tr("Pointed memory"));
} }
} }
void StackWidget::onItemChanged(QStandardItem *item) StackModel::StackModel(QObject *parent)
: QAbstractTableModel(parent)
{ {
if (updatingData || item->column() != COLUMN_VALUE) { }
return;
} void StackModel::reload()
QModelIndex index = item->index(); {
int row = item->row(); QJsonArray stackValues = Core()->getStack().array();
QString text = item->text();
// Queue the update instead of performing immediately. Editing will trigger reload. beginResetModel();
// Performing reload while itemChanged signal is on stack would result values.clear();
// in itemView getting stuck in EditingState and preventing further edits. for (const QJsonValue &value : stackValues) {
QMetaObject::invokeMethod(this, [this, index, row, text]() { QJsonObject stackItem = value.toObject();
QString offsetString = index.sibling(row, COLUMN_OFFSET).data().toString(); Item item;
bool ok = false;
auto offset = offsetString.toULongLong(&ok, 16); item.offset = stackItem["addr"].toVariant().toULongLong();
if (ok) { item.value = RAddressString(stackItem["value"].toVariant().toULongLong());
Core()->editBytesEndian(offset, text);
}
}, Qt::QueuedConnection); QJsonValue refObject = stackItem["ref"];
if (!refObject.isUndefined()) { // check that the key exists
QString ref = refObject.toString();
if (ref.contains("ascii") && ref.count("-->") == 1) {
ref = Core()->cmd(QString("psz @ [%1]").arg(item.offset));
}
item.description = ref;
if (refObject.toString().contains("ascii") && refObject.toString().count("-->") == 1) {
item.descriptionColor = QVariant(QColor(243, 156, 17));
} else if (ref.contains("program R X") && ref.count("-->") == 0) {
item.descriptionColor = QVariant(QColor(Qt::red));
} else if (ref.contains("stack") && ref.count("-->") == 0) {
item.descriptionColor = QVariant(QColor(Qt::cyan));
} else if (ref.contains("library") && ref.count("-->") == 0) {
item.descriptionColor = QVariant(QColor(Qt::green));
}
}
values.push_back(item);
}
endResetModel();
}
int StackModel::rowCount(const QModelIndex &) const
{
return this->values.size();
}
int StackModel::columnCount(const QModelIndex &) const
{
return ColumnCount;
}
QVariant StackModel::data(const QModelIndex &index, int role) const
{
if (!index.isValid() || index.row() >= values.count())
return QVariant();
const auto &item = values.at(index.row());
switch (role) {
case Qt::DisplayRole:
switch (index.column()) {
case OffsetColumn:
return RAddressString(item.offset);
case ValueColumn:
return item.value;
case DescriptionColumn:
return item.description;
default:
return QVariant();
}
case Qt::ForegroundRole:
switch (index.column()) {
case DescriptionColumn:
return item.descriptionColor;
default:
return QVariant();
}
case StackDescriptionRole:
return QVariant::fromValue(item);
default:
return QVariant();
}
}
QVariant StackModel::headerData(int section, Qt::Orientation orientation, int role) const
{
Q_UNUSED(orientation);
switch (role) {
case Qt::DisplayRole:
switch (section) {
case OffsetColumn:
return tr("Offset");
case ValueColumn:
return tr("Value");
case DescriptionColumn:
return tr("Reference");
default:
return QVariant();
}
default:
return QVariant();
}
}
bool StackModel::setData(const QModelIndex &index, const QVariant &value, int role)
{
if (role != Qt::EditRole || index.column() != ValueColumn) {
return false;
}
auto currentData = data(index, StackDescriptionRole);
if (!currentData.canConvert<StackModel::Item>()) {
return false;
}
auto currentItem = currentData.value<StackModel::Item>();
Core()->editBytesEndian(currentItem.offset, value.toString());
return true;
}
Qt::ItemFlags StackModel::flags(const QModelIndex &index) const
{
switch (index.column()) {
case ValueColumn:
return QAbstractTableModel::flags(index) | Qt::ItemIsEditable;
default:
return QAbstractTableModel::flags(index);
}
} }

View File

@ -15,6 +15,39 @@ namespace Ui {
class StackWidget; class StackWidget;
} }
class StackModel : public QAbstractTableModel
{
Q_OBJECT
public:
struct Item {
RVA offset;
QString value;
QString description;
QVariant descriptionColor;
};
enum Column { OffsetColumn = 0, ValueColumn, DescriptionColumn, ColumnCount};
enum Role { StackDescriptionRole = Qt::UserRole };
StackModel(QObject *parent = nullptr);
void reload();
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
int columnCount(const QModelIndex &parent = QModelIndex()) const override;
QVariant data(const QModelIndex &index, int role) const override;
QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override;
bool setData(const QModelIndex &index, const QVariant &value, int role) override;
Qt::ItemFlags flags(const QModelIndex &index) const override;
private:
QVector<Item> values;
};
Q_DECLARE_METATYPE(StackModel::Item)
class StackWidget : public CutterDockWidget class StackWidget : public CutterDockWidget
{ {
Q_OBJECT Q_OBJECT
@ -31,15 +64,13 @@ private slots:
void customMenuRequested(QPoint pos); void customMenuRequested(QPoint pos);
void editStack(); void editStack();
void onCurrentChanged(const QModelIndex &current, const QModelIndex &previous); void onCurrentChanged(const QModelIndex &current, const QModelIndex &previous);
void onItemChanged(QStandardItem *item);
private: private:
std::unique_ptr<Ui::StackWidget> ui; std::unique_ptr<Ui::StackWidget> ui;
QTableView *viewStack = new QTableView; QTableView *viewStack = new QTableView(this);
QStandardItemModel *modelStack = new QStandardItemModel(1, 3, this); StackModel *modelStack = new StackModel(this);
QAction *editAction; QAction *editAction;
QAction menuText; QAction menuText;
RefreshDeferrer *refreshDeferrer; RefreshDeferrer *refreshDeferrer;
AddressableItemContextMenu addressableItemContextMenu; AddressableItemContextMenu addressableItemContextMenu;
bool updatingData = false;
}; };

View File

@ -240,7 +240,7 @@ void StringsWidget::stringSearchFinished(const QList<StringDescription> &strings
tree->showItemsNumber(proxyModel->rowCount()); tree->showItemsNumber(proxyModel->rowCount());
task = nullptr; task.clear();
} }
void StringsWidget::on_actionCopy() void StringsWidget::on_actionCopy()