forked from qt-creator/qt-creator
BinEditor/MemoryView: Follow pointers, potentially in new window.
Reviewed-by: hjk
This commit is contained in:
@@ -37,8 +37,10 @@
|
||||
#include <QtCore/QTemporaryFile>
|
||||
|
||||
#include <QtGui/QApplication>
|
||||
#include <QtGui/QAction>
|
||||
#include <QtGui/QClipboard>
|
||||
#include <QtGui/QFontMetrics>
|
||||
#include <QtGui/QMenu>
|
||||
#include <QtGui/QPainter>
|
||||
#include <QtGui/QScrollBar>
|
||||
#include <QtGui/QWheelEvent>
|
||||
@@ -93,6 +95,7 @@ BinEditor::BinEditor(QWidget *parent)
|
||||
m_lowNibble = false;
|
||||
m_cursorVisible = false;
|
||||
m_caseSensitiveSearch = false;
|
||||
m_canRequestNewWindow = false;
|
||||
setFocusPolicy(Qt::WheelFocus);
|
||||
}
|
||||
|
||||
@@ -178,7 +181,8 @@ bool BinEditor::requestDataAt(int pos, bool synchronous) const
|
||||
if (!m_lazyRequests.contains(block)) {
|
||||
m_lazyRequests.insert(block);
|
||||
emit const_cast<BinEditor*>(this)->
|
||||
lazyDataRequested(m_baseAddr / m_blockSize + block, synchronous);
|
||||
lazyDataRequested(editorInterface(), m_baseAddr / m_blockSize + block,
|
||||
synchronous);
|
||||
if (!m_lazyRequests.contains(block))
|
||||
return true; // synchronous data source
|
||||
}
|
||||
@@ -1119,12 +1123,16 @@ void BinEditor::zoomOut(int range)
|
||||
zoomIn(-range);
|
||||
}
|
||||
|
||||
void BinEditor::copy()
|
||||
void BinEditor::copy(bool raw)
|
||||
{
|
||||
const int selStart = selectionStart();
|
||||
const int selEnd = selectionEnd();
|
||||
if (selStart < selEnd) {
|
||||
const QByteArray &data = dataMid(selStart, selEnd - selStart);
|
||||
if (raw) {
|
||||
QApplication::clipboard()->setText(data);
|
||||
return;
|
||||
}
|
||||
QString hexString;
|
||||
const char * const hex = "0123456789abcdef";
|
||||
for (int i = 0; i < data.size(); ++i) {
|
||||
@@ -1217,3 +1225,81 @@ void BinEditor::redo()
|
||||
if (!m_redoStack.size())
|
||||
emit redoAvailable(false);
|
||||
}
|
||||
|
||||
void BinEditor::contextMenuEvent(QContextMenuEvent *event)
|
||||
{
|
||||
const int selStart = selectionStart();
|
||||
const int byteCount = selectionEnd() - selStart;
|
||||
if (byteCount == 0)
|
||||
return;
|
||||
|
||||
QMenu contextMenu;
|
||||
QAction copyAsciiAction(tr("Copy Selection as ASCII Characters"), this);
|
||||
QAction copyHexAction(tr("Copy Selection as Hex Values"), this);
|
||||
QMenu jumpBeMenu;
|
||||
QMenu jumpLeMenu;
|
||||
QAction jumpToBeAddressHere(tr("In This Window"), this);
|
||||
QAction jumpToBeAddressNewWindow(tr("In New Window"), this);
|
||||
QAction jumpToLeAddressHere(tr("In This Window"), this);
|
||||
QAction jumpToLeAddressNewWindow(tr("In New Window"), this);
|
||||
contextMenu.addAction(©AsciiAction);
|
||||
contextMenu.addAction(©HexAction);
|
||||
contextMenu.addMenu(&jumpBeMenu);
|
||||
|
||||
quint64 beAddress = 0;
|
||||
quint64 leAddress = 0;
|
||||
if (byteCount <= 8) {
|
||||
const QByteArray &data = dataMid(selStart, byteCount);
|
||||
for (int pos = 0; pos < byteCount; ++pos) {
|
||||
const quint64 val = static_cast<quint64>(data.at(pos)) & 0xff;
|
||||
beAddress += val << (pos * 8);
|
||||
leAddress += val << ((byteCount - pos - 1) * 8);
|
||||
}
|
||||
|
||||
setupJumpToMenuAction(&jumpBeMenu, &jumpToBeAddressHere,
|
||||
&jumpToBeAddressNewWindow, beAddress);
|
||||
|
||||
// If the menu entries would be identical, show only one of them.
|
||||
if (beAddress != leAddress) {
|
||||
setupJumpToMenuAction(&jumpLeMenu, &jumpToLeAddressHere,
|
||||
&jumpToLeAddressNewWindow, leAddress);
|
||||
contextMenu.addMenu(&jumpLeMenu);
|
||||
}
|
||||
} else {
|
||||
jumpBeMenu.setTitle(tr("Jump to Address"));
|
||||
jumpBeMenu.setEnabled(false);
|
||||
}
|
||||
|
||||
QAction *action = contextMenu.exec(event->globalPos());
|
||||
if (action == ©AsciiAction)
|
||||
copy(true);
|
||||
else if (action == ©HexAction)
|
||||
copy(false);
|
||||
else if (action == &jumpToBeAddressHere)
|
||||
setCursorPosition(beAddress - m_baseAddr);
|
||||
else if (action == &jumpToLeAddressHere)
|
||||
setCursorPosition(leAddress - m_baseAddr);
|
||||
else if (action == &jumpToBeAddressNewWindow)
|
||||
emit newWindowRequested(beAddress);
|
||||
else if (action == &jumpToLeAddressNewWindow)
|
||||
emit newWindowRequested(leAddress);
|
||||
}
|
||||
|
||||
void BinEditor::setupJumpToMenuAction(QMenu *menu, QAction *actionHere,
|
||||
QAction *actionNew, quint64 addr)
|
||||
{
|
||||
menu->setTitle(tr("Jump to Address 0x%1").arg(QString::number(addr, 16)));
|
||||
menu->addAction(actionHere);
|
||||
if (addr < m_baseAddr || addr >= m_baseAddr + m_size)
|
||||
actionHere->setEnabled(false);
|
||||
if (!m_canRequestNewWindow)
|
||||
actionNew->setEnabled(false);
|
||||
menu->addAction(actionNew);
|
||||
if (!actionHere->isEnabled() && !actionNew->isEnabled())
|
||||
menu->setEnabled(false);
|
||||
}
|
||||
|
||||
void BinEditor::setNewWindowRequestAllowed()
|
||||
{
|
||||
m_canRequestNewWindow = true;
|
||||
}
|
||||
|
||||
@@ -40,6 +40,8 @@
|
||||
#include <QtGui/QTextDocument>
|
||||
#include <QtGui/QTextFormat>
|
||||
|
||||
QT_FORWARD_DECLARE_CLASS(QMenu)
|
||||
|
||||
namespace Core {
|
||||
class IEditor;
|
||||
}
|
||||
@@ -69,6 +71,7 @@ public:
|
||||
Q_INVOKABLE void setLazyData(quint64 startAddr, int range, int blockSize = 4096);
|
||||
inline int lazyDataBlockSize() const { return m_blockSize; }
|
||||
Q_INVOKABLE void addLazyData(quint64 block, const QByteArray &data);
|
||||
Q_INVOKABLE void setNewWindowRequestAllowed();
|
||||
bool save(const QString &oldFileName, const QString &newFileName);
|
||||
|
||||
void zoomIn(int range = 1);
|
||||
@@ -116,7 +119,7 @@ public:
|
||||
public Q_SLOTS:
|
||||
void setFontSettings(const TextEditor::FontSettings &fs);
|
||||
void highlightSearchResults(const QByteArray &pattern, QTextDocument::FindFlags findFlags = 0);
|
||||
void copy();
|
||||
void copy(bool raw = false);
|
||||
|
||||
Q_SIGNALS:
|
||||
void modificationChanged(bool modified);
|
||||
@@ -125,7 +128,8 @@ Q_SIGNALS:
|
||||
void copyAvailable(bool);
|
||||
void cursorPositionChanged(int position);
|
||||
|
||||
void lazyDataRequested(quint64 block, bool synchronous);
|
||||
void lazyDataRequested(Core::IEditor *editor, quint64 block, bool synchronous);
|
||||
void newWindowRequested(quint64 address);
|
||||
|
||||
protected:
|
||||
void scrollContentsBy(int dx, int dy);
|
||||
@@ -140,6 +144,7 @@ protected:
|
||||
void focusInEvent(QFocusEvent *);
|
||||
void focusOutEvent(QFocusEvent *);
|
||||
void timerEvent(QTimerEvent *);
|
||||
void contextMenuEvent(QContextMenuEvent *event);
|
||||
|
||||
private:
|
||||
bool m_inLazyMode;
|
||||
@@ -203,6 +208,9 @@ private:
|
||||
int findPattern(const QByteArray &data, const QByteArray &dataHex, int from, int offset, int *match);
|
||||
void drawItems(QPainter *painter, int x, int y, const QString &itemString);
|
||||
|
||||
void setupJumpToMenuAction(QMenu *menu, QAction *actionHere, QAction *actionNew,
|
||||
quint64 addr);
|
||||
|
||||
struct BinEditorEditCommand {
|
||||
int position;
|
||||
uchar character;
|
||||
@@ -214,6 +222,7 @@ private:
|
||||
Core::IEditor *m_ieditor;
|
||||
QString m_addressString;
|
||||
int m_addressBytes;
|
||||
bool m_canRequestNewWindow;
|
||||
};
|
||||
|
||||
} // namespace BINEditor
|
||||
|
||||
@@ -174,7 +174,8 @@ public:
|
||||
m_mimeType(QLatin1String(BINEditor::Constants::C_BINEDITOR_MIMETYPE))
|
||||
{
|
||||
m_editor = parent;
|
||||
connect(m_editor, SIGNAL(lazyDataRequested(quint64, bool)), this, SLOT(provideData(quint64)));
|
||||
connect(m_editor, SIGNAL(lazyDataRequested(Core::IEditor *, quint64, bool)),
|
||||
this, SLOT(provideData(Core::IEditor *, quint64)));
|
||||
}
|
||||
~BinEditorFile() {}
|
||||
|
||||
@@ -211,7 +212,7 @@ public:
|
||||
}
|
||||
|
||||
private slots:
|
||||
void provideData(quint64 block) {
|
||||
void provideData(Core::IEditor *, quint64 block) {
|
||||
QFile file(m_fileName);
|
||||
if (file.open(QIODevice::ReadOnly)) {
|
||||
int blockSize = m_editor->lazyDataBlockSize();
|
||||
|
||||
@@ -1135,7 +1135,7 @@ void CdbDebugEngine::fetchDisassembler(DisassemblerViewAgent *agent)
|
||||
}
|
||||
}
|
||||
|
||||
void CdbDebugEngine::fetchMemory(MemoryViewAgent *agent, quint64 addr, quint64 length)
|
||||
void CdbDebugEngine::fetchMemory(MemoryViewAgent *agent, QObject *token, quint64 addr, quint64 length)
|
||||
{
|
||||
if (!m_d->m_hDebuggeeProcess && !length)
|
||||
return;
|
||||
@@ -1149,7 +1149,7 @@ void CdbDebugEngine::fetchMemory(MemoryViewAgent *agent, quint64 addr, quint64 l
|
||||
}
|
||||
if (received < length)
|
||||
data.truncate(received);
|
||||
agent->addLazyData(addr, data);
|
||||
agent->addLazyData(token, addr, data);
|
||||
}
|
||||
|
||||
void CdbDebugEngine::reloadModules()
|
||||
|
||||
@@ -90,7 +90,7 @@ public:
|
||||
virtual void attemptBreakpointSynchronization();
|
||||
|
||||
virtual void fetchDisassembler(DisassemblerViewAgent *agent);
|
||||
virtual void fetchMemory(MemoryViewAgent *, quint64 addr, quint64 length);
|
||||
virtual void fetchMemory(MemoryViewAgent *, QObject *, quint64 addr, quint64 length);
|
||||
|
||||
virtual void reloadModules();
|
||||
virtual void loadSymbols(const QString &moduleName);
|
||||
|
||||
@@ -72,35 +72,41 @@ namespace Internal {
|
||||
MemoryViewAgent::MemoryViewAgent(DebuggerManager *manager, quint64 addr)
|
||||
: QObject(manager), m_engine(manager->currentEngine()), m_manager(manager)
|
||||
{
|
||||
init(addr);
|
||||
createBinEditor(addr);
|
||||
}
|
||||
|
||||
MemoryViewAgent::MemoryViewAgent(DebuggerManager *manager, const QString &addr)
|
||||
: QObject(manager), m_engine(manager->currentEngine()), m_manager(manager)
|
||||
{
|
||||
bool ok = true;
|
||||
init(addr.toULongLong(&ok, 0));
|
||||
createBinEditor(addr.toULongLong(&ok, 0));
|
||||
//qDebug() << " ADDRESS: " << addr << addr.toUInt(&ok, 0);
|
||||
}
|
||||
|
||||
MemoryViewAgent::~MemoryViewAgent()
|
||||
{
|
||||
if (m_editor)
|
||||
m_editor->deleteLater();
|
||||
foreach (QPointer<Core::IEditor> editor, m_editors) {
|
||||
if (editor)
|
||||
editor->deleteLater();
|
||||
}
|
||||
}
|
||||
|
||||
void MemoryViewAgent::init(quint64 addr)
|
||||
void MemoryViewAgent::createBinEditor(quint64 addr)
|
||||
{
|
||||
Core::EditorManager *editorManager = Core::EditorManager::instance();
|
||||
QString titlePattern = tr("Memory $");
|
||||
m_editor = editorManager->openEditorWithContents(
|
||||
Core::IEditor *editor = editorManager->openEditorWithContents(
|
||||
Core::Constants::K_DEFAULT_BINARY_EDITOR_ID,
|
||||
&titlePattern);
|
||||
if (m_editor) {
|
||||
connect(m_editor->widget(), SIGNAL(lazyDataRequested(quint64,bool)),
|
||||
this, SLOT(fetchLazyData(quint64,bool)));
|
||||
editorManager->activateEditor(m_editor);
|
||||
QMetaObject::invokeMethod(m_editor->widget(), "setLazyData",
|
||||
if (editor) {
|
||||
connect(editor->widget(), SIGNAL(lazyDataRequested(Core::IEditor *, quint64,bool)),
|
||||
this, SLOT(fetchLazyData(Core::IEditor *, quint64,bool)));
|
||||
connect(editor->widget(), SIGNAL(newWindowRequested(quint64)),
|
||||
this, SLOT(createBinEditor(quint64)));
|
||||
m_editors << editor;
|
||||
editorManager->activateEditor(editor);
|
||||
QMetaObject::invokeMethod(editor->widget(), "setNewWindowRequestAllowed");
|
||||
QMetaObject::invokeMethod(editor->widget(), "setLazyData",
|
||||
Q_ARG(quint64, addr), Q_ARG(int, 1024 * 1024), Q_ARG(int, BinBlockSize));
|
||||
} else {
|
||||
m_manager->showMessageBox(QMessageBox::Warning,
|
||||
@@ -111,19 +117,23 @@ void MemoryViewAgent::init(quint64 addr)
|
||||
}
|
||||
}
|
||||
|
||||
void MemoryViewAgent::fetchLazyData(quint64 block, bool sync)
|
||||
void MemoryViewAgent::fetchLazyData(Core::IEditor *editor, quint64 block, bool sync)
|
||||
{
|
||||
Q_UNUSED(sync); // FIXME: needed support for incremental searching
|
||||
if (m_engine)
|
||||
m_engine->fetchMemory(this, BinBlockSize * block, BinBlockSize);
|
||||
m_engine->fetchMemory(this, editor, BinBlockSize * block, BinBlockSize);
|
||||
}
|
||||
|
||||
void MemoryViewAgent::addLazyData(quint64 addr, const QByteArray &ba)
|
||||
void MemoryViewAgent::addLazyData(QObject *editorToken, quint64 addr,
|
||||
const QByteArray &ba)
|
||||
{
|
||||
if (m_editor && m_editor->widget())
|
||||
QMetaObject::invokeMethod(m_editor->widget(), "addLazyData",
|
||||
Core::IEditor *editor = qobject_cast<Core::IEditor *>(editorToken);
|
||||
if (editor && editor->widget()) {
|
||||
Core::EditorManager::instance()->activateEditor(editor);
|
||||
QMetaObject::invokeMethod(editor->widget(), "addLazyData",
|
||||
Q_ARG(quint64, addr / BinBlockSize), Q_ARG(QByteArray, ba));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////
|
||||
|
||||
@@ -37,6 +37,7 @@
|
||||
|
||||
#include <QtCore/QObject>
|
||||
#include <QtCore/QDebug>
|
||||
#include <QtCore/QList>
|
||||
#include <QtCore/QPointer>
|
||||
#include <QtGui/QAction>
|
||||
|
||||
@@ -61,15 +62,15 @@ public:
|
||||
|
||||
public slots:
|
||||
// Called from Engine
|
||||
void addLazyData(quint64 addr, const QByteArray &data);
|
||||
void addLazyData(QObject *editorToken, quint64 addr, const QByteArray &data);
|
||||
// Called from Editor
|
||||
void fetchLazyData(quint64 block, bool sync);
|
||||
void fetchLazyData(Core::IEditor *, quint64 block, bool sync);
|
||||
|
||||
private:
|
||||
void init(quint64 startaddr);
|
||||
Q_SLOT void createBinEditor(quint64 startAddr);
|
||||
|
||||
QPointer<IDebuggerEngine> m_engine;
|
||||
QPointer<Core::IEditor> m_editor;
|
||||
QList<QPointer<Core::IEditor> > m_editors;
|
||||
QPointer<DebuggerManager> m_manager;
|
||||
};
|
||||
|
||||
|
||||
@@ -3542,21 +3542,23 @@ void GdbEngine::handleWatchPoint(const GdbResponse &response)
|
||||
|
||||
struct MemoryAgentCookie
|
||||
{
|
||||
MemoryAgentCookie() : agent(0), address(0) {}
|
||||
MemoryAgentCookie(MemoryViewAgent *agent_, quint64 address_)
|
||||
: agent(agent_), address(address_)
|
||||
MemoryAgentCookie() : agent(0), token(0), address(0) {}
|
||||
MemoryAgentCookie(MemoryViewAgent *agent_, QObject *token_, quint64 address_)
|
||||
: agent(agent_), token(token_), address(address_)
|
||||
{}
|
||||
QPointer<MemoryViewAgent> agent;
|
||||
QPointer<QObject> token;
|
||||
quint64 address;
|
||||
};
|
||||
|
||||
void GdbEngine::fetchMemory(MemoryViewAgent *agent, quint64 addr, quint64 length)
|
||||
void GdbEngine::fetchMemory(MemoryViewAgent *agent, QObject *token, quint64 addr,
|
||||
quint64 length)
|
||||
{
|
||||
//qDebug() << "GDB MEMORY FETCH" << agent << addr << length;
|
||||
postCommand("-data-read-memory " + QByteArray::number(addr) + " x 1 1 "
|
||||
+ QByteArray::number(length),
|
||||
NeedsStop, CB(handleFetchMemory),
|
||||
QVariant::fromValue(MemoryAgentCookie(agent, addr)));
|
||||
QVariant::fromValue(MemoryAgentCookie(agent, token, addr)));
|
||||
}
|
||||
|
||||
void GdbEngine::handleFetchMemory(const GdbResponse &response)
|
||||
@@ -3581,7 +3583,7 @@ void GdbEngine::handleFetchMemory(const GdbResponse &response)
|
||||
QTC_ASSERT(ok, return);
|
||||
ba.append(c);
|
||||
}
|
||||
ac.agent->addLazyData(ac.address, ba);
|
||||
ac.agent->addLazyData(ac.token, ac.address, ba);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -428,7 +428,8 @@ private: ////////// View & Data Stuff //////////
|
||||
|
||||
virtual void assignValueInDebugger(const QString &expr, const QString &value);
|
||||
|
||||
virtual void fetchMemory(MemoryViewAgent *agent, quint64 addr, quint64 length);
|
||||
virtual void fetchMemory(MemoryViewAgent *agent, QObject *token,
|
||||
quint64 addr, quint64 length);
|
||||
void handleFetchMemory(const GdbResponse &response);
|
||||
|
||||
virtual void watchPoint(const QPoint &);
|
||||
|
||||
@@ -112,7 +112,7 @@ public:
|
||||
virtual void reloadFullStack() = 0;
|
||||
|
||||
virtual void watchPoint(const QPoint &) {}
|
||||
virtual void fetchMemory(MemoryViewAgent *, quint64 addr, quint64 length)
|
||||
virtual void fetchMemory(MemoryViewAgent *, QObject *, quint64 addr, quint64 length)
|
||||
{ Q_UNUSED(addr); Q_UNUSED(length); }
|
||||
virtual void fetchDisassembler(DisassemblerViewAgent *) {}
|
||||
virtual void setRegisterValue(int regnr, const QString &value)
|
||||
|
||||
Reference in New Issue
Block a user