/*
 * Copyright 2011-2015, Rene Gollent, rene@gollent.com. All rights reserved.
 * Distributed under the terms of the MIT License.
 */
 
 
#include "MemoryView.h"
 
#include <algorithm>
 
#include <ctype.h>
#include <stdio.h>
 
#include <ByteOrder.h>
#include <Clipboard.h>
#include <Looper.h>
#include <MenuItem.h>
#include <MessageRunner.h>
#include <Messenger.h>
#include <PopUpMenu.h>
#include <Region.h>
#include <ScrollView.h>
#include <String.h>
 
#include "Architecture.h"
#include "AutoDeleter.h"
#include "MessageCodes.h"
#include "Team.h"
#include "TeamMemoryBlock.h"
 
 
enum {
	MSG_TARGET_ADDRESS_CHANGED	= 'mtac',
	MSG_VIEW_AUTOSCROLL			= 'mvas'
};
 
static const bigtime_t kScrollTimer = 10000LL;
 
 
MemoryView::MemoryView(::Team* team, Listener* listener)
	:
	BView("memoryView", B_WILL_DRAW | B_FRAME_EVENTS | B_NAVIGABLE
		| B_SUBPIXEL_PRECISE),
	fTeam(team),
	fTargetBlock(NULL),
	fEditableData(NULL),
	fEditedOffsets(),
	fTargetAddress(0LL),
	fEditMode(false),
	fEditLowNybble(false),
	fCharWidth(0.0),
	fLineHeight(0.0),
	fTextCharsPerLine(0),
	fHexBlocksPerLine(0),
	fHexMode(HexMode8BitInt),
	fTextMode(TextModeASCII),
	fSelectionBase(0),
	fSelectionStart(0),
	fSelectionEnd(0),
	fScrollRunner(NULL),
	fTrackingMouse(false),
	fListener(listener)
{
	Architecture* architecture = team->GetArchitecture();
	fTargetAddressSize = architecture->AddressSize() * 2;
	fCurrentEndianMode = architecture->IsBigEndian()
		? EndianModeBigEndian : EndianModeLittleEndian;
 
}
 
 
MemoryView::~MemoryView()
{
	if (fTargetBlock != NULL)
		fTargetBlock->ReleaseReference();
 
	delete[] fEditableData;
}
 
 
/*static */ MemoryView*
MemoryView::Create(::Team* team, Listener* listener)
{
	MemoryView* self = new MemoryView(team, listener);
 
	try {
		self->_Init();
	} catch(...) {
		delete self;
		throw;
	}
 
	return self;
}
 
 
void
MemoryView::SetTargetAddress(TeamMemoryBlock* block, target_addr_t address)
{
	fTargetAddress = address;
	if (block != fTargetBlock) {
		if (fTargetBlock != NULL)
			fTargetBlock->ReleaseReference();
 
		fTargetBlock = block;
		if (block != NULL)
			fTargetBlock->AcquireReference();
	}
 
	MakeFocus(true);
	BMessenger(this).SendMessage(MSG_TARGET_ADDRESS_CHANGED);
}
 
 
void
MemoryView::UnsetListener()
{
	fListener = NULL;
}
 
 
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
MemoryView::AttachedToWindow()
{
	BView::AttachedToWindow();
	SetViewUIColor(B_DOCUMENT_BACKGROUND_COLOR);
	SetFont(be_fixed_font);
	fCharWidth = be_fixed_font->StringWidth("a");
	font_height fontHeight;
	be_fixed_font->GetHeight(&fontHeight);
	fLineHeight = ceilf(fontHeight.ascent + fontHeight.descent
		+ fontHeight.leading);
}
 
 
void
MemoryView::Draw(BRect rect)
{
	rect = Bounds();
 
	float divider = (fTargetAddressSize + 1) * fCharWidth;
	StrokeLine(BPoint(divider, rect.top),
				BPoint(divider, rect.bottom));
 
	if (fTargetBlock == NULL)
		return;
 
	uint32 hexBlockSize = _GetHexDigitsPerBlock() + 1;
	uint32 blockByteSize = hexBlockSize / 2;
	if (fHexMode != HexModeNone && fTextMode != TextModeNone) {
		divider += (fHexBlocksPerLine * hexBlockSize + 1) * fCharWidth;
		StrokeLine(BPoint(divider, rect.top),
					BPoint(divider, rect.bottom));
	}
 
	char buffer[32];
	char textbuffer[512];
 
	const char* dataSource = (const char*)(fEditMode ? fEditableData
			: fTargetBlock->Data());
 
	int32 startLine = int32(rect.top / fLineHeight);
	const char* currentAddress = dataSource + fHexBlocksPerLine
		* blockByteSize * startLine;
	const char* maxAddress = dataSource + fTargetBlock->Size();
	const char* targetAddress = dataSource + fTargetAddress
		- fTargetBlock->BaseAddress();
	BPoint drawPoint(1.0, (startLine + 1) * fLineHeight);
	int32 currentBlocksPerLine = fHexBlocksPerLine;
	int32 currentCharsPerLine = fTextCharsPerLine;
	font_height fh;
	GetFontHeight(&fh);
	target_addr_t lineAddress = fTargetBlock->BaseAddress() + startLine
		* currentCharsPerLine;
	bool highlightBlock = false;
	rgb_color highlightColor;
	for (; currentAddress < maxAddress && drawPoint.y < rect.bottom
		+ fLineHeight; drawPoint.y += fLineHeight) {
		drawPoint.x = 1.0;
		snprintf(buffer, sizeof(buffer), "%0*" B_PRIx64,
			(int)fTargetAddressSize, lineAddress);
		PushState();
		SetHighColor(tint_color(HighColor(), B_LIGHTEN_1_TINT));
		DrawString(buffer, drawPoint);
		drawPoint.x += fCharWidth * (fTargetAddressSize + 2);
		PopState();
		if (fHexMode != HexModeNone) {
			if (currentAddress + (currentBlocksPerLine * blockByteSize)
				> maxAddress) {
				currentCharsPerLine = maxAddress - currentAddress;
				currentBlocksPerLine = currentCharsPerLine
					/ blockByteSize;
			}
 
			for (int32 j = 0; j < currentBlocksPerLine; j++) {
				const char* blockAddress = currentAddress + (j
					* blockByteSize);
				_GetNextHexBlock(buffer, sizeof(buffer), blockAddress);
 
				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) {
						highlightBlock = true;
						highlightColor.set_to(216, 0, 0);
				}
 
				if (highlightBlock) {
					PushState();
					SetHighColor(highlightColor);
				}
 
				DrawString(buffer, drawPoint);
 
				if (highlightBlock)
					PopState();
 
				drawPoint.x += fCharWidth * hexBlockSize;
			}
 
			if (currentBlocksPerLine < fHexBlocksPerLine)
				drawPoint.x += fCharWidth * hexBlockSize
					* (fHexBlocksPerLine - currentBlocksPerLine);
		}
 
		if (fTextMode != TextModeNone) {
			drawPoint.x += fCharWidth;
			for (int32 j = 0; j < currentCharsPerLine; j++) {
				// filter non-printable characters
				textbuffer[j] = currentAddress[j] > 32 ? currentAddress[j]
					: '.';
			}
			textbuffer[fTextCharsPerLine] = '\0';
			DrawString(textbuffer, drawPoint);
			if (targetAddress >= currentAddress && targetAddress
				< currentAddress + currentCharsPerLine) {
				PushState();
				SetHighColor(B_TRANSPARENT_COLOR);
				SetDrawingMode(B_OP_INVERT);
				uint32 blockAddress = uint32(targetAddress - currentAddress);
				if (fHexMode != HexModeNone)
					blockAddress &= ~(blockByteSize - 1);
				float startX = drawPoint.x + fCharWidth * blockAddress;
				float endX = startX;
				if (fHexMode != HexModeNone)
					endX += fCharWidth * ((hexBlockSize - 1) / 2);
				else
					endX += fCharWidth;
				FillRect(BRect(startX, drawPoint.y - fh.ascent, endX,
					drawPoint.y + fh.descent));
				PopState();
			}
		}
		if (currentBlocksPerLine > 0) {
			currentAddress += currentBlocksPerLine * blockByteSize;
			lineAddress += currentBlocksPerLine * blockByteSize;
		} else {
			currentAddress += fTextCharsPerLine;
			lineAddress += fTextCharsPerLine;
		}
	}
 
	if (fSelectionStart != fSelectionEnd) {
		PushState();
		BRegion selectionRegion;
		_GetSelectionRegion(selectionRegion);
		SetDrawingMode(B_OP_INVERT);
		FillRegion(&selectionRegion, B_SOLID_HIGH);
		PopState();
	}
 
	if (fEditMode) {
		PushState();
		BRect caretRect;
		_GetEditCaretRect(caretRect);
		SetDrawingMode(B_OP_INVERT);
		FillRect(caretRect, B_SOLID_HIGH);
		PopState();
 
	}
}
 
 
void
MemoryView::FrameResized(float width, float height)
{
	BView::FrameResized(width, height);
	_RecalcScrollBars();
	Invalidate();
}
 
 
void
MemoryView::KeyDown(const char* bytes, int32 numBytes)
{
	bool handled = true;
	if (fTargetBlock != NULL) {
		target_addr_t newAddress = fTargetAddress;
		target_addr_t maxAddress = fTargetBlock->BaseAddress()
			+ fTargetBlock->Size() - 1;
		int32 blockSize = 1;
		if (fHexMode != HexModeNone)
			blockSize = 1 << (fHexMode - 1);
		int32 lineCount = int32(Bounds().Height() / fLineHeight);
 
		switch(bytes[0]) {
			case B_UP_ARROW:
			{
				newAddress -= blockSize * fHexBlocksPerLine;
				break;
			}
			case B_DOWN_ARROW:
			{
				newAddress += blockSize * fHexBlocksPerLine;
				break;
			}
			case B_LEFT_ARROW:
			{
				if (fEditMode) {
					if (!fEditLowNybble)
						newAddress--;
					fEditLowNybble = !fEditLowNybble;
					if (newAddress == fTargetAddress)
						Invalidate();
				} else
					newAddress -= blockSize;
				break;
			}
			case B_RIGHT_ARROW:
			{
				if (fEditMode) {
					if (fEditLowNybble)
						newAddress++;
					fEditLowNybble = !fEditLowNybble;
					if (newAddress == fTargetAddress)
						Invalidate();
				} else
					newAddress += blockSize;
				break;
			}
			case B_PAGE_UP:
			{
				newAddress -= (blockSize * fHexBlocksPerLine) * lineCount;
				break;
			}
			case B_PAGE_DOWN:
			{
				newAddress += (blockSize * fHexBlocksPerLine) * lineCount;
				break;
			}
			case B_HOME:
			{
				newAddress = fTargetBlock->BaseAddress();
				fEditLowNybble = false;
				break;
			}
			case B_END:
			{
				newAddress = maxAddress;
				fEditLowNybble = true;
				break;
			}
			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;
 
				break;
			}
		}
 
		if (handled) {
			if (newAddress < fTargetBlock->BaseAddress())
				newAddress = fTargetAddress;
			else if (newAddress > maxAddress)
				newAddress = maxAddress;
 
			if (newAddress != fTargetAddress) {
				fTargetAddress = newAddress;
				BMessenger(this).SendMessage(MSG_TARGET_ADDRESS_CHANGED);
			}
		}
	} else
		handled = false;
 
	if (!handled)
		BView::KeyDown(bytes, numBytes);
}
 
 
void
MemoryView::MakeFocus(bool isFocused)
{
	BScrollView* parent = dynamic_cast<BScrollView*>(Parent());
	if (parent != NULL)
		parent->SetBorderHighlighted(isFocused);
 
	BView::MakeFocus(isFocused);
}
 
 
void
MemoryView::MessageReceived(BMessage* message)
{
	switch(message->what) {
		case B_COPY:
		{
			_CopySelectionToClipboard();
			break;
		}
		case MSG_TARGET_ADDRESS_CHANGED:
		{
			_RecalcScrollBars();
			ScrollToSelection();
			Invalidate();
			if (fListener != NULL)
				fListener->TargetAddressChanged(fTargetAddress);
			break;
		}
		case MSG_SET_HEX_MODE:
		{
			// while editing, hex view changes are disallowed.
			if (fEditMode)
				break;
 
			int32 mode;
			if (message->FindInt32("mode", &mode) == B_OK) {
				if (fHexMode == mode)
					break;
 
				fHexMode = mode;
				_RecalcScrollBars();
				Invalidate();
 
				if (fListener != NULL)
					fListener->HexModeChanged(mode);
			}
			break;
		}
		case MSG_SET_ENDIAN_MODE:
		{
			int32 mode;
			if (message->FindInt32("mode", &mode) == B_OK) {
				if (fCurrentEndianMode == mode)
					break;
 
				fCurrentEndianMode = mode;
				Invalidate();
 
				if (fListener != NULL)
					fListener->EndianModeChanged(mode);
			}
			break;
		}
		case MSG_SET_TEXT_MODE:
		{
			int32 mode;
			if (message->FindInt32("mode", &mode) == B_OK) {
				if (fTextMode == mode)
					break;
 
				fTextMode = mode;
				_RecalcScrollBars();
				Invalidate();
 
				if (fListener != NULL)
					fListener->TextModeChanged(mode);
			}
			break;
		}
		case MSG_VIEW_AUTOSCROLL:
		{
			_HandleAutoScroll();
			break;
		}
		default:
		{
			BView::MessageReceived(message);
			break;
		}
	}
}
 
 
void
MemoryView::MouseDown(BPoint point)
{
	if (!IsFocus())
		MakeFocus(true);
 
	if (fTargetBlock == NULL)
		return;
 
	int32 buttons;
	if (Looper()->CurrentMessage()->FindInt32("buttons", &buttons) != B_OK)
		buttons = B_PRIMARY_MOUSE_BUTTON;
 
	if (buttons == B_SECONDARY_MOUSE_BUTTON) {
		_HandleContextMenu(point);
		return;
	}
 
	int32 offset = _GetOffsetAt(point);
	if (offset < fSelectionStart || offset > fSelectionEnd) {
		BRegion oldSelectionRegion;
		_GetSelectionRegion(oldSelectionRegion);
		fSelectionBase = offset;
		fSelectionStart = fSelectionBase;
		fSelectionEnd = fSelectionBase;
		Invalidate(oldSelectionRegion.Frame());
	}
 
	SetMouseEventMask(B_POINTER_EVENTS, B_NO_POINTER_HISTORY);
	fTrackingMouse = true;
}
 
 
void
MemoryView::MouseMoved(BPoint point, uint32 transit, const BMessage* message)
{
	if (!fTrackingMouse)
		return;
 
	BRegion oldSelectionRegion;
	_GetSelectionRegion(oldSelectionRegion);
	int32 offset = _GetOffsetAt(point);
	if (offset < fSelectionBase) {
		fSelectionStart = offset;
		fSelectionEnd = fSelectionBase;
	} else {
		fSelectionStart = fSelectionBase;
		fSelectionEnd = offset;
	}
 
	BRegion region;
	_GetSelectionRegion(region);
	region.Include(&oldSelectionRegion);
	Invalidate(region.Frame());
 
	switch (transit) {
		case B_EXITED_VIEW:
			fScrollRunner = new BMessageRunner(BMessenger(this),
				new BMessage(MSG_VIEW_AUTOSCROLL), kScrollTimer);
			break;
 
		case B_ENTERED_VIEW:
			delete fScrollRunner;
			fScrollRunner = NULL;
			break;
 
		default:
			break;
	}
}
 
 
void
MemoryView::MouseUp(BPoint point)
{
	fTrackingMouse = false;
	delete fScrollRunner;
	fScrollRunner = NULL;
}
 
 
void
MemoryView::ScrollToSelection()
{
	if (fTargetBlock != NULL) {
		target_addr_t offset = fTargetAddress - fTargetBlock->BaseAddress();
		int32 lineNumber = 0;
		if (fHexBlocksPerLine > 0) {
			lineNumber = offset / (fHexBlocksPerLine
				* (_GetHexDigitsPerBlock() / 2));
		} else if (fTextCharsPerLine > 0)
			lineNumber = offset / fTextCharsPerLine;
 
		float y = lineNumber * fLineHeight;
		if (y < Bounds().top)
			ScrollTo(0.0, y);
		else if (y + fLineHeight > Bounds().bottom)
			ScrollTo(0.0, y + fLineHeight - Bounds().Height());
	}
}
 
 
void
MemoryView::TargetedByScrollView(BScrollView* scrollView)
{
	BView::TargetedByScrollView(scrollView);
	scrollView->ScrollBar(B_VERTICAL)->SetRange(0.0, 0.0);
}
 
 
BSize
MemoryView::MinSize()
{
	return BSize(0.0, 0.0);
}
 
 
BSize
MemoryView::PreferredSize()
{
	return MinSize();
}
 
 
BSize
MemoryView::MaxSize()
{
	return BSize(B_SIZE_UNLIMITED, B_SIZE_UNLIMITED);
}
 
 
void
MemoryView::_Init()
{
	SetViewUIColor(B_PANEL_BACKGROUND_COLOR);
}
 
 
void
MemoryView::_RecalcScrollBars()
{
	float max = 0.0;
	BScrollBar *scrollBar = ScrollBar(B_VERTICAL);
	if (fTargetBlock != NULL) {
		int32 hexDigits = _GetHexDigitsPerBlock();
		int32 sizeFactor = 1 + hexDigits;
		_RecalcBounds();
 
		float hexWidth = fHexRight - fHexLeft;
		int32 nybblesPerLine = int32(hexWidth / fCharWidth);
		fHexBlocksPerLine = 0;
		fTextCharsPerLine = 0;
		if (fHexMode != HexModeNone) {
			fHexBlocksPerLine = nybblesPerLine / sizeFactor;
			fHexBlocksPerLine &= ~1;
			fHexRight = fHexLeft + (fHexBlocksPerLine * sizeFactor
				* fCharWidth);
			if (fTextMode != TextModeNone)
				fTextCharsPerLine = fHexBlocksPerLine * hexDigits / 2;
		} else if (fTextMode != TextModeNone)
			fTextCharsPerLine = int32((fTextRight - fTextLeft) / fCharWidth);
		int32 lineCount = 0;
		float totalHeight = 0.0;
		if (fHexBlocksPerLine > 0) {
			lineCount = fTargetBlock->Size() / (fHexBlocksPerLine
					* hexDigits / 2);
		} else if (fTextCharsPerLine > 0)
			lineCount = fTargetBlock->Size() / fTextCharsPerLine;
 
		totalHeight = lineCount * fLineHeight;
		if (totalHeight > 0.0) {
			BRect bounds = Bounds();
			max = totalHeight - bounds.Height();
			scrollBar->SetProportion(bounds.Height() / totalHeight);
			scrollBar->SetSteps(fLineHeight, bounds.Height());
		}
	}
	scrollBar->SetRange(0.0, max);
}
 
void
MemoryView::_GetNextHexBlock(char* buffer, int32 bufferSize,
	const char* address) const
{
	switch(fHexMode) {
		case HexMode8BitInt:
		{
			snprintf(buffer, bufferSize, "%02" B_PRIx8,
				*((const uint8*)address));
			break;
		}
		case HexMode16BitInt:
		{
			uint16 data = *((const uint16*)address);
			switch(fCurrentEndianMode)
			{
				case EndianModeBigEndian:
				{
					data = B_HOST_TO_BENDIAN_INT16(data);
				}
				break;
 
				case EndianModeLittleEndian:
				{
					data = B_HOST_TO_LENDIAN_INT16(data);
				}
				break;
			}
			snprintf(buffer, bufferSize, "%04" B_PRIx16,
				data);
			break;
		}
		case HexMode32BitInt:
		{
			uint32 data = *((const uint32*)address);
			switch(fCurrentEndianMode)
			{
				case EndianModeBigEndian:
				{
					data = B_HOST_TO_BENDIAN_INT32(data);
				}
				break;
 
				case EndianModeLittleEndian:
				{
					data = B_HOST_TO_LENDIAN_INT32(data);
				}
				break;
			}
			snprintf(buffer, bufferSize, "%08" B_PRIx32,
				data);
			break;
		}
		case HexMode64BitInt:
		{
			uint64 data = *((const uint64*)address);
			switch(fCurrentEndianMode)
			{
				case EndianModeBigEndian:
				{
					data = B_HOST_TO_BENDIAN_INT64(data);
				}
				break;
 
				case EndianModeLittleEndian:
				{
					data = B_HOST_TO_LENDIAN_INT64(data);
				}
				break;
			}
			snprintf(buffer, bufferSize, "%0*" B_PRIx64,
				16, data);
			break;
		}
	}
}
 
 
int32
MemoryView::_GetOffsetAt(BPoint point) const
{
	if (fTargetBlock == NULL)
		return -1;
 
	// TODO: support selection in the text region as well
	if (fHexMode == HexModeNone)
		return -1;
 
	int32 lineNumber = int32(point.y / fLineHeight);
	int32 charsPerBlock = _GetHexDigitsPerBlock() / 2;
	int32 totalHexBlocks = fTargetBlock->Size() / charsPerBlock;
	int32 lineCount = totalHexBlocks / fHexBlocksPerLine;
 
	if (lineNumber >= lineCount)
		return -1;
 
	point.x -= fHexLeft;
	if (point.x < 0)
		point.x = 0;
	else if (point.x > fHexRight)
		point.x = fHexRight;
 
	float blockWidth = (charsPerBlock * 2 + 1) * fCharWidth;
	int32 containingBlock = int32(floor(point.x / blockWidth));
 
	return fHexBlocksPerLine * charsPerBlock * lineNumber
		+ containingBlock * charsPerBlock;
}
 
 
BPoint
MemoryView::_GetPointForOffset(int32 offset) const
{
	BPoint point;
	if (fHexMode == HexModeNone)
		return point;
 
	int32 bytesPerLine = fHexBlocksPerLine * _GetHexDigitsPerBlock() / 2;
	int32 line = offset / bytesPerLine;
	int32 lineOffset = offset % bytesPerLine;
 
	point.x = fHexLeft + (lineOffset * 2 * fCharWidth)
			+ (lineOffset * 2 * fCharWidth / _GetHexDigitsPerBlock());
	point.y = line * fLineHeight;
 
	return point;
}
 
 
void
MemoryView::_RecalcBounds()
{
	fHexLeft = 0;
	fHexRight = 0;
	fTextLeft = 0;
	fTextRight = 0;
 
	// the left bound is determined by the space taken up by the actual
	// displayed addresses.
	float left = _GetAddressDisplayWidth();
	float width = Bounds().Width() - left;
 
	if (fHexMode != HexModeNone) {
		int32 hexDigits = _GetHexDigitsPerBlock();
		int32 sizeFactor = 1 + hexDigits;
		if (fTextMode != TextModeNone) {
			float hexProportion = sizeFactor / (float)(sizeFactor
				+ hexDigits / 2);
			float hexWidth = width * hexProportion;
			fTextLeft = left + hexWidth;
			fHexLeft = left;
			// when sharing the display between hex and text,
			// we allocate a 2 character space to separate the views
			hexWidth -= 2 * fCharWidth;
			fHexRight = left + hexWidth;
		} else {
			fHexLeft = left;
			fHexRight = left + width;
		}
	} else if (fTextMode != TextModeNone) {
		fTextLeft = left;
		fTextRight = left + width;
	}
}
 
 
float
MemoryView::_GetAddressDisplayWidth() const
{
	return (fTargetAddressSize + 2) * fCharWidth;
}
 
 
void
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)
		return;
 
	region.MakeEmpty();
	BPoint startPoint = _GetPointForOffset(fSelectionStart);
	BPoint endPoint = _GetPointForOffset(fSelectionEnd);
 
	BRect rect;
	if (startPoint.y == endPoint.y) {
		// single line case
		rect.left = startPoint.x;
		rect.top = startPoint.y;
		rect.right = endPoint.x;
		rect.bottom = endPoint.y + fLineHeight;
		region.Include(rect);
	} else {
		float currentLine = startPoint.y;
 
		// first line
		rect.left = startPoint.x;
		rect.top = startPoint.y;
		rect.right = fHexRight;
		rect.bottom = startPoint.y + fLineHeight;
		region.Include(rect);
		currentLine += fLineHeight;
 
		// middle region
		if (currentLine < endPoint.y) {
			rect.left = fHexLeft;
			rect.top = currentLine;
			rect.right = fHexRight;
			rect.bottom = endPoint.y;
			region.Include(rect);
		}
 
		rect.left = fHexLeft;
		rect.top = endPoint.y;
		rect.right = endPoint.x;
		rect.bottom = endPoint.y + fLineHeight;
		region.Include(rect);
	}
}
 
 
void
MemoryView::_GetSelectedText(BString& text) const
{
	if (fSelectionStart == fSelectionEnd)
		return;
 
	text.Truncate(0);
	const uint8* dataSource = fEditMode ? fEditableData : fTargetBlock->Data();
 
	const char* data = (const char *)dataSource + fSelectionStart;
	int16 blockSize = _GetHexDigitsPerBlock() / 2;
	int32 count = (fSelectionEnd - fSelectionStart)
		/ blockSize;
 
	char buffer[32];
	for (int32 i = 0; i < count; i++) {
		_GetNextHexBlock(buffer, sizeof(buffer), data);
		data += blockSize;
		text << buffer;
		if (i < count - 1)
			text << " ";
	}
}
 
 
void
MemoryView::_CopySelectionToClipboard()
{
	BString text;
	_GetSelectedText(text);
 
	if (text.Length() > 0) {
		be_clipboard->Lock();
		be_clipboard->Data()->RemoveData("text/plain");
		be_clipboard->Data()->AddData ("text/plain",
			B_MIME_TYPE, text.String(), text.Length());
		be_clipboard->Commit();
		be_clipboard->Unlock();
	}
}
 
 
void
MemoryView::_HandleAutoScroll()
{
	BPoint point;
	uint32 buttons;
	GetMouse(&point, &buttons);
	float difference = 0.0;
	int factor = 0;
	BRect visibleRect = Bounds();
	if (point.y < visibleRect.top)
		difference = point.y - visibleRect.top;
	else if (point.y > visibleRect.bottom)
		difference = point.y - visibleRect.bottom;
	if (difference != 0.0) {
		factor = (int)(ceilf(difference / fLineHeight));
		_ScrollByLines(factor);
	}
 
	MouseMoved(point, B_OUTSIDE_VIEW, NULL);
}
 
 
void
MemoryView::_ScrollByLines(int32 lineCount)
{
	BScrollBar* vertical = ScrollBar(B_VERTICAL);
	if (vertical == NULL)
		return;
 
	float value = vertical->Value();
	vertical->SetValue(value + fLineHeight * lineCount);
}
 
 
void
MemoryView::_HandleContextMenu(BPoint point)
{
	int32 offset = _GetOffsetAt(point);
	if (offset < fSelectionStart || offset > fSelectionEnd)
		return;
 
	BPopUpMenu* menu = new(std::nothrow) BPopUpMenu("Options");
	if (menu == NULL)
		return;
 
	ObjectDeleter<BPopUpMenu> menuDeleter(menu);
	ObjectDeleter<BMenuItem> itemDeleter;
	ObjectDeleter<BMessage> messageDeleter;
	BMessage* message = NULL;
	BMenuItem* item = NULL;
	if (fSelectionEnd - fSelectionStart == fTargetAddressSize / 2) {
		BMessage* message = new(std::nothrow) BMessage(MSG_INSPECT_ADDRESS);
		if (message == NULL)
			return;
 
		target_addr_t address;
		if (fTargetAddressSize == 8)
			address = *((uint32*)(fTargetBlock->Data() + fSelectionStart));
		else
			address = *((uint64*)(fTargetBlock->Data() + fSelectionStart));
 
		if (fCurrentEndianMode == EndianModeBigEndian)
			address = B_HOST_TO_BENDIAN_INT64(address);
		else
			address = B_HOST_TO_LENDIAN_INT64(address);
 
		messageDeleter.SetTo(message);
		message->AddUInt64("address", address);
		BMenuItem* item = new(std::nothrow) BMenuItem("Inspect", message);
		if (item == NULL)
			return;
 
		messageDeleter.Detach();
		itemDeleter.SetTo(item);
		if (!menu->AddItem(item))
			return;
 
		item->SetTarget(Looper());
		itemDeleter.Detach();
	}
 
	message = new(std::nothrow) BMessage(B_COPY);
	if (message == NULL)
		return;
 
	messageDeleter.SetTo(message);
	item = new(std::nothrow) BMenuItem("Copy", message);
	if (item == NULL)
		return;
 
	messageDeleter.Detach();
	itemDeleter.SetTo(item);
	if (!menu->AddItem(item))
		return;
 
	item->SetTarget(this);
	itemDeleter.Detach();
	menuDeleter.Detach();
 
	BPoint screenWhere(point);
	ConvertToScreen(&screenWhere);
	BRect mouseRect(screenWhere, screenWhere);
	mouseRect.InsetBy(-4.0, -4.0);
	menu->Go(screenWhere, true, false, mouseRect, true);
}
 
 
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
 
 
MemoryView::Listener::~Listener()
{
}

↑ V730 Not all members of a class are initialized inside the constructor. Consider inspecting: fHexLeft, fHexRight, fTextLeft, fTextRight.

↑ V570 The 'data' variable is assigned to itself.

↑ V763 Parameter 'rect' is always rewritten in function body before being used.

↑ V570 The 'data' variable is assigned to itself.

↑ V570 The 'address' variable is assigned to itself.

↑ V570 The 'data' variable is assigned to itself.