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

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

View File

@ -31,6 +31,24 @@ matrix:
- pyenv global 3.7.1
- 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

View File

@ -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
~~~~~~~~~~~~~~~~~

View File

@ -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()

View File

@ -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();

View File

@ -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")

View File

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

View File

@ -208,7 +208,21 @@ QJsonDocument ColorThemeWorker::getTheme(const QString& themeName) const
theme.remove(key);
}
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

View File

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

View File

@ -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

View File

@ -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

View File

@ -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

View File

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

View File

@ -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()

View File

@ -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)

View File

@ -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;
}

View File

@ -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);

View File

@ -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());

View File

@ -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();

View File

@ -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 {

View File

@ -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());
}

View File

@ -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

View File

@ -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

View File

@ -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()) {

View File

@ -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();
}

View File

@ -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:

View File

@ -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;

View File

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

View File

@ -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 &block;
}
}
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));
}
}

View File

@ -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;

View File

@ -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

View File

@ -194,9 +194,9 @@ void GraphvizLayout::CalculateLayout(std::unordered_map<ut64, GraphBlock> &block
if (edge.target == block.entry && edge.polyline.first().y() < edge.polyline.last().y()) {
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 {

View File

@ -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) {

View File

@ -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()));

View File

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

View File

@ -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;

View File

@ -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

View File

@ -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));

View File

@ -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 &current, 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);
}

View File

@ -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 &current, 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;
};

View File

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