mirror of
https://github.com/rizinorg/cutter.git
synced 2025-01-18 18:38:51 +00:00
Improve compatibility with cmake 3.5 and QT 5.5-5.11. (#1805)
This commit is contained in:
parent
cbd8984ca7
commit
b11ba240d2
30
.travis.yml
30
.travis.yml
@ -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
|
||||||
|
@ -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
|
||||||
~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~
|
||||||
|
@ -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()
|
||||||
|
|
||||||
|
@ -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();
|
||||||
|
@ -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")
|
||||||
|
25
src/cmake/FindGraphviz.cmake
Normal file
25
src/cmake/FindGraphviz.cmake
Normal 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()
|
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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"
|
||||||
|
@ -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()
|
||||||
|
@ -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)
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
|
@ -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());
|
||||||
|
@ -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();
|
||||||
|
@ -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 {
|
||||||
|
|
||||||
|
@ -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());
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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()) {
|
||||||
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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:
|
||||||
|
@ -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;
|
||||||
|
@ -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
|
||||||
|
@ -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 █
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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;
|
||||||
|
@ -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
|
||||||
|
@ -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 {
|
||||||
|
@ -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) {
|
||||||
|
@ -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()));
|
||||||
|
@ -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);
|
||||||
|
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
@ -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));
|
||||||
|
@ -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 ¤t, 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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -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 ¤t, const QModelIndex &previous);
|
void onCurrentChanged(const QModelIndex ¤t, 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;
|
|
||||||
};
|
};
|
||||||
|
@ -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()
|
||||||
|
Loading…
Reference in New Issue
Block a user