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
|
||||
- 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
|
||||
os: osx
|
||||
osx_image: xcode10.3
|
||||
@ -82,8 +100,10 @@ addons:
|
||||
brewfile: scripts/Brewfile
|
||||
|
||||
install:
|
||||
- scripts/fetch_deps.sh
|
||||
- source cutter-deps/env.sh
|
||||
- if [[ "$BUILD_SYSTEM" != "cmake_nodep" ]]; then
|
||||
scripts/fetch_deps.sh;
|
||||
source cutter-deps/env.sh;
|
||||
fi
|
||||
- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then
|
||||
export PATH=/usr/local/opt/llvm/bin:$PATH;
|
||||
source scripts/prepare_breakpad_macos.sh;
|
||||
@ -131,6 +151,12 @@ script:
|
||||
-DCUTTER_USE_BUNDLED_RADARE2=ON
|
||||
../src &&
|
||||
make -j4;
|
||||
elif [[ "$BUILD_SYSTEM" == "cmake_nodep" ]]; then
|
||||
cmake
|
||||
-DCMAKE_BUILD_TYPE=Release
|
||||
-DCUTTER_USE_BUNDLED_RADARE2=ON
|
||||
../src &&
|
||||
make -j4;
|
||||
fi
|
||||
elif [[ "$TRAVIS_OS_NAME" == "osx" ]]; then
|
||||
if [[ "$BUILD_SYSTEM" == "qmake" ]]; then
|
||||
|
@ -13,7 +13,7 @@ alternatives – cmake and meson.
|
||||
In any case, there are obviously some requirements:
|
||||
|
||||
* Radare2 installed from submodule
|
||||
* Qt 5.9 or above
|
||||
* Qt 5.9 or above (can compile using 5.5 with some limitations)
|
||||
* Python3.6
|
||||
* 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_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_USE_BUNDLED_RADARE2`` CMake only, automatically compile Radare2 from submodule.
|
||||
|
||||
--------------
|
||||
|
||||
@ -91,7 +92,7 @@ Building with Cmake
|
||||
Requirements
|
||||
~~~~~~~~~~~~
|
||||
|
||||
- CMake >= 3.1
|
||||
- CMake >= 3.5 (recommended >= 3.6)
|
||||
|
||||
Building on Linux
|
||||
~~~~~~~~~~~~~~~~~
|
||||
|
@ -1,7 +1,6 @@
|
||||
cmake_minimum_required(VERSION 3.5)
|
||||
|
||||
cmake_minimum_required(VERSION 3.1)
|
||||
|
||||
if(${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.12.0")
|
||||
if(POLICY CMP0074)
|
||||
cmake_policy(SET CMP0074 NEW)
|
||||
endif()
|
||||
|
||||
@ -103,19 +102,13 @@ else()
|
||||
set(KSYNTAXHIGHLIGHTING_STATUS OFF)
|
||||
endif()
|
||||
|
||||
find_package(PkgConfig REQUIRED)
|
||||
|
||||
if (CUTTER_ENABLE_GRAPHVIZ)
|
||||
if (CUTTER_ENABLE_GRAPHVIZ STREQUAL AUTO)
|
||||
pkg_check_modules(GVC IMPORTED_TARGET libgvc)
|
||||
if (GVC_FOUND)
|
||||
set(CUTTER_ENABLE_GRAPHVIZ ON)
|
||||
find_package(Graphviz)
|
||||
else()
|
||||
set(CUTTER_ENABLE_GRAPHVIZ OFF)
|
||||
endif()
|
||||
else()
|
||||
pkg_check_modules(GVC REQUIRED IMPORTED_TARGET libgvc)
|
||||
find_package(Graphviz REQUIRED)
|
||||
endif()
|
||||
set (CUTTER_ENABLE_GRAPHVIZ ${Graphviz_FOUND})
|
||||
endif()
|
||||
|
||||
message(STATUS "")
|
||||
@ -157,7 +150,7 @@ else()
|
||||
endif()
|
||||
|
||||
|
||||
if (CUTTER_ENABLE_GRAPHVIZ)
|
||||
if (TARGET Graphviz::GVC)
|
||||
list(APPEND SOURCE_FILES ${CUTTER_PRO_GRAPHVIZ_SOURCES})
|
||||
list(APPEND HEADER_FILES ${CUTTER_PRO_GRAPHVIZ_HEADERS})
|
||||
endif()
|
||||
@ -173,8 +166,8 @@ endif()
|
||||
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")
|
||||
|
||||
if (CUTTER_ENABLE_GRAPHVIZ)
|
||||
target_link_libraries(Cutter PkgConfig::GVC)
|
||||
if (TARGET Graphviz::GVC)
|
||||
target_link_libraries(Cutter Graphviz::GVC)
|
||||
target_compile_definitions(Cutter PRIVATE CUTTER_ENABLE_GRAPHVIZ)
|
||||
endif()
|
||||
|
||||
|
@ -148,6 +148,7 @@ int main(int argc, char *argv[])
|
||||
migrateThemes();
|
||||
|
||||
if (Config()->getAutoUpdateEnabled()) {
|
||||
#if CUTTER_UPDATE_WORKER_AVAILABLE
|
||||
UpdateWorker *updateWorker = new UpdateWorker;
|
||||
QObject::connect(updateWorker, &UpdateWorker::checkComplete,
|
||||
[=](const QVersionNumber &version, const QString & error) {
|
||||
@ -157,6 +158,7 @@ int main(int argc, char *argv[])
|
||||
updateWorker->deleteLater();
|
||||
});
|
||||
updateWorker->checkCurrentVersion(7000);
|
||||
#endif
|
||||
}
|
||||
|
||||
int ret = a.exec();
|
||||
|
@ -26,7 +26,7 @@ set(Radare2_INCLUDE_DIRS "${RADARE2_INSTALL_DIR}/include/libr")
|
||||
|
||||
add_library(Radare2 INTERFACE)
|
||||
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")
|
||||
else()
|
||||
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);
|
||||
}
|
||||
|
||||
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
|
||||
|
@ -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
|
||||
|
@ -18,6 +18,7 @@ class QWidget;
|
||||
class QTreeView;
|
||||
class QAction;
|
||||
class QMenu;
|
||||
class QPaintDevice;
|
||||
|
||||
namespace qhelpers {
|
||||
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 prependQAction(QAction *action, QMenu *menu);
|
||||
qreal devicePixelRatio(const QPaintDevice *p);
|
||||
|
||||
} // qhelpers
|
||||
|
||||
|
@ -1,5 +1,6 @@
|
||||
#include "UpdateWorker.h"
|
||||
|
||||
#if CUTTER_UPDATE_WORKER_AVAILABLE
|
||||
#include <QUrl>
|
||||
#include <QFile>
|
||||
#include <QTimer>
|
||||
@ -19,6 +20,7 @@
|
||||
#include "common/Configuration.h"
|
||||
#include "CutterConfig.h"
|
||||
|
||||
|
||||
UpdateWorker::UpdateWorker(QObject *parent) :
|
||||
QObject(parent), pending(false)
|
||||
{
|
||||
@ -217,3 +219,4 @@ QVersionNumber UpdateWorker::currentVersionNumber()
|
||||
{
|
||||
return QVersionNumber(CUTTER_VERSION_MAJOR, CUTTER_VERSION_MINOR, CUTTER_VERSION_PATCH);
|
||||
}
|
||||
#endif // CUTTER_UPDATE_WORKER_AVAILABLE
|
||||
|
@ -1,10 +1,21 @@
|
||||
#ifndef 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 <QTimer>
|
||||
#include <QObject>
|
||||
#include <QtNetwork/QNetworkAccessManager>
|
||||
|
||||
#include <QVersionNumber>
|
||||
|
||||
class QNetworkReply;
|
||||
@ -126,4 +137,5 @@ private:
|
||||
QNetworkReply *checkReply;
|
||||
};
|
||||
|
||||
#endif //CUTTER_UPDATE_WORKER_AVAILABLE
|
||||
#endif // UPDATEWORKER_H
|
||||
|
@ -4,6 +4,9 @@
|
||||
#include <QDir>
|
||||
#include <QCoreApplication>
|
||||
|
||||
#include <cassert>
|
||||
#include <memory>
|
||||
|
||||
#include "common/TempConfig.h"
|
||||
#include "common/Configuration.h"
|
||||
#include "common/AsyncTask.h"
|
||||
|
@ -187,6 +187,10 @@ void MainWindow::initUI()
|
||||
plugin->setupInterface(this);
|
||||
}
|
||||
|
||||
#if QT_VERSION < QT_VERSION_CHECK(5, 7, 0)
|
||||
ui->actionGrouped_dock_dragging->setVisible(false);
|
||||
#endif
|
||||
|
||||
initLayout();
|
||||
}
|
||||
|
||||
@ -1402,9 +1406,13 @@ void MainWindow::on_actionExport_as_code_triggered()
|
||||
|
||||
void MainWindow::on_actionGrouped_dock_dragging_triggered(bool checked)
|
||||
{
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(5, 7, 0)
|
||||
auto options = dockOptions();
|
||||
options.setFlag(QMainWindow::DockOption::GroupedDragging, checked);
|
||||
setDockOptions(options);
|
||||
#else
|
||||
Q_UNUSED(checked);
|
||||
#endif
|
||||
}
|
||||
|
||||
void MainWindow::seekToFunctionLastInstruction()
|
||||
|
@ -53,6 +53,11 @@ AboutDialog::AboutDialog(QWidget *parent) :
|
||||
|
||||
QSignalBlocker s(ui->updatesCheckBox);
|
||||
ui->updatesCheckBox->setChecked(Config()->getAutoUpdateEnabled());
|
||||
|
||||
if (!CUTTER_UPDATE_WORKER_AVAILABLE) {
|
||||
ui->updatesCheckBox->hide();
|
||||
ui->checkForUpdatesButton->hide();
|
||||
}
|
||||
}
|
||||
|
||||
AboutDialog::~AboutDialog() {}
|
||||
@ -80,6 +85,7 @@ void AboutDialog::on_showPluginsButton_clicked()
|
||||
|
||||
void AboutDialog::on_checkForUpdatesButton_clicked()
|
||||
{
|
||||
#if CUTTER_UPDATE_WORKER_AVAILABLE
|
||||
UpdateWorker updateWorker;
|
||||
|
||||
QProgressDialog waitDialog;
|
||||
@ -105,6 +111,7 @@ void AboutDialog::on_checkForUpdatesButton_clicked()
|
||||
|
||||
updateWorker.checkCurrentVersion(7000);
|
||||
waitDialog.exec();
|
||||
#endif
|
||||
}
|
||||
|
||||
void AboutDialog::on_updatesCheckBox_stateChanged(int)
|
||||
|
@ -345,7 +345,8 @@ void NewFileDialog::fillIOPluginsList()
|
||||
if (plugin.permissions.contains('d')) {
|
||||
continue;
|
||||
}
|
||||
for (const auto &uri : qAsConst(plugin.uris)) {
|
||||
const auto &uris = plugin.uris;
|
||||
for (const auto &uri : uris) {
|
||||
if (uri == "file://") {
|
||||
continue;
|
||||
}
|
||||
|
@ -17,7 +17,9 @@ TypesInteractionDialog::TypesInteractionDialog(QWidget *parent, bool readOnly) :
|
||||
QFont font = Config()->getFont();
|
||||
ui->plainTextEdit->setFont(font);
|
||||
ui->plainTextEdit->setPlainText("");
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)
|
||||
ui->plainTextEdit->setTabStopDistance(4 * QFontMetrics(font).horizontalAdvance(' '));
|
||||
#endif
|
||||
syntaxHighLighter = Config()->createSyntaxHighlighter(ui->plainTextEdit->document());
|
||||
ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false);
|
||||
ui->plainTextEdit->setReadOnly(readOnly);
|
||||
|
@ -16,7 +16,7 @@ WelcomeDialog::WelcomeDialog(QWidget *parent) :
|
||||
ui(new Ui::WelcomeDialog)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
setWindowFlag(Qt::WindowContextHelpButtonHint, false);
|
||||
setWindowFlags(windowFlags() & (~Qt::WindowContextHelpButtonHint));
|
||||
ui->logoSvgWidget->load(Config()->getLogoFile());
|
||||
ui->versionLabel->setText("<font color='#a4a9b2'>" + tr("Version ") + CUTTER_VERSION_FULL + "</font>");
|
||||
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(ui->commentsComboBox, QOverload<int>::of(&QComboBox::currentIndexChanged), this,
|
||||
using indexSignalType = void (QComboBox::*)(int);
|
||||
connect(ui->commentsComboBox, static_cast<indexSignalType>(&QComboBox::currentIndexChanged), this,
|
||||
&AsmOptionsWidget::commentsComboBoxChanged);
|
||||
connect(ui->asmComboBox, QOverload<int>::of(&QComboBox::currentIndexChanged), this,
|
||||
connect(ui->asmComboBox, static_cast<indexSignalType>(&QComboBox::currentIndexChanged), this,
|
||||
&AsmOptionsWidget::asmComboBoxChanged);
|
||||
connect(Core(), SIGNAL(asmOptionsChanged()), this, SLOT(updateAsmOptionsFromVars()));
|
||||
updateAsmOptionsFromVars();
|
||||
|
@ -21,7 +21,7 @@ PreferencesDialog::PreferencesDialog(QWidget *parent)
|
||||
{
|
||||
setAttribute(Qt::WA_DeleteOnClose);
|
||||
ui->setupUi(this);
|
||||
setWindowFlag(Qt::WindowContextHelpButtonHint, false);
|
||||
setWindowFlags(windowFlags() & (~Qt::WindowContextHelpButtonHint));
|
||||
|
||||
QList<PreferenceCategory> prefs {
|
||||
|
||||
|
@ -22,7 +22,48 @@ DisassemblyContextMenu::DisassemblyContextMenu(QWidget *parent, MainWindow *main
|
||||
: QMenu(parent),
|
||||
offset(0),
|
||||
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());
|
||||
addAction(&actionCopy);
|
||||
@ -209,7 +250,7 @@ void DisassemblyContextMenu::addSetToDataMenu()
|
||||
SLOT(on_actionSetToDataEx_triggered()), getSetToDataExSequence());
|
||||
setToDataMenu->addAction(&actionSetToDataEx);
|
||||
|
||||
auto switchAction = new QAction();
|
||||
auto switchAction = new QAction(this);
|
||||
initAction(switchAction, "Switch Data",
|
||||
SLOT(on_actionSetToData_triggered()), getSetToDataSequence());
|
||||
}
|
||||
|
@ -327,7 +327,7 @@ QColor ColorPicker::getColorAtMouse()
|
||||
const QPixmap pixmap = QGuiApplication::screens().at(desktop->screenNumber())
|
||||
->grabWindow(desktop->winId(),
|
||||
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
|
||||
|
@ -14,6 +14,7 @@
|
||||
|
||||
#include "common/Configuration.h"
|
||||
#include "common/ColorThemeWorker.h"
|
||||
#include "common/Helpers.h"
|
||||
|
||||
#include "widgets/ColorThemeListView.h"
|
||||
|
||||
@ -39,7 +40,7 @@ void ColorOptionDelegate::paint(QPainter *painter,
|
||||
const QStyleOptionViewItem &option,
|
||||
const QModelIndex &index) const
|
||||
{
|
||||
int margin = this->margin * painter->device()->devicePixelRatioF();
|
||||
int margin = this->margin * qhelpers::devicePixelRatio(painter->device());
|
||||
painter->save();
|
||||
painter->setFont(option.font);
|
||||
painter->setRenderHint(QPainter::Antialiasing);
|
||||
@ -138,7 +139,7 @@ void ColorOptionDelegate::paint(QPainter *painter,
|
||||
// Create chess-like pattern of black and white squares
|
||||
// and fill background of roundedColorRect with it
|
||||
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;
|
||||
QPixmap p(c1, c1);
|
||||
QPainter paint(&p);
|
||||
@ -165,7 +166,7 @@ void ColorOptionDelegate::paint(QPainter *painter,
|
||||
|
||||
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 h = QPen().width();
|
||||
h += fontHeight; // option name
|
||||
|
@ -176,7 +176,7 @@ void ConsoleWidget::executeCommand(const QString &command)
|
||||
ui->outputTextEdit->appendHtml(cmd_line + result);
|
||||
scrollOutputToEnd();
|
||||
historyAdd(command);
|
||||
commandTask = nullptr;
|
||||
commandTask.clear();
|
||||
ui->inputLineEdit->setEnabled(true);
|
||||
ui->inputLineEdit->setFocus();
|
||||
if (oldOffset != Core()->getOffset()) {
|
||||
|
@ -9,6 +9,7 @@
|
||||
#include "common/SyntaxHighlighter.h"
|
||||
#include "common/BasicBlockHighlighter.h"
|
||||
#include "dialogs/MultitypeFileSaveDialog.h"
|
||||
#include "common/Helpers.h"
|
||||
|
||||
#include <QColorDialog>
|
||||
#include <QPainter>
|
||||
@ -27,16 +28,19 @@
|
||||
#include <QStandardPaths>
|
||||
#include <QClipboard>
|
||||
#include <QApplication>
|
||||
#include <QAction>
|
||||
|
||||
#include <cmath>
|
||||
|
||||
DisassemblerGraphView::DisassemblerGraphView(QWidget *parent, CutterSeekable *seekable,
|
||||
MainWindow *mainWindow)
|
||||
MainWindow *mainWindow, QList<QAction *> additionalMenuActions)
|
||||
: GraphView(parent),
|
||||
mFontMetrics(nullptr),
|
||||
blockMenu(new DisassemblyContextMenu(this, mainWindow)),
|
||||
contextMenu(new QMenu(this)),
|
||||
seekable(seekable)
|
||||
seekable(seekable),
|
||||
actionExportGraph(this),
|
||||
actionUnhighlight(this)
|
||||
{
|
||||
highlight_token = nullptr;
|
||||
auto *layout = new QVBoxLayout(this);
|
||||
@ -100,8 +104,6 @@ DisassemblerGraphView::DisassemblerGraphView(QWidget *parent, CutterSeekable *se
|
||||
// Export Graph menu
|
||||
actionExportGraph.setText(tr("Export Graph"));
|
||||
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
|
||||
contextMenu->addAction(&actionExportGraph);
|
||||
@ -133,7 +135,7 @@ DisassemblerGraphView::DisassemblerGraphView(QWidget *parent, CutterSeekable *se
|
||||
}
|
||||
layoutMenu->addActions(layoutGroup->actions());
|
||||
contextMenu->addSeparator();
|
||||
contextMenu->addAction(&actionSyncOffset);
|
||||
contextMenu->addActions(additionalMenuActions);
|
||||
|
||||
|
||||
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
|
||||
auto transform = p.combinedTransform();
|
||||
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;
|
||||
}
|
||||
|
||||
@ -648,7 +651,7 @@ RVA DisassemblerGraphView::getAddrForMouseEvent(GraphBlock &block, QPoint *point
|
||||
}
|
||||
|
||||
DisassemblerGraphView::Instr *DisassemblerGraphView::getInstrForMouseEvent(
|
||||
GraphView::GraphBlock &block, QPoint *point)
|
||||
GraphView::GraphBlock &block, QPoint *point, bool force)
|
||||
{
|
||||
DisassemblyBlock &db = disassembly_blocks[block.entry];
|
||||
|
||||
@ -667,6 +670,13 @@ DisassemblerGraphView::Instr *DisassemblerGraphView::getInstrForMouseEvent(
|
||||
}
|
||||
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;
|
||||
}
|
||||
@ -935,7 +945,7 @@ QPoint DisassemblerGraphView::getInstructionOffset(const DisassemblyBlock &block
|
||||
void DisassemblerGraphView::blockClicked(GraphView::GraphBlock &block, QMouseEvent *event,
|
||||
QPoint pos)
|
||||
{
|
||||
Instr *instr = getInstrForMouseEvent(block, &pos);
|
||||
Instr *instr = getInstrForMouseEvent(block, &pos, event->button() == Qt::RightButton);
|
||||
if (!instr) {
|
||||
return;
|
||||
}
|
||||
@ -952,12 +962,25 @@ void DisassemblerGraphView::blockClicked(GraphView::GraphBlock &block, QMouseEve
|
||||
if (highlight_token) {
|
||||
blockMenu->setCurHighlightedWord(highlight_token->content);
|
||||
}
|
||||
if (event->button() == Qt::RightButton) {
|
||||
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());
|
||||
}
|
||||
viewport()->update();
|
||||
|
||||
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,
|
||||
@ -1112,16 +1135,13 @@ void DisassemblerGraphView::exportR2GraphvizGraph(QString filePath, QString type
|
||||
{
|
||||
TempConfig tempConfig;
|
||||
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)
|
||||
{
|
||||
GraphView::mousePressEvent(event);
|
||||
if (!event->isAccepted() && event->button() == Qt::RightButton) {
|
||||
contextMenu->exec(event->globalPos());
|
||||
event->accept();
|
||||
}
|
||||
emit graphMoved();
|
||||
}
|
||||
|
||||
|
@ -87,7 +87,8 @@ class DisassemblerGraphView : public GraphView
|
||||
};
|
||||
|
||||
public:
|
||||
DisassemblerGraphView(QWidget *parent, CutterSeekable *seekable, MainWindow *mainWindow);
|
||||
DisassemblerGraphView(QWidget *parent, CutterSeekable *seekable, MainWindow *mainWindow,
|
||||
QList<QAction *> additionalMenuAction);
|
||||
~DisassemblerGraphView() override;
|
||||
std::unordered_map<ut64, DisassemblyBlock> disassembly_blocks;
|
||||
virtual void drawBlock(QPainter &p, GraphView::GraphBlock &block, bool interactive) override;
|
||||
@ -146,6 +147,9 @@ protected:
|
||||
void resizeEvent(QResizeEvent *event) override;
|
||||
|
||||
void paintEvent(QPaintEvent *event) override;
|
||||
void blockContextMenuRequested(GraphView::GraphBlock &block, QContextMenuEvent *event,
|
||||
QPoint pos) override;
|
||||
void contextMenuEvent(QContextMenuEvent *event) override;
|
||||
|
||||
private slots:
|
||||
void on_actionExportGraph_triggered();
|
||||
@ -175,7 +179,7 @@ private:
|
||||
QPoint getTextOffset(int line) const;
|
||||
QPoint getInstructionOffset(const DisassemblyBlock &block, int line) const;
|
||||
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.
|
||||
* Inefficient don't use this function when iterating over all instructions.
|
||||
@ -216,7 +220,6 @@ private:
|
||||
|
||||
QAction actionExportGraph;
|
||||
QAction actionUnhighlight;
|
||||
QAction actionSyncOffset;
|
||||
|
||||
QLabel *emptyText = nullptr;
|
||||
signals:
|
||||
|
@ -169,9 +169,7 @@ DisassemblyWidget::DisassemblyWidget(MainWindow *main, QAction *action)
|
||||
connect(mCtxMenu, SIGNAL(copy()), mDisasTextEdit, SLOT(copy()));
|
||||
|
||||
mCtxMenu->addSeparator();
|
||||
syncIt.setText(tr("Sync/unsync offset"));
|
||||
mCtxMenu->addAction(&syncIt);
|
||||
connect(&syncIt, &QAction::triggered, seekable, &CutterSeekable::toggleSynchronization);
|
||||
mCtxMenu->addAction(&syncAction);
|
||||
connect(seekable, &CutterSeekable::seekableSeekChanged, this, &DisassemblyWidget::on_seekChanged);
|
||||
|
||||
addActions(mCtxMenu->actions());
|
||||
@ -907,10 +905,11 @@ void DisassemblyLeftPanel::paintEvent(QPaintEvent *event)
|
||||
}
|
||||
|
||||
const RVA currOffset = disas->getSeekable()->getOffset();
|
||||
qreal pixelRatio = qhelpers::devicePixelRatio(p.device());
|
||||
// Draw the lines
|
||||
for (const auto& l : lines) {
|
||||
int lineOffset = int((distanceBetweenLines * arrowInfo[l.offset].second + distanceBetweenLines) *
|
||||
p.device()->devicePixelRatioF());
|
||||
pixelRatio);
|
||||
// Skip until we reach a line that jumps to a destination
|
||||
if (l.arrow == RVA_INVALID) {
|
||||
continue;
|
||||
|
@ -88,8 +88,6 @@ private:
|
||||
void connectCursorPositionChanged(bool disconnect);
|
||||
|
||||
void moveCursorRelative(bool up, bool page);
|
||||
|
||||
QAction syncIt;
|
||||
};
|
||||
|
||||
class DisassemblyScrollArea : public QAbstractScrollArea
|
||||
|
@ -4,6 +4,7 @@
|
||||
#ifdef CUTTER_ENABLE_GRAPHVIZ
|
||||
#include "GraphvizLayout.h"
|
||||
#endif
|
||||
#include "Helpers.h"
|
||||
|
||||
#include <vector>
|
||||
#include <QPainter>
|
||||
@ -12,7 +13,7 @@
|
||||
#include <QPropertyAnimation>
|
||||
#include <QSvgGenerator>
|
||||
|
||||
#ifndef QT_NO_OPENGL
|
||||
#ifndef CUTTER_NO_OPENGL_GRAPH
|
||||
#include <QOpenGLContext>
|
||||
#include <QOpenGLWidget>
|
||||
#include <QOpenGLPaintDevice>
|
||||
@ -22,12 +23,12 @@
|
||||
GraphView::GraphView(QWidget *parent)
|
||||
: QAbstractScrollArea(parent)
|
||||
, useGL(false)
|
||||
#ifndef QT_NO_OPENGL
|
||||
#ifndef CUTTER_NO_OPENGL_GRAPH
|
||||
, cacheTexture(0)
|
||||
, cacheFBO(0)
|
||||
#endif
|
||||
{
|
||||
#ifndef QT_NO_OPENGL
|
||||
#ifndef CUTTER_NO_OPENGL_GRAPH
|
||||
if (useGL) {
|
||||
glWidget = new QOpenGLWidget(this);
|
||||
setViewport(glWidget);
|
||||
@ -76,20 +77,11 @@ void GraphView::blockHelpEvent(GraphView::GraphBlock &block, QHelpEvent *event,
|
||||
|
||||
bool GraphView::helpEvent(QHelpEvent *event)
|
||||
{
|
||||
int x = event->pos().x() + offset.x();
|
||||
int y = event->pos().y() - offset.y();
|
||||
|
||||
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);
|
||||
blockHelpEvent(block, event, pos);
|
||||
auto p = viewToLogicalCoordinates(event->pos());
|
||||
if (auto block = getBlockContaining(p)) {
|
||||
blockHelpEvent(*block, event, p - QPoint(block->x, block->y));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -111,6 +103,10 @@ GraphView::EdgeConfiguration GraphView::edgeConfiguration(GraphView::GraphBlock
|
||||
return ec;
|
||||
}
|
||||
|
||||
void GraphView::blockContextMenuRequested(GraphView::GraphBlock &, QContextMenuEvent *, QPoint)
|
||||
{
|
||||
}
|
||||
|
||||
bool GraphView::event(QEvent *event)
|
||||
{
|
||||
if (event->type() == QEvent::ToolTip) {
|
||||
@ -122,6 +118,17 @@ bool GraphView::event(QEvent *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.
|
||||
void GraphView::computeGraph(ut64 entry)
|
||||
{
|
||||
@ -154,7 +161,7 @@ void GraphView::setViewScale(qreal scale)
|
||||
QSize GraphView::getCacheSize()
|
||||
{
|
||||
return
|
||||
#ifndef QT_NO_OPENGL
|
||||
#ifndef CUTTER_NO_OPENGL_GRAPH
|
||||
useGL ? cacheSize :
|
||||
#endif
|
||||
pixmap.size();
|
||||
@ -163,33 +170,29 @@ QSize GraphView::getCacheSize()
|
||||
qreal GraphView::getCacheDevicePixelRatioF()
|
||||
{
|
||||
return
|
||||
#ifndef QT_NO_OPENGL
|
||||
#ifndef CUTTER_NO_OPENGL_GRAPH
|
||||
useGL ? 1.0 :
|
||||
#endif
|
||||
pixmap.devicePixelRatioF();
|
||||
qhelpers::devicePixelRatio(&pixmap);
|
||||
}
|
||||
|
||||
QSize GraphView::getRequiredCacheSize()
|
||||
{
|
||||
return
|
||||
#ifndef QT_NO_OPENGL
|
||||
useGL ? viewport()->size() :
|
||||
#endif
|
||||
viewport()->size() * devicePixelRatioF();
|
||||
return viewport()->size() * getRequiredCacheDevicePixelRatioF();
|
||||
}
|
||||
|
||||
qreal GraphView::getRequiredCacheDevicePixelRatioF()
|
||||
{
|
||||
return
|
||||
#ifndef QT_NO_OPENGL
|
||||
#ifndef CUTTER_NO_OPENGL_GRAPH
|
||||
useGL ? 1.0f :
|
||||
#endif
|
||||
devicePixelRatioF();
|
||||
qhelpers::devicePixelRatio(this);
|
||||
}
|
||||
|
||||
void GraphView::paintEvent(QPaintEvent *)
|
||||
{
|
||||
#ifndef QT_NO_OPENGL
|
||||
#ifndef CUTTER_NO_OPENGL_GRAPH
|
||||
if (useGL) {
|
||||
glWidget->makeCurrent();
|
||||
}
|
||||
@ -206,7 +209,7 @@ void GraphView::paintEvent(QPaintEvent *)
|
||||
}
|
||||
|
||||
if (useGL) {
|
||||
#ifndef QT_NO_OPENGL
|
||||
#ifndef CUTTER_NO_OPENGL_GRAPH
|
||||
auto gl = glWidget->context()->extraFunctions();
|
||||
gl->glBindFramebuffer(GL_READ_FRAMEBUFFER, cacheFBO);
|
||||
gl->glBindFramebuffer(GL_DRAW_FRAMEBUFFER, glWidget->defaultFramebufferObject());
|
||||
@ -249,12 +252,12 @@ void GraphView::addViewOffset(QPoint move, bool emitSignal)
|
||||
|
||||
void GraphView::paintGraphCache()
|
||||
{
|
||||
#ifndef QT_NO_OPENGL
|
||||
#ifndef CUTTER_NO_OPENGL_GRAPH
|
||||
QOpenGLPaintDevice *paintDevice = nullptr;
|
||||
#endif
|
||||
QPainter p;
|
||||
if (useGL) {
|
||||
#ifndef QT_NO_OPENGL
|
||||
#ifndef CUTTER_NO_OPENGL_GRAPH
|
||||
auto gl = QOpenGLContext::currentContext()->functions();
|
||||
|
||||
bool resizeTex = false;
|
||||
@ -289,7 +292,7 @@ void GraphView::paintGraphCache()
|
||||
p.begin(paintDevice);
|
||||
#endif
|
||||
} else {
|
||||
auto dpr = devicePixelRatioF();
|
||||
auto dpr = qhelpers::devicePixelRatio(this);
|
||||
pixmap = QPixmap(getRequiredCacheSize());
|
||||
pixmap.setDevicePixelRatio(dpr);
|
||||
p.begin(&pixmap);
|
||||
@ -299,7 +302,7 @@ void GraphView::paintGraphCache()
|
||||
paint(p, offset, this->viewport()->rect(), current_scale);
|
||||
|
||||
p.end();
|
||||
#ifndef QT_NO_OPENGL
|
||||
#ifndef CUTTER_NO_OPENGL_GRAPH
|
||||
delete paintDevice;
|
||||
#endif
|
||||
}
|
||||
@ -488,6 +491,25 @@ void GraphView::showRectangle(const QRect &block, bool anywhere)
|
||||
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)
|
||||
{
|
||||
graphLayout = layout;
|
||||
@ -550,22 +572,15 @@ void GraphView::mousePressEvent(QMouseEvent *event)
|
||||
return;
|
||||
}
|
||||
|
||||
int x = event->pos().x() / current_scale + offset.x();
|
||||
int y = event->pos().y() / current_scale + offset.y();
|
||||
QPoint pos = viewToLogicalCoordinates(event->pos());
|
||||
|
||||
// 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);
|
||||
blockClicked(block, event, pos);
|
||||
if (auto block = getBlockContaining(pos)) {
|
||||
blockClicked(*block, event, pos - QPoint(block->x, block->y));
|
||||
// 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
|
||||
if (event->button() == Qt::LeftButton) {
|
||||
@ -577,13 +592,13 @@ void GraphView::mousePressEvent(QMouseEvent *event)
|
||||
}
|
||||
QPointF start = edge.polyline.first();
|
||||
QPointF end = edge.polyline.last();
|
||||
if (checkPointClicked(start, x, y)) {
|
||||
if (checkPointClicked(start, pos.x(), pos.y())) {
|
||||
showBlock(blocks[edge.target]);
|
||||
// TODO: Callback to child
|
||||
return;
|
||||
break;
|
||||
}
|
||||
if (checkPointClicked(end, x, y, true)) {
|
||||
if (checkPointClicked(end, pos.x(), pos.y(), true)) {
|
||||
showBlock(block);
|
||||
// TODO: Callback to child
|
||||
return;
|
||||
@ -615,19 +630,9 @@ void GraphView::mouseMoveEvent(QMouseEvent *event)
|
||||
|
||||
void GraphView::mouseDoubleClickEvent(QMouseEvent *event)
|
||||
{
|
||||
int x = event->pos().x() / current_scale + offset.x();
|
||||
int y = event->pos().y() / current_scale + offset.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;
|
||||
}
|
||||
auto p = viewToLogicalCoordinates(event->pos());
|
||||
if (auto block = getBlockContaining(p)) {
|
||||
blockDoubleClicked(*block, event, p - QPoint(block->x, block->y));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -17,7 +17,12 @@
|
||||
#include "core/Cutter.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;
|
||||
#endif
|
||||
|
||||
@ -64,6 +69,13 @@ public:
|
||||
* @param anywhere - set to true for minimizing movement
|
||||
*/
|
||||
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);
|
||||
Layout getGraphLayout() const { return graphLayout; }
|
||||
@ -101,8 +113,11 @@ protected:
|
||||
virtual void wheelEvent(QWheelEvent *event) override;
|
||||
virtual EdgeConfiguration edgeConfiguration(GraphView::GraphBlock &from, GraphView::GraphBlock *to,
|
||||
bool interactive = true);
|
||||
virtual void blockContextMenuRequested(GraphView::GraphBlock &block, QContextMenuEvent *event,
|
||||
QPoint pos);
|
||||
|
||||
bool event(QEvent *event) override;
|
||||
void contextMenuEvent(QContextMenuEvent *event) override;
|
||||
|
||||
// Mouse events
|
||||
void mousePressEvent(QMouseEvent *event) override;
|
||||
@ -153,7 +168,7 @@ private:
|
||||
*/
|
||||
QPixmap pixmap;
|
||||
|
||||
#ifndef QT_NO_OPENGL
|
||||
#ifndef CUTTER_NO_OPENGL_GRAPH
|
||||
uint32_t cacheTexture;
|
||||
uint32_t cacheFBO;
|
||||
QSize cacheSize;
|
||||
|
@ -23,7 +23,7 @@ GraphWidget::GraphWidget(MainWindow *main, QAction *action) :
|
||||
header->setReadOnly(true);
|
||||
layout->addWidget(header);
|
||||
|
||||
graphView = new DisassemblerGraphView(layoutWidget, seekable, main);
|
||||
graphView = new DisassemblerGraphView(layoutWidget, seekable, main, {&syncAction});
|
||||
layout->addWidget(graphView);
|
||||
|
||||
// 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()) {
|
||||
std::reverse(edge.polyline.begin(), edge.polyline.end());
|
||||
}
|
||||
auto it = edge.polyline.rbegin();
|
||||
auto it = std::prev(edge.polyline.end());
|
||||
QPointF direction = *it;
|
||||
direction -= *(++it);
|
||||
direction -= *(--it);
|
||||
edge.arrow = getArrowDirection(direction, lineType == LineType::Polyline);
|
||||
|
||||
} else {
|
||||
|
@ -817,7 +817,7 @@ void HexWidget::fillSelectionBackground(QPainter &painter, bool ascii)
|
||||
return;
|
||||
}
|
||||
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);
|
||||
if (ascii == cursorOnAscii) {
|
||||
painter.setBrush(highlightColor);
|
||||
@ -1060,6 +1060,30 @@ const QColor HexWidget::itemColor(uint8_t byte)
|
||||
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)
|
||||
{
|
||||
quint8 byte;
|
||||
@ -1082,9 +1106,9 @@ QVariant HexWidget::readItem(int offset, QColor *color)
|
||||
return QVariant(static_cast<qint64>(static_cast<qint8>(byte)));
|
||||
case 2:
|
||||
if (itemBigEndian)
|
||||
word = qFromBigEndian<quint16>(dataPtr);
|
||||
word = fromBigEndian<quint16>(dataPtr);
|
||||
else
|
||||
word = qFromLittleEndian<quint16>(dataPtr);
|
||||
word = fromLittleEndian<quint16>(dataPtr);
|
||||
if (color)
|
||||
*color = defColor;
|
||||
if (!signedItem)
|
||||
@ -1092,9 +1116,9 @@ QVariant HexWidget::readItem(int offset, QColor *color)
|
||||
return QVariant(static_cast<qint64>(static_cast<qint16>(word)));
|
||||
case 4:
|
||||
if (itemBigEndian)
|
||||
dword = qFromBigEndian<quint32>(dataPtr);
|
||||
dword = fromBigEndian<quint32>(dataPtr);
|
||||
else
|
||||
dword = qFromLittleEndian<quint32>(dataPtr);
|
||||
dword = fromLittleEndian<quint32>(dataPtr);
|
||||
if (color)
|
||||
*color = defColor;
|
||||
if (itemFormat == ItemFormatFloat) {
|
||||
@ -1106,9 +1130,9 @@ QVariant HexWidget::readItem(int offset, QColor *color)
|
||||
return QVariant(static_cast<qint64>(static_cast<qint32>(dword)));
|
||||
case 8:
|
||||
if (itemBigEndian)
|
||||
qword = qFromBigEndian<quint64>(dataPtr);
|
||||
qword = fromBigEndian<quint64>(dataPtr);
|
||||
else
|
||||
qword = qFromLittleEndian<quint64>(dataPtr);
|
||||
qword = fromLittleEndian<quint64>(dataPtr);
|
||||
if (color)
|
||||
*color = defColor;
|
||||
if (itemFormat == ItemFormatFloat) {
|
||||
|
@ -73,8 +73,6 @@ HexdumpWidget::HexdumpWidget(MainWindow *main, QAction *action) :
|
||||
refresh(offset ? *offset : RVA_INVALID);
|
||||
});
|
||||
|
||||
connect(&syncAction, &QAction::triggered, seekable, &CutterSeekable::toggleSynchronization);
|
||||
syncAction.setText(tr("Sync/unsync offset"));
|
||||
this->ui->hexTextView->addAction(&syncAction);
|
||||
|
||||
connect(Config(), SIGNAL(fontsUpdated()), this, SLOT(fontsUpdated()));
|
||||
|
@ -62,8 +62,6 @@ private:
|
||||
|
||||
QString getWindowTitle() const override;
|
||||
|
||||
QAction syncAction;
|
||||
|
||||
private slots:
|
||||
void onSeekChanged(RVA addr);
|
||||
|
||||
|
@ -3,15 +3,25 @@
|
||||
#include "MainWindow.h"
|
||||
#include <QAction>
|
||||
#include <QEvent>
|
||||
#include <QMenu>
|
||||
#include <QContextMenuEvent>
|
||||
|
||||
MemoryDockWidget::MemoryDockWidget(MemoryWidgetType type, MainWindow *parent, QAction *action)
|
||||
: CutterDockWidget(parent, action)
|
||||
, mType(type), seekable(new CutterSeekable(this))
|
||||
, mType(type)
|
||||
, seekable(new CutterSeekable(this))
|
||||
, syncAction(tr("Sync/unsync offset"), this)
|
||||
{
|
||||
if (parent) {
|
||||
parent->addMemoryDockWidget(this);
|
||||
}
|
||||
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()
|
||||
@ -61,6 +71,12 @@ void MemoryDockWidget::updateWindowTitle()
|
||||
setWindowTitle(name);
|
||||
}
|
||||
|
||||
void MemoryDockWidget::contextMenuEvent(QContextMenuEvent *event)
|
||||
{
|
||||
event->accept();
|
||||
dockMenu->exec(mapToGlobal(event->pos()));
|
||||
}
|
||||
|
||||
CutterSeekable *MemoryDockWidget::getSeekable() const
|
||||
{
|
||||
return seekable;
|
||||
|
@ -4,6 +4,8 @@
|
||||
#include "CutterDockWidget.h"
|
||||
#include "core/Cutter.h"
|
||||
|
||||
#include <QAction>
|
||||
|
||||
class CutterSeekable;
|
||||
|
||||
/* Disassembly/Graph/Hexdump/Decompiler view priority */
|
||||
@ -14,7 +16,7 @@ class MemoryDockWidget : public CutterDockWidget
|
||||
Q_OBJECT
|
||||
public:
|
||||
MemoryDockWidget(MemoryWidgetType type, MainWindow *parent, QAction *action = nullptr);
|
||||
~MemoryDockWidget() {}
|
||||
~MemoryDockWidget() override {}
|
||||
|
||||
CutterSeekable *getSeekable() const;
|
||||
|
||||
@ -24,7 +26,7 @@ public:
|
||||
{
|
||||
return mType;
|
||||
}
|
||||
bool eventFilter(QObject *object, QEvent *event);
|
||||
bool eventFilter(QObject *object, QEvent *event) override;
|
||||
private:
|
||||
|
||||
MemoryWidgetType mType;
|
||||
@ -34,8 +36,11 @@ public slots:
|
||||
|
||||
protected:
|
||||
CutterSeekable *seekable = nullptr;
|
||||
QAction syncAction;
|
||||
QMenu *dockMenu = nullptr;
|
||||
|
||||
virtual QString getWindowTitle() const = 0;
|
||||
void contextMenuEvent(QContextMenuEvent *event) override;
|
||||
};
|
||||
|
||||
#endif // MEMORYDOCKWIDGET_H
|
||||
|
@ -196,8 +196,8 @@ SearchWidget::SearchWidget(MainWindow *main, QAction *action) :
|
||||
refreshSearch();
|
||||
});
|
||||
|
||||
connect(ui->searchspaceCombo, QOverload<int>::of(&QComboBox::currentIndexChanged),
|
||||
[ = ](int index) { updatePlaceholderText(index);});
|
||||
connect(ui->searchspaceCombo, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged),
|
||||
this, [this](int index) { updatePlaceholderText(index);});
|
||||
|
||||
QString currentSearchBoundary = Core()->getConfig("search.in");
|
||||
ui->searchInCombo->setCurrentIndex(ui->searchInCombo->findData(currentSearchBoundary));
|
||||
|
@ -8,23 +8,15 @@
|
||||
#include "QHeaderView"
|
||||
#include "QMenu"
|
||||
|
||||
enum ColumnIndex {
|
||||
COLUMN_OFFSET = 0,
|
||||
COLUMN_VALUE,
|
||||
COLUMN_DESCRIPTION
|
||||
};
|
||||
|
||||
StackWidget::StackWidget(MainWindow *main, QAction *action) :
|
||||
CutterDockWidget(main, action),
|
||||
ui(new Ui::StackWidget),
|
||||
menuText(this),
|
||||
addressableItemContextMenu(this, main)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
|
||||
// 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->setModel(modelStack);
|
||||
viewStack->verticalHeader()->hide();
|
||||
@ -51,7 +43,6 @@ StackWidget::StackWidget(MainWindow *main, QAction *action) :
|
||||
connect(editAction, &QAction::triggered, this, &StackWidget::editStack);
|
||||
connect(viewStack->selectionModel(), &QItemSelectionModel::currentChanged,
|
||||
this, &StackWidget::onCurrentChanged);
|
||||
connect(modelStack, &QStandardItemModel::itemChanged, this, &StackWidget::onItemChanged);
|
||||
|
||||
addressableItemContextMenu.addAction(editAction);
|
||||
addActions(addressableItemContextMenu.actions());
|
||||
@ -73,47 +64,8 @@ void StackWidget::updateContents()
|
||||
|
||||
void StackWidget::setStackGrid()
|
||||
{
|
||||
updatingData = true;
|
||||
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);
|
||||
modelStack->reload();
|
||||
viewStack->resizeColumnsToContents();
|
||||
updatingData = false;
|
||||
}
|
||||
|
||||
void StackWidget::fontsUpdatedSlot()
|
||||
@ -127,10 +79,10 @@ void StackWidget::onDoubleClicked(const QModelIndex &index)
|
||||
return;
|
||||
// Check if we are clicking on the offset or value columns and seek if it is the case
|
||||
int column = index.column();
|
||||
if (column <= COLUMN_VALUE) {
|
||||
if (column <= StackModel::ValueColumn) {
|
||||
QString item = index.data().toString();
|
||||
Core()->seek(item);
|
||||
if (column == COLUMN_OFFSET) {
|
||||
if (column == StackModel::OffsetColumn) {
|
||||
mainWindow->showMemoryWidget(MemoryWidgetType::Hexdump);
|
||||
} else {
|
||||
Core()->showMemoryWidget();
|
||||
@ -148,11 +100,11 @@ void StackWidget::editStack()
|
||||
bool ok;
|
||||
int row = viewStack->selectionModel()->currentIndex().row();
|
||||
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);
|
||||
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);
|
||||
|
||||
if (e.exec()) {
|
||||
@ -169,38 +121,149 @@ void StackWidget::onCurrentChanged(const QModelIndex ¤t, const QModelIndex
|
||||
Q_UNUSED(previous)
|
||||
auto currentIndex = viewStack->selectionModel()->currentIndex();
|
||||
QString offsetString;
|
||||
if (currentIndex.column() != COLUMN_DESCRIPTION) {
|
||||
if (currentIndex.column() != StackModel::DescriptionColumn) {
|
||||
offsetString = currentIndex.data().toString();
|
||||
} else {
|
||||
offsetString = currentIndex.sibling(currentIndex.row(), COLUMN_VALUE).data().toString();
|
||||
offsetString = currentIndex.sibling(currentIndex.row(), StackModel::ValueColumn).data().toString();
|
||||
}
|
||||
|
||||
RVA offset = Core()->math(offsetString);
|
||||
addressableItemContextMenu.setTarget(offset);
|
||||
if (currentIndex.column() == COLUMN_OFFSET) {
|
||||
if (currentIndex.column() == StackModel::OffsetColumn) {
|
||||
menuText.setText(tr("Stack position"));
|
||||
} else {
|
||||
menuText.setText(tr("Pointed memory"));
|
||||
}
|
||||
}
|
||||
|
||||
void StackWidget::onItemChanged(QStandardItem *item)
|
||||
StackModel::StackModel(QObject *parent)
|
||||
: QAbstractTableModel(parent)
|
||||
{
|
||||
if (updatingData || item->column() != COLUMN_VALUE) {
|
||||
return;
|
||||
}
|
||||
QModelIndex index = item->index();
|
||||
int row = item->row();
|
||||
QString text = item->text();
|
||||
// Queue the update instead of performing immediately. Editing will trigger reload.
|
||||
// Performing reload while itemChanged signal is on stack would result
|
||||
// in itemView getting stuck in EditingState and preventing further edits.
|
||||
QMetaObject::invokeMethod(this, [this, index, row, text]() {
|
||||
QString offsetString = index.sibling(row, COLUMN_OFFSET).data().toString();
|
||||
bool ok = false;
|
||||
auto offset = offsetString.toULongLong(&ok, 16);
|
||||
if (ok) {
|
||||
Core()->editBytesEndian(offset, text);
|
||||
|
||||
void StackModel::reload()
|
||||
{
|
||||
QJsonArray stackValues = Core()->getStack().array();
|
||||
|
||||
beginResetModel();
|
||||
values.clear();
|
||||
for (const QJsonValue &value : stackValues) {
|
||||
QJsonObject stackItem = value.toObject();
|
||||
Item item;
|
||||
|
||||
item.offset = stackItem["addr"].toVariant().toULongLong();
|
||||
item.value = RAddressString(stackItem["value"].toVariant().toULongLong());
|
||||
|
||||
|
||||
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);
|
||||
}
|
||||
}, Qt::QueuedConnection);
|
||||
}
|
||||
|
@ -15,6 +15,39 @@ namespace Ui {
|
||||
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
|
||||
{
|
||||
Q_OBJECT
|
||||
@ -31,15 +64,13 @@ private slots:
|
||||
void customMenuRequested(QPoint pos);
|
||||
void editStack();
|
||||
void onCurrentChanged(const QModelIndex ¤t, const QModelIndex &previous);
|
||||
void onItemChanged(QStandardItem *item);
|
||||
|
||||
private:
|
||||
std::unique_ptr<Ui::StackWidget> ui;
|
||||
QTableView *viewStack = new QTableView;
|
||||
QStandardItemModel *modelStack = new QStandardItemModel(1, 3, this);
|
||||
QTableView *viewStack = new QTableView(this);
|
||||
StackModel *modelStack = new StackModel(this);
|
||||
QAction *editAction;
|
||||
QAction menuText;
|
||||
RefreshDeferrer *refreshDeferrer;
|
||||
AddressableItemContextMenu addressableItemContextMenu;
|
||||
bool updatingData = false;
|
||||
|
||||
};
|
||||
|
@ -240,7 +240,7 @@ void StringsWidget::stringSearchFinished(const QList<StringDescription> &strings
|
||||
|
||||
tree->showItemsNumber(proxyModel->rowCount());
|
||||
|
||||
task = nullptr;
|
||||
task.clear();
|
||||
}
|
||||
|
||||
void StringsWidget::on_actionCopy()
|
||||
|
Loading…
Reference in New Issue
Block a user