Debugger: Add edit mode support to MemoryView.
MemoryView: - Add hooks and supporting status tracking members to enable edit mode. When editing is requested, we allocate a duplicate copy of the current block's data to perform edits in. Currently, editing is only supported from within the hex view, and when edit mode is enabled, the view is locked to 8-bit hex mode in order to avoid any possible confusion with regards to source vs target endian orientation. - Extend Draw() to determine whether to write data from the edit data store or the actual memory block. Also implement highlighting the current edit position caret when in edit mode, as well as highlighting bytes that have been changed compared to the block's original data.
This commit is contained in:
@@ -8,6 +8,7 @@
|
|||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
|
||||||
|
#include <ctype.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
|
||||||
#include <ByteOrder.h>
|
#include <ByteOrder.h>
|
||||||
@@ -42,7 +43,11 @@ MemoryView::MemoryView(::Team* team, Listener* listener)
|
|||||||
| B_SUBPIXEL_PRECISE),
|
| B_SUBPIXEL_PRECISE),
|
||||||
fTeam(team),
|
fTeam(team),
|
||||||
fTargetBlock(NULL),
|
fTargetBlock(NULL),
|
||||||
|
fEditableData(NULL),
|
||||||
|
fEditedOffsets(),
|
||||||
fTargetAddress(0LL),
|
fTargetAddress(0LL),
|
||||||
|
fEditMode(false),
|
||||||
|
fEditLowNybble(false),
|
||||||
fCharWidth(0.0),
|
fCharWidth(0.0),
|
||||||
fLineHeight(0.0),
|
fLineHeight(0.0),
|
||||||
fTextCharsPerLine(0),
|
fTextCharsPerLine(0),
|
||||||
@@ -67,6 +72,8 @@ MemoryView::~MemoryView()
|
|||||||
{
|
{
|
||||||
if (fTargetBlock != NULL)
|
if (fTargetBlock != NULL)
|
||||||
fTargetBlock->ReleaseReference();
|
fTargetBlock->ReleaseReference();
|
||||||
|
|
||||||
|
delete[] fEditableData;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -111,6 +118,32 @@ MemoryView::UnsetListener()
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
MemoryView::SetEditMode(bool enabled)
|
||||||
|
{
|
||||||
|
if (fTargetBlock == NULL)
|
||||||
|
return B_BAD_VALUE;
|
||||||
|
else if (fEditMode == enabled)
|
||||||
|
return B_OK;
|
||||||
|
|
||||||
|
if (enabled) {
|
||||||
|
status_t error = _SetupEditableData();
|
||||||
|
if (error != B_OK)
|
||||||
|
return error;
|
||||||
|
} else {
|
||||||
|
delete[] fEditableData;
|
||||||
|
fEditableData = NULL;
|
||||||
|
fEditedOffsets.clear();
|
||||||
|
fEditLowNybble = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
fEditMode = enabled;
|
||||||
|
Invalidate();
|
||||||
|
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void
|
void
|
||||||
MemoryView::AttachedToWindow()
|
MemoryView::AttachedToWindow()
|
||||||
{
|
{
|
||||||
@@ -148,13 +181,15 @@ MemoryView::Draw(BRect rect)
|
|||||||
char buffer[32];
|
char buffer[32];
|
||||||
char textbuffer[512];
|
char textbuffer[512];
|
||||||
|
|
||||||
|
const char* dataSource = (const char*)(fEditMode ? fEditableData
|
||||||
|
: fTargetBlock->Data());
|
||||||
|
|
||||||
int32 startLine = int32(rect.top / fLineHeight);
|
int32 startLine = int32(rect.top / fLineHeight);
|
||||||
const char* currentAddress = (const char*)(fTargetBlock->Data()
|
const char* currentAddress = dataSource + fHexBlocksPerLine
|
||||||
+ fHexBlocksPerLine * blockByteSize * startLine);
|
* blockByteSize * startLine;
|
||||||
const char* maxAddress = (const char*)(fTargetBlock->Data()
|
const char* maxAddress = dataSource + fTargetBlock->Size();
|
||||||
+ fTargetBlock->Size());
|
const char* targetAddress = dataSource + fTargetAddress
|
||||||
const char* targetAddress = (const char *)fTargetBlock->Data()
|
- fTargetBlock->BaseAddress();
|
||||||
+ fTargetAddress - fTargetBlock->BaseAddress();
|
|
||||||
BPoint drawPoint(1.0, (startLine + 1) * fLineHeight);
|
BPoint drawPoint(1.0, (startLine + 1) * fLineHeight);
|
||||||
int32 currentBlocksPerLine = fHexBlocksPerLine;
|
int32 currentBlocksPerLine = fHexBlocksPerLine;
|
||||||
int32 currentCharsPerLine = fTextCharsPerLine;
|
int32 currentCharsPerLine = fTextCharsPerLine;
|
||||||
@@ -164,6 +199,8 @@ MemoryView::Draw(BRect rect)
|
|||||||
GetFontHeight(&fh);
|
GetFontHeight(&fh);
|
||||||
target_addr_t lineAddress = fTargetBlock->BaseAddress() + startLine
|
target_addr_t lineAddress = fTargetBlock->BaseAddress() + startLine
|
||||||
* currentCharsPerLine;
|
* currentCharsPerLine;
|
||||||
|
bool highlightBlock = false;
|
||||||
|
rgb_color highlightColor;
|
||||||
for (; currentAddress < maxAddress && drawPoint.y < rect.bottom
|
for (; currentAddress < maxAddress && drawPoint.y < rect.bottom
|
||||||
+ fLineHeight; drawPoint.y += fLineHeight) {
|
+ fLineHeight; drawPoint.y += fLineHeight) {
|
||||||
drawPoint.x = 1.0;
|
drawPoint.x = 1.0;
|
||||||
@@ -186,14 +223,34 @@ MemoryView::Draw(BRect rect)
|
|||||||
const char* blockAddress = currentAddress + (j
|
const char* blockAddress = currentAddress + (j
|
||||||
* blockByteSize);
|
* blockByteSize);
|
||||||
_GetNextHexBlock(buffer, sizeof(buffer), blockAddress);
|
_GetNextHexBlock(buffer, sizeof(buffer), blockAddress);
|
||||||
if (targetAddress >= blockAddress && targetAddress <
|
|
||||||
|
highlightBlock = false;
|
||||||
|
if (fEditMode)
|
||||||
|
{
|
||||||
|
int32 offset = blockAddress - dataSource;
|
||||||
|
for (uint32 i = 0; i < blockByteSize; i++) {
|
||||||
|
if (fEditedOffsets.count(offset + i) != 0) {
|
||||||
|
highlightBlock = true;
|
||||||
|
highlightColor.set_to(0, 216, 0);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if (targetAddress >= blockAddress && targetAddress <
|
||||||
blockAddress + blockByteSize) {
|
blockAddress + blockByteSize) {
|
||||||
|
highlightBlock = true;
|
||||||
|
highlightColor.set_to(216, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (highlightBlock) {
|
||||||
PushState();
|
PushState();
|
||||||
SetHighColor(make_color(216,0,0));
|
SetHighColor(highlightColor);
|
||||||
|
}
|
||||||
|
|
||||||
DrawString(buffer, drawPoint);
|
DrawString(buffer, drawPoint);
|
||||||
|
|
||||||
|
if (highlightBlock)
|
||||||
PopState();
|
PopState();
|
||||||
} else
|
|
||||||
DrawString(buffer, drawPoint);
|
|
||||||
|
|
||||||
drawPoint.x += fCharWidth * hexBlockSize;
|
drawPoint.x += fCharWidth * hexBlockSize;
|
||||||
}
|
}
|
||||||
@@ -248,6 +305,16 @@ MemoryView::Draw(BRect rect)
|
|||||||
FillRegion(&selectionRegion, B_SOLID_HIGH);
|
FillRegion(&selectionRegion, B_SOLID_HIGH);
|
||||||
PopState();
|
PopState();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (fEditMode) {
|
||||||
|
PushState();
|
||||||
|
BRect caretRect;
|
||||||
|
_GetEditCaretRect(caretRect);
|
||||||
|
SetDrawingMode(B_OP_INVERT);
|
||||||
|
FillRect(caretRect, B_SOLID_HIGH);
|
||||||
|
PopState();
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -286,11 +353,21 @@ MemoryView::KeyDown(const char* bytes, int32 numBytes)
|
|||||||
}
|
}
|
||||||
case B_LEFT_ARROW:
|
case B_LEFT_ARROW:
|
||||||
{
|
{
|
||||||
|
if (fEditMode) {
|
||||||
|
if (!fEditLowNybble)
|
||||||
|
newAddress--;
|
||||||
|
fEditLowNybble = !fEditLowNybble;
|
||||||
|
} else
|
||||||
newAddress -= blockSize;
|
newAddress -= blockSize;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case B_RIGHT_ARROW:
|
case B_RIGHT_ARROW:
|
||||||
{
|
{
|
||||||
|
if (fEditMode) {
|
||||||
|
if (fEditLowNybble)
|
||||||
|
newAddress++;
|
||||||
|
fEditLowNybble = !fEditLowNybble;
|
||||||
|
} else
|
||||||
newAddress += blockSize;
|
newAddress += blockSize;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -307,19 +384,59 @@ MemoryView::KeyDown(const char* bytes, int32 numBytes)
|
|||||||
case B_HOME:
|
case B_HOME:
|
||||||
{
|
{
|
||||||
newAddress = fTargetBlock->BaseAddress();
|
newAddress = fTargetBlock->BaseAddress();
|
||||||
|
fEditLowNybble = false;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case B_END:
|
case B_END:
|
||||||
{
|
{
|
||||||
newAddress = maxAddress;
|
newAddress = maxAddress;
|
||||||
|
fEditLowNybble = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
{
|
{
|
||||||
|
if (fEditMode && isxdigit(bytes[0]))
|
||||||
|
{
|
||||||
|
int value = 0;
|
||||||
|
if (isdigit(bytes[0]))
|
||||||
|
value = bytes[0] - '0';
|
||||||
|
else
|
||||||
|
value = (int)strtol(bytes, NULL, 16);
|
||||||
|
|
||||||
|
int32 byteOffset = fTargetAddress
|
||||||
|
- fTargetBlock->BaseAddress();
|
||||||
|
|
||||||
|
if (fEditLowNybble)
|
||||||
|
value = (fEditableData[byteOffset] & 0xf0) | value;
|
||||||
|
else {
|
||||||
|
value = (fEditableData[byteOffset] & 0x0f)
|
||||||
|
| (value << 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
fEditableData[byteOffset] = value;
|
||||||
|
|
||||||
|
if (fEditableData[byteOffset]
|
||||||
|
!= fTargetBlock->Data()[byteOffset]) {
|
||||||
|
fEditedOffsets.insert(byteOffset);
|
||||||
|
} else
|
||||||
|
fEditedOffsets.erase(byteOffset);
|
||||||
|
|
||||||
|
if (fEditLowNybble) {
|
||||||
|
if (newAddress < maxAddress) {
|
||||||
|
newAddress++;
|
||||||
|
fEditLowNybble = false;
|
||||||
|
}
|
||||||
|
} else
|
||||||
|
fEditLowNybble = true;
|
||||||
|
|
||||||
|
Invalidate();
|
||||||
|
} else
|
||||||
handled = false;
|
handled = false;
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (handled) {
|
if (handled) {
|
||||||
if (newAddress < fTargetBlock->BaseAddress())
|
if (newAddress < fTargetBlock->BaseAddress())
|
||||||
newAddress = fTargetAddress;
|
newAddress = fTargetAddress;
|
||||||
@@ -370,11 +487,21 @@ MemoryView::MessageReceived(BMessage* message)
|
|||||||
}
|
}
|
||||||
case MSG_SET_HEX_MODE:
|
case MSG_SET_HEX_MODE:
|
||||||
{
|
{
|
||||||
|
// while editing, hex view changes are disallowed.
|
||||||
|
if (fEditMode)
|
||||||
|
break;
|
||||||
|
|
||||||
int32 mode;
|
int32 mode;
|
||||||
if (message->FindInt32("mode", &mode) == B_OK) {
|
if (message->FindInt32("mode", &mode) == B_OK) {
|
||||||
|
if (fHexMode == mode)
|
||||||
|
break;
|
||||||
|
|
||||||
fHexMode = mode;
|
fHexMode = mode;
|
||||||
_RecalcScrollBars();
|
_RecalcScrollBars();
|
||||||
Invalidate();
|
Invalidate();
|
||||||
|
|
||||||
|
if (fListener != NULL)
|
||||||
|
fListener->HexModeChanged(mode);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -382,8 +509,14 @@ MemoryView::MessageReceived(BMessage* message)
|
|||||||
{
|
{
|
||||||
int32 mode;
|
int32 mode;
|
||||||
if (message->FindInt32("mode", &mode) == B_OK) {
|
if (message->FindInt32("mode", &mode) == B_OK) {
|
||||||
|
if (fCurrentEndianMode == mode)
|
||||||
|
break;
|
||||||
|
|
||||||
fCurrentEndianMode = mode;
|
fCurrentEndianMode = mode;
|
||||||
Invalidate();
|
Invalidate();
|
||||||
|
|
||||||
|
if (fListener != NULL)
|
||||||
|
fListener->EndianModeChanged(mode);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -391,9 +524,15 @@ MemoryView::MessageReceived(BMessage* message)
|
|||||||
{
|
{
|
||||||
int32 mode;
|
int32 mode;
|
||||||
if (message->FindInt32("mode", &mode) == B_OK) {
|
if (message->FindInt32("mode", &mode) == B_OK) {
|
||||||
|
if (fTextMode == mode)
|
||||||
|
break;
|
||||||
|
|
||||||
fTextMode = mode;
|
fTextMode = mode;
|
||||||
_RecalcScrollBars();
|
_RecalcScrollBars();
|
||||||
Invalidate();
|
Invalidate();
|
||||||
|
|
||||||
|
if (fListener != NULL)
|
||||||
|
fListener->TextModeChanged(mode);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -593,7 +732,7 @@ MemoryView::_RecalcScrollBars()
|
|||||||
|
|
||||||
void
|
void
|
||||||
MemoryView::_GetNextHexBlock(char* buffer, int32 bufferSize,
|
MemoryView::_GetNextHexBlock(char* buffer, int32 bufferSize,
|
||||||
const char* address)
|
const char* address) const
|
||||||
{
|
{
|
||||||
switch(fHexMode) {
|
switch(fHexMode) {
|
||||||
case HexMode8BitInt:
|
case HexMode8BitInt:
|
||||||
@@ -765,7 +904,25 @@ MemoryView::_GetAddressDisplayWidth() const
|
|||||||
|
|
||||||
|
|
||||||
void
|
void
|
||||||
MemoryView::_GetSelectionRegion(BRegion& region)
|
MemoryView::_GetEditCaretRect(BRect& rect) const
|
||||||
|
{
|
||||||
|
if (!fEditMode)
|
||||||
|
return;
|
||||||
|
|
||||||
|
int32 byteOffset = fTargetAddress - fTargetBlock->BaseAddress();
|
||||||
|
BPoint point = _GetPointForOffset(byteOffset);
|
||||||
|
if (fEditLowNybble)
|
||||||
|
point.x += fCharWidth;
|
||||||
|
|
||||||
|
rect.left = point.x;
|
||||||
|
rect.right = point.x + fCharWidth;
|
||||||
|
rect.top = point.y;
|
||||||
|
rect.bottom = point.y + fLineHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
MemoryView::_GetSelectionRegion(BRegion& region) const
|
||||||
{
|
{
|
||||||
if (fHexMode == HexModeNone || fTargetBlock == NULL)
|
if (fHexMode == HexModeNone || fTargetBlock == NULL)
|
||||||
return;
|
return;
|
||||||
@@ -812,14 +969,15 @@ MemoryView::_GetSelectionRegion(BRegion& region)
|
|||||||
|
|
||||||
|
|
||||||
void
|
void
|
||||||
MemoryView::_GetSelectedText(BString& text)
|
MemoryView::_GetSelectedText(BString& text) const
|
||||||
{
|
{
|
||||||
if (fSelectionStart == fSelectionEnd)
|
if (fSelectionStart == fSelectionEnd)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
text.Truncate(0);
|
text.Truncate(0);
|
||||||
|
const uint8* dataSource = fEditMode ? fEditableData : fTargetBlock->Data();
|
||||||
|
|
||||||
char* data = (char *)fTargetBlock->Data() + fSelectionStart;
|
const char* data = (const char *)dataSource + fSelectionStart;
|
||||||
int16 blockSize = _GetHexDigitsPerBlock() / 2;
|
int16 blockSize = _GetHexDigitsPerBlock() / 2;
|
||||||
int32 count = (fSelectionEnd - fSelectionStart)
|
int32 count = (fSelectionEnd - fSelectionStart)
|
||||||
/ blockSize;
|
/ blockSize;
|
||||||
@@ -959,6 +1117,27 @@ MemoryView::_HandleContextMenu(BPoint point)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
MemoryView::_SetupEditableData()
|
||||||
|
{
|
||||||
|
fEditableData = new(std::nothrow) uint8[fTargetBlock->Size()];
|
||||||
|
if (fEditableData == NULL)
|
||||||
|
return B_NO_MEMORY;
|
||||||
|
|
||||||
|
memcpy(fEditableData, fTargetBlock->Data(), fTargetBlock->Size());
|
||||||
|
|
||||||
|
if (fHexMode != HexMode8BitInt) {
|
||||||
|
fHexMode = HexMode8BitInt;
|
||||||
|
if (fListener != NULL)
|
||||||
|
fListener->HexModeChanged(fHexMode);
|
||||||
|
|
||||||
|
_RecalcScrollBars();
|
||||||
|
}
|
||||||
|
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
//#pragma mark - Listener
|
//#pragma mark - Listener
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -7,6 +7,8 @@
|
|||||||
#define MEMORY_VIEW_H
|
#define MEMORY_VIEW_H
|
||||||
|
|
||||||
|
|
||||||
|
#include <set>
|
||||||
|
|
||||||
#include <View.h>
|
#include <View.h>
|
||||||
|
|
||||||
#include "Types.h"
|
#include "Types.h"
|
||||||
@@ -24,7 +26,6 @@ enum {
|
|||||||
HexMode16BitInt,
|
HexMode16BitInt,
|
||||||
HexMode32BitInt,
|
HexMode32BitInt,
|
||||||
HexMode64BitInt
|
HexMode64BitInt
|
||||||
// TODO: floating point representation?
|
|
||||||
};
|
};
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
@@ -56,9 +57,18 @@ public:
|
|||||||
|
|
||||||
void SetTargetAddress(TeamMemoryBlock* block,
|
void SetTargetAddress(TeamMemoryBlock* block,
|
||||||
target_addr_t address);
|
target_addr_t address);
|
||||||
|
|
||||||
void UnsetListener();
|
void UnsetListener();
|
||||||
|
|
||||||
|
inline bool GetEditMode() const
|
||||||
|
{ return fEditMode; }
|
||||||
|
status_t SetEditMode(bool enabled);
|
||||||
|
|
||||||
|
inline void* GetEditedData() const
|
||||||
|
{ return fEditableData; }
|
||||||
|
|
||||||
|
void CommitChanges();
|
||||||
|
void RevertChanges();
|
||||||
|
|
||||||
virtual void AttachedToWindow();
|
virtual void AttachedToWindow();
|
||||||
virtual void Draw(BRect rect);
|
virtual void Draw(BRect rect);
|
||||||
virtual void FrameResized(float width, float height);
|
virtual void FrameResized(float width, float height);
|
||||||
@@ -81,7 +91,8 @@ private:
|
|||||||
void _Init();
|
void _Init();
|
||||||
void _RecalcScrollBars();
|
void _RecalcScrollBars();
|
||||||
void _GetNextHexBlock(char* buffer,
|
void _GetNextHexBlock(char* buffer,
|
||||||
int32 bufferSize, const char* address);
|
int32 bufferSize,
|
||||||
|
const char* address) const;
|
||||||
|
|
||||||
int32 _GetOffsetAt(BPoint point) const;
|
int32 _GetOffsetAt(BPoint point) const;
|
||||||
BPoint _GetPointForOffset(int32 offset) const;
|
BPoint _GetPointForOffset(int32 offset) const;
|
||||||
@@ -91,18 +102,28 @@ private:
|
|||||||
inline int32 _GetHexDigitsPerBlock() const
|
inline int32 _GetHexDigitsPerBlock() const
|
||||||
{ return 1 << fHexMode; };
|
{ return 1 << fHexMode; };
|
||||||
|
|
||||||
void _GetSelectionRegion(BRegion& region);
|
void _GetEditCaretRect(BRect& rect) const;
|
||||||
void _GetSelectedText(BString& text);
|
void _GetSelectionRegion(BRegion& region) const;
|
||||||
|
void _GetSelectedText(BString& text) const;
|
||||||
void _CopySelectionToClipboard();
|
void _CopySelectionToClipboard();
|
||||||
|
|
||||||
void _HandleAutoScroll();
|
void _HandleAutoScroll();
|
||||||
void _ScrollByLines(int32 lineCount);
|
void _ScrollByLines(int32 lineCount);
|
||||||
void _HandleContextMenu(BPoint point);
|
void _HandleContextMenu(BPoint point);
|
||||||
|
|
||||||
|
status_t _SetupEditableData();
|
||||||
|
|
||||||
|
private:
|
||||||
|
typedef std::set<int32> ModifiedIndexSet;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
::Team* fTeam;
|
::Team* fTeam;
|
||||||
TeamMemoryBlock* fTargetBlock;
|
TeamMemoryBlock* fTargetBlock;
|
||||||
|
uint8* fEditableData;
|
||||||
|
ModifiedIndexSet fEditedOffsets;
|
||||||
target_addr_t fTargetAddress;
|
target_addr_t fTargetAddress;
|
||||||
|
bool fEditMode;
|
||||||
|
bool fEditLowNybble;
|
||||||
float fCharWidth;
|
float fCharWidth;
|
||||||
float fLineHeight;
|
float fLineHeight;
|
||||||
int32 fTargetAddressSize;
|
int32 fTargetAddressSize;
|
||||||
|
|||||||
Reference in New Issue
Block a user