/*
 * 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.