xref: /haiku/src/apps/debugger/user_interface/gui/inspector_window/MemoryView.cpp (revision 62c7ec5c06eb3be95b2774315f6d04b69ad9fae5)
1 /*
2  * Copyright 2011-2015, Rene Gollent, rene@gollent.com. All rights reserved.
3  * Distributed under the terms of the MIT License.
4  */
5 
6 
7 #include "MemoryView.h"
8 
9 #include <algorithm>
10 
11 #include <ctype.h>
12 #include <stdio.h>
13 
14 #include <ByteOrder.h>
15 #include <Clipboard.h>
16 #include <Looper.h>
17 #include <MenuItem.h>
18 #include <MessageRunner.h>
19 #include <Messenger.h>
20 #include <PopUpMenu.h>
21 #include <Region.h>
22 #include <ScrollView.h>
23 #include <String.h>
24 
25 #include "Architecture.h"
26 #include "AutoDeleter.h"
27 #include "MessageCodes.h"
28 #include "Team.h"
29 #include "TeamMemoryBlock.h"
30 
31 
32 enum {
33 	MSG_TARGET_ADDRESS_CHANGED	= 'mtac',
34 	MSG_VIEW_AUTOSCROLL			= 'mvas'
35 };
36 
37 static const bigtime_t kScrollTimer = 10000LL;
38 
39 
MemoryView(::Team * team,Listener * listener)40 MemoryView::MemoryView(::Team* team, Listener* listener)
41 	:
42 	BView("memoryView", B_WILL_DRAW | B_FRAME_EVENTS | B_NAVIGABLE
43 		| B_SUBPIXEL_PRECISE),
44 	fTeam(team),
45 	fTargetBlock(NULL),
46 	fEditableData(NULL),
47 	fEditedOffsets(),
48 	fTargetAddress(0LL),
49 	fEditMode(false),
50 	fEditLowNybble(false),
51 	fCharWidth(0.0),
52 	fLineHeight(0.0),
53 	fTextCharsPerLine(0),
54 	fHexBlocksPerLine(0),
55 	fHexMode(HexMode8BitInt),
56 	fTextMode(TextModeASCII),
57 	fSelectionBase(0),
58 	fSelectionStart(0),
59 	fSelectionEnd(0),
60 	fScrollRunner(NULL),
61 	fTrackingMouse(false),
62 	fListener(listener)
63 {
64 	Architecture* architecture = team->GetArchitecture();
65 	fTargetAddressSize = architecture->AddressSize() * 2;
66 	fCurrentEndianMode = architecture->IsBigEndian()
67 		? EndianModeBigEndian : EndianModeLittleEndian;
68 
69 }
70 
71 
~MemoryView()72 MemoryView::~MemoryView()
73 {
74 	if (fTargetBlock != NULL)
75 		fTargetBlock->ReleaseReference();
76 
77 	delete[] fEditableData;
78 }
79 
80 
81 /*static */ MemoryView*
Create(::Team * team,Listener * listener)82 MemoryView::Create(::Team* team, Listener* listener)
83 {
84 	MemoryView* self = new MemoryView(team, listener);
85 
86 	try {
87 		self->_Init();
88 	} catch(...) {
89 		delete self;
90 		throw;
91 	}
92 
93 	return self;
94 }
95 
96 
97 void
SetTargetAddress(TeamMemoryBlock * block,target_addr_t address)98 MemoryView::SetTargetAddress(TeamMemoryBlock* block, target_addr_t address)
99 {
100 	fTargetAddress = address;
101 	if (block != fTargetBlock) {
102 		if (fTargetBlock != NULL)
103 			fTargetBlock->ReleaseReference();
104 
105 		fTargetBlock = block;
106 		if (block != NULL)
107 			fTargetBlock->AcquireReference();
108 	}
109 
110 	MakeFocus(true);
111 	BMessenger(this).SendMessage(MSG_TARGET_ADDRESS_CHANGED);
112 }
113 
114 
115 void
UnsetListener()116 MemoryView::UnsetListener()
117 {
118 	fListener = NULL;
119 }
120 
121 
122 status_t
SetEditMode(bool enabled)123 MemoryView::SetEditMode(bool enabled)
124 {
125 	if (fTargetBlock == NULL)
126 		return B_BAD_VALUE;
127 	else if (fEditMode == enabled)
128 		return B_OK;
129 
130 	if (enabled) {
131 		status_t error = _SetupEditableData();
132 		if (error != B_OK)
133 			return error;
134 	} else {
135 		delete[] fEditableData;
136 		fEditableData = NULL;
137 		fEditedOffsets.clear();
138 		fEditLowNybble = false;
139 	}
140 
141 	fEditMode = enabled;
142 	Invalidate();
143 
144 	return B_OK;
145 }
146 
147 
148 void
AttachedToWindow()149 MemoryView::AttachedToWindow()
150 {
151 	BView::AttachedToWindow();
152 	SetViewUIColor(B_DOCUMENT_BACKGROUND_COLOR);
153 	SetFont(be_fixed_font);
154 	fCharWidth = be_fixed_font->StringWidth("a");
155 	font_height fontHeight;
156 	be_fixed_font->GetHeight(&fontHeight);
157 	fLineHeight = ceilf(fontHeight.ascent + fontHeight.descent
158 		+ fontHeight.leading);
159 }
160 
161 
162 void
Draw(BRect rect)163 MemoryView::Draw(BRect rect)
164 {
165 	rect = Bounds();
166 
167 	float divider = (fTargetAddressSize + 1) * fCharWidth;
168 	StrokeLine(BPoint(divider, rect.top),
169 				BPoint(divider, rect.bottom));
170 
171 	if (fTargetBlock == NULL)
172 		return;
173 
174 	uint32 hexBlockSize = _GetHexDigitsPerBlock() + 1;
175 	uint32 blockByteSize = hexBlockSize / 2;
176 	if (fHexMode != HexModeNone && fTextMode != TextModeNone) {
177 		divider += (fHexBlocksPerLine * hexBlockSize + 1) * fCharWidth;
178 		StrokeLine(BPoint(divider, rect.top),
179 					BPoint(divider, rect.bottom));
180 	}
181 
182 	char buffer[32];
183 	char textbuffer[512];
184 
185 	const char* dataSource = (const char*)(fEditMode ? fEditableData
186 			: fTargetBlock->Data());
187 
188 	int32 startLine = int32(rect.top / fLineHeight);
189 	const char* currentAddress = dataSource + fHexBlocksPerLine
190 		* blockByteSize * startLine;
191 	const char* maxAddress = dataSource + fTargetBlock->Size();
192 	const char* targetAddress = dataSource + fTargetAddress
193 		- fTargetBlock->BaseAddress();
194 	BPoint drawPoint(1.0, (startLine + 1) * fLineHeight);
195 	int32 currentBlocksPerLine = fHexBlocksPerLine;
196 	int32 currentCharsPerLine = fTextCharsPerLine;
197 	font_height fh;
198 	GetFontHeight(&fh);
199 	target_addr_t lineAddress = fTargetBlock->BaseAddress() + startLine
200 		* currentCharsPerLine;
201 	bool highlightBlock = false;
202 	rgb_color highlightColor;
203 	for (; currentAddress < maxAddress && drawPoint.y < rect.bottom
204 		+ fLineHeight; drawPoint.y += fLineHeight) {
205 		drawPoint.x = 1.0;
206 		snprintf(buffer, sizeof(buffer), "%0*" B_PRIx64,
207 			(int)fTargetAddressSize, lineAddress);
208 		PushState();
209 		SetHighColor(tint_color(HighColor(), B_LIGHTEN_1_TINT));
210 		DrawString(buffer, drawPoint);
211 		drawPoint.x += fCharWidth * (fTargetAddressSize + 2);
212 		PopState();
213 		if (fHexMode != HexModeNone) {
214 			if (currentAddress + (currentBlocksPerLine * blockByteSize)
215 				> maxAddress) {
216 				currentCharsPerLine = maxAddress - currentAddress;
217 				currentBlocksPerLine = currentCharsPerLine
218 					/ blockByteSize;
219 			}
220 
221 			for (int32 j = 0; j < currentBlocksPerLine; j++) {
222 				const char* blockAddress = currentAddress + (j
223 					* blockByteSize);
224 				_GetNextHexBlock(buffer, sizeof(buffer), blockAddress);
225 
226 				highlightBlock = false;
227 				if (fEditMode)
228 				{
229 					int32 offset = blockAddress - dataSource;
230 					for (uint32 i = 0; i < blockByteSize; i++) {
231 						if (fEditedOffsets.count(offset + i) != 0) {
232 							highlightBlock = true;
233 							highlightColor.set_to(0, 216, 0);
234 							break;
235 						}
236 					}
237 
238 				} else if (targetAddress >= blockAddress && targetAddress <
239 						blockAddress + blockByteSize) {
240 						highlightBlock = true;
241 						highlightColor.set_to(216, 0, 0);
242 				}
243 
244 				if (highlightBlock) {
245 					PushState();
246 					SetHighColor(highlightColor);
247 				}
248 
249 				DrawString(buffer, drawPoint);
250 
251 				if (highlightBlock)
252 					PopState();
253 
254 				drawPoint.x += fCharWidth * hexBlockSize;
255 			}
256 
257 			if (currentBlocksPerLine < fHexBlocksPerLine)
258 				drawPoint.x += fCharWidth * hexBlockSize
259 					* (fHexBlocksPerLine - currentBlocksPerLine);
260 		}
261 
262 		if (fTextMode != TextModeNone) {
263 			drawPoint.x += fCharWidth;
264 			for (int32 j = 0; j < currentCharsPerLine; j++) {
265 				// filter non-printable characters
266 				textbuffer[j] = currentAddress[j] > 32 ? currentAddress[j]
267 					: '.';
268 			}
269 			textbuffer[fTextCharsPerLine] = '\0';
270 			DrawString(textbuffer, drawPoint);
271 			if (targetAddress >= currentAddress && targetAddress
272 				< currentAddress + currentCharsPerLine) {
273 				PushState();
274 				SetHighColor(B_TRANSPARENT_COLOR);
275 				SetDrawingMode(B_OP_INVERT);
276 				uint32 blockAddress = uint32(targetAddress - currentAddress);
277 				if (fHexMode != HexModeNone)
278 					blockAddress &= ~(blockByteSize - 1);
279 				float startX = drawPoint.x + fCharWidth * blockAddress;
280 				float endX = startX;
281 				if (fHexMode != HexModeNone)
282 					endX += fCharWidth * ((hexBlockSize - 1) / 2);
283 				else
284 					endX += fCharWidth;
285 				FillRect(BRect(startX, drawPoint.y - fh.ascent, endX,
286 					drawPoint.y + fh.descent));
287 				PopState();
288 			}
289 		}
290 		if (currentBlocksPerLine > 0) {
291 			currentAddress += currentBlocksPerLine * blockByteSize;
292 			lineAddress += currentBlocksPerLine * blockByteSize;
293 		} else {
294 			currentAddress += fTextCharsPerLine;
295 			lineAddress += fTextCharsPerLine;
296 		}
297 	}
298 
299 	if (fSelectionStart != fSelectionEnd) {
300 		PushState();
301 		BRegion selectionRegion;
302 		_GetSelectionRegion(selectionRegion);
303 		SetDrawingMode(B_OP_INVERT);
304 		FillRegion(&selectionRegion, B_SOLID_HIGH);
305 		PopState();
306 	}
307 
308 	if (fEditMode) {
309 		PushState();
310 		BRect caretRect;
311 		_GetEditCaretRect(caretRect);
312 		SetDrawingMode(B_OP_INVERT);
313 		FillRect(caretRect, B_SOLID_HIGH);
314 		PopState();
315 
316 	}
317 }
318 
319 
320 void
FrameResized(float width,float height)321 MemoryView::FrameResized(float width, float height)
322 {
323 	BView::FrameResized(width, height);
324 	_RecalcScrollBars();
325 	Invalidate();
326 }
327 
328 
329 void
KeyDown(const char * bytes,int32 numBytes)330 MemoryView::KeyDown(const char* bytes, int32 numBytes)
331 {
332 	bool handled = true;
333 	if (fTargetBlock != NULL) {
334 		target_addr_t newAddress = fTargetAddress;
335 		target_addr_t maxAddress = fTargetBlock->BaseAddress()
336 			+ fTargetBlock->Size() - 1;
337 		int32 blockSize = 1;
338 		if (fHexMode != HexModeNone)
339 			blockSize = 1 << (fHexMode - 1);
340 		int32 lineCount = int32(Bounds().Height() / fLineHeight);
341 
342 		switch(bytes[0]) {
343 			case B_UP_ARROW:
344 			{
345 				newAddress -= blockSize * fHexBlocksPerLine;
346 				break;
347 			}
348 			case B_DOWN_ARROW:
349 			{
350 				newAddress += blockSize * fHexBlocksPerLine;
351 				break;
352 			}
353 			case B_LEFT_ARROW:
354 			{
355 				if (fEditMode) {
356 					if (!fEditLowNybble)
357 						newAddress--;
358 					fEditLowNybble = !fEditLowNybble;
359 					if (newAddress == fTargetAddress)
360 						Invalidate();
361 				} else
362 					newAddress -= blockSize;
363 				break;
364 			}
365 			case B_RIGHT_ARROW:
366 			{
367 				if (fEditMode) {
368 					if (fEditLowNybble)
369 						newAddress++;
370 					fEditLowNybble = !fEditLowNybble;
371 					if (newAddress == fTargetAddress)
372 						Invalidate();
373 				} else
374 					newAddress += blockSize;
375 				break;
376 			}
377 			case B_PAGE_UP:
378 			{
379 				newAddress -= (blockSize * fHexBlocksPerLine) * lineCount;
380 				break;
381 			}
382 			case B_PAGE_DOWN:
383 			{
384 				newAddress += (blockSize * fHexBlocksPerLine) * lineCount;
385 				break;
386 			}
387 			case B_HOME:
388 			{
389 				newAddress = fTargetBlock->BaseAddress();
390 				fEditLowNybble = false;
391 				break;
392 			}
393 			case B_END:
394 			{
395 				newAddress = maxAddress;
396 				fEditLowNybble = true;
397 				break;
398 			}
399 			default:
400 			{
401 				if (fEditMode && isxdigit(bytes[0]))
402 				{
403 					int value = 0;
404 					if (isdigit(bytes[0]))
405 						value = bytes[0] - '0';
406 					else
407 						value = (int)strtol(bytes, NULL, 16);
408 
409 					int32 byteOffset = fTargetAddress
410 						- fTargetBlock->BaseAddress();
411 
412 					if (fEditLowNybble)
413 						value = (fEditableData[byteOffset] & 0xf0) | value;
414 					else {
415 						value = (fEditableData[byteOffset] & 0x0f)
416 							| (value << 4);
417 					}
418 
419 					fEditableData[byteOffset] = value;
420 
421 					if (fEditableData[byteOffset]
422 						!= fTargetBlock->Data()[byteOffset]) {
423 						fEditedOffsets.insert(byteOffset);
424 					} else
425 						fEditedOffsets.erase(byteOffset);
426 
427 					if (fEditLowNybble) {
428 						if (newAddress < maxAddress) {
429 							newAddress++;
430 							fEditLowNybble = false;
431 						}
432 					} else
433 						fEditLowNybble = true;
434 
435 					Invalidate();
436 				} else
437 					handled = false;
438 
439 				break;
440 			}
441 		}
442 
443 		if (handled) {
444 			if (newAddress < fTargetBlock->BaseAddress())
445 				newAddress = fTargetAddress;
446 			else if (newAddress > maxAddress)
447 				newAddress = maxAddress;
448 
449 			if (newAddress != fTargetAddress) {
450 				fTargetAddress = newAddress;
451 				BMessenger(this).SendMessage(MSG_TARGET_ADDRESS_CHANGED);
452 			}
453 		}
454 	} else
455 		handled = false;
456 
457 	if (!handled)
458 		BView::KeyDown(bytes, numBytes);
459 }
460 
461 
462 void
MakeFocus(bool isFocused)463 MemoryView::MakeFocus(bool isFocused)
464 {
465 	BScrollView* parent = dynamic_cast<BScrollView*>(Parent());
466 	if (parent != NULL)
467 		parent->SetBorderHighlighted(isFocused);
468 
469 	BView::MakeFocus(isFocused);
470 }
471 
472 
473 void
MessageReceived(BMessage * message)474 MemoryView::MessageReceived(BMessage* message)
475 {
476 	switch(message->what) {
477 		case B_COPY:
478 		{
479 			_CopySelectionToClipboard();
480 			break;
481 		}
482 		case MSG_TARGET_ADDRESS_CHANGED:
483 		{
484 			_RecalcScrollBars();
485 			ScrollToSelection();
486 			Invalidate();
487 			if (fListener != NULL)
488 				fListener->TargetAddressChanged(fTargetAddress);
489 			break;
490 		}
491 		case MSG_SET_HEX_MODE:
492 		{
493 			// while editing, hex view changes are disallowed.
494 			if (fEditMode)
495 				break;
496 
497 			int32 mode;
498 			if (message->FindInt32("mode", &mode) == B_OK) {
499 				if (fHexMode == mode)
500 					break;
501 
502 				fHexMode = mode;
503 				_RecalcScrollBars();
504 				Invalidate();
505 
506 				if (fListener != NULL)
507 					fListener->HexModeChanged(mode);
508 			}
509 			break;
510 		}
511 		case MSG_SET_ENDIAN_MODE:
512 		{
513 			int32 mode;
514 			if (message->FindInt32("mode", &mode) == B_OK) {
515 				if (fCurrentEndianMode == mode)
516 					break;
517 
518 				fCurrentEndianMode = mode;
519 				Invalidate();
520 
521 				if (fListener != NULL)
522 					fListener->EndianModeChanged(mode);
523 			}
524 			break;
525 		}
526 		case MSG_SET_TEXT_MODE:
527 		{
528 			int32 mode;
529 			if (message->FindInt32("mode", &mode) == B_OK) {
530 				if (fTextMode == mode)
531 					break;
532 
533 				fTextMode = mode;
534 				_RecalcScrollBars();
535 				Invalidate();
536 
537 				if (fListener != NULL)
538 					fListener->TextModeChanged(mode);
539 			}
540 			break;
541 		}
542 		case MSG_VIEW_AUTOSCROLL:
543 		{
544 			_HandleAutoScroll();
545 			break;
546 		}
547 		default:
548 		{
549 			BView::MessageReceived(message);
550 			break;
551 		}
552 	}
553 }
554 
555 
556 void
MouseDown(BPoint point)557 MemoryView::MouseDown(BPoint point)
558 {
559 	if (!IsFocus())
560 		MakeFocus(true);
561 
562 	if (fTargetBlock == NULL)
563 		return;
564 
565 	int32 buttons;
566 	if (Looper()->CurrentMessage()->FindInt32("buttons", &buttons) != B_OK)
567 		buttons = B_PRIMARY_MOUSE_BUTTON;
568 
569 	if (buttons == B_SECONDARY_MOUSE_BUTTON) {
570 		_HandleContextMenu(point);
571 		return;
572 	}
573 
574 	int32 offset = _GetOffsetAt(point);
575 	if (offset < fSelectionStart || offset > fSelectionEnd) {
576 		BRegion oldSelectionRegion;
577 		_GetSelectionRegion(oldSelectionRegion);
578 		fSelectionBase = offset;
579 		fSelectionStart = fSelectionBase;
580 		fSelectionEnd = fSelectionBase;
581 		Invalidate(oldSelectionRegion.Frame());
582 	}
583 
584 	SetMouseEventMask(B_POINTER_EVENTS, B_NO_POINTER_HISTORY);
585 	fTrackingMouse = true;
586 }
587 
588 
589 void
MouseMoved(BPoint point,uint32 transit,const BMessage * message)590 MemoryView::MouseMoved(BPoint point, uint32 transit, const BMessage* message)
591 {
592 	if (!fTrackingMouse)
593 		return;
594 
595 	BRegion oldSelectionRegion;
596 	_GetSelectionRegion(oldSelectionRegion);
597 	int32 offset = _GetOffsetAt(point);
598 	if (offset < fSelectionBase) {
599 		fSelectionStart = offset;
600 		fSelectionEnd = fSelectionBase;
601 	} else {
602 		fSelectionStart = fSelectionBase;
603 		fSelectionEnd = offset;
604 	}
605 
606 	BRegion region;
607 	_GetSelectionRegion(region);
608 	region.Include(&oldSelectionRegion);
609 	Invalidate(region.Frame());
610 
611 	switch (transit) {
612 		case B_EXITED_VIEW:
613 			fScrollRunner = new BMessageRunner(BMessenger(this),
614 				new BMessage(MSG_VIEW_AUTOSCROLL), kScrollTimer);
615 			break;
616 
617 		case B_ENTERED_VIEW:
618 			delete fScrollRunner;
619 			fScrollRunner = NULL;
620 			break;
621 
622 		default:
623 			break;
624 	}
625 }
626 
627 
628 void
MouseUp(BPoint point)629 MemoryView::MouseUp(BPoint point)
630 {
631 	fTrackingMouse = false;
632 	delete fScrollRunner;
633 	fScrollRunner = NULL;
634 }
635 
636 
637 void
ScrollToSelection()638 MemoryView::ScrollToSelection()
639 {
640 	if (fTargetBlock != NULL) {
641 		target_addr_t offset = fTargetAddress - fTargetBlock->BaseAddress();
642 		int32 lineNumber = 0;
643 		if (fHexBlocksPerLine > 0) {
644 			lineNumber = offset / (fHexBlocksPerLine
645 				* (_GetHexDigitsPerBlock() / 2));
646 		} else if (fTextCharsPerLine > 0)
647 			lineNumber = offset / fTextCharsPerLine;
648 
649 		float y = lineNumber * fLineHeight;
650 		if (y < Bounds().top)
651 			ScrollTo(0.0, y);
652 		else if (y + fLineHeight > Bounds().bottom)
653 			ScrollTo(0.0, y + fLineHeight - Bounds().Height());
654 	}
655 }
656 
657 
658 void
TargetedByScrollView(BScrollView * scrollView)659 MemoryView::TargetedByScrollView(BScrollView* scrollView)
660 {
661 	BView::TargetedByScrollView(scrollView);
662 	scrollView->ScrollBar(B_VERTICAL)->SetRange(0.0, 0.0);
663 }
664 
665 
666 BSize
MinSize()667 MemoryView::MinSize()
668 {
669 	return BSize(0.0, 0.0);
670 }
671 
672 
673 BSize
PreferredSize()674 MemoryView::PreferredSize()
675 {
676 	return MinSize();
677 }
678 
679 
680 BSize
MaxSize()681 MemoryView::MaxSize()
682 {
683 	return BSize(B_SIZE_UNLIMITED, B_SIZE_UNLIMITED);
684 }
685 
686 
687 void
_Init()688 MemoryView::_Init()
689 {
690 	SetViewUIColor(B_PANEL_BACKGROUND_COLOR);
691 }
692 
693 
694 void
_RecalcScrollBars()695 MemoryView::_RecalcScrollBars()
696 {
697 	float max = 0.0;
698 	BScrollBar *scrollBar = ScrollBar(B_VERTICAL);
699 	if (fTargetBlock != NULL) {
700 		int32 hexDigits = _GetHexDigitsPerBlock();
701 		int32 sizeFactor = 1 + hexDigits;
702 		_RecalcBounds();
703 
704 		float hexWidth = fHexRight - fHexLeft;
705 		int32 nybblesPerLine = int32(hexWidth / fCharWidth);
706 		fHexBlocksPerLine = 0;
707 		fTextCharsPerLine = 0;
708 		if (fHexMode != HexModeNone) {
709 			fHexBlocksPerLine = nybblesPerLine / sizeFactor;
710 			fHexBlocksPerLine &= ~1;
711 			fHexRight = fHexLeft + (fHexBlocksPerLine * sizeFactor
712 				* fCharWidth);
713 			if (fTextMode != TextModeNone)
714 				fTextCharsPerLine = fHexBlocksPerLine * hexDigits / 2;
715 		} else if (fTextMode != TextModeNone)
716 			fTextCharsPerLine = int32((fTextRight - fTextLeft) / fCharWidth);
717 		int32 lineCount = 0;
718 		float totalHeight = 0.0;
719 		if (fHexBlocksPerLine > 0) {
720 			lineCount = fTargetBlock->Size() / (fHexBlocksPerLine
721 					* hexDigits / 2);
722 		} else if (fTextCharsPerLine > 0)
723 			lineCount = fTargetBlock->Size() / fTextCharsPerLine;
724 
725 		totalHeight = lineCount * fLineHeight;
726 		if (totalHeight > 0.0) {
727 			BRect bounds = Bounds();
728 			max = totalHeight - bounds.Height();
729 			scrollBar->SetProportion(bounds.Height() / totalHeight);
730 			scrollBar->SetSteps(fLineHeight, bounds.Height());
731 		}
732 	}
733 	scrollBar->SetRange(0.0, max);
734 }
735 
736 void
_GetNextHexBlock(char * buffer,int32 bufferSize,const char * address) const737 MemoryView::_GetNextHexBlock(char* buffer, int32 bufferSize,
738 	const char* address) const
739 {
740 	switch(fHexMode) {
741 		case HexMode8BitInt:
742 		{
743 			snprintf(buffer, bufferSize, "%02" B_PRIx8,
744 				*((const uint8*)address));
745 			break;
746 		}
747 		case HexMode16BitInt:
748 		{
749 			uint16 data = *((const uint16*)address);
750 			switch(fCurrentEndianMode)
751 			{
752 				case EndianModeBigEndian:
753 				{
754 					data = B_HOST_TO_BENDIAN_INT16(data);
755 				}
756 				break;
757 
758 				case EndianModeLittleEndian:
759 				{
760 					data = B_HOST_TO_LENDIAN_INT16(data);
761 				}
762 				break;
763 			}
764 			snprintf(buffer, bufferSize, "%04" B_PRIx16,
765 				data);
766 			break;
767 		}
768 		case HexMode32BitInt:
769 		{
770 			uint32 data = *((const uint32*)address);
771 			switch(fCurrentEndianMode)
772 			{
773 				case EndianModeBigEndian:
774 				{
775 					data = B_HOST_TO_BENDIAN_INT32(data);
776 				}
777 				break;
778 
779 				case EndianModeLittleEndian:
780 				{
781 					data = B_HOST_TO_LENDIAN_INT32(data);
782 				}
783 				break;
784 			}
785 			snprintf(buffer, bufferSize, "%08" B_PRIx32,
786 				data);
787 			break;
788 		}
789 		case HexMode64BitInt:
790 		{
791 			uint64 data = *((const uint64*)address);
792 			switch(fCurrentEndianMode)
793 			{
794 				case EndianModeBigEndian:
795 				{
796 					data = B_HOST_TO_BENDIAN_INT64(data);
797 				}
798 				break;
799 
800 				case EndianModeLittleEndian:
801 				{
802 					data = B_HOST_TO_LENDIAN_INT64(data);
803 				}
804 				break;
805 			}
806 			snprintf(buffer, bufferSize, "%0*" B_PRIx64,
807 				16, data);
808 			break;
809 		}
810 	}
811 }
812 
813 
814 int32
_GetOffsetAt(BPoint point) const815 MemoryView::_GetOffsetAt(BPoint point) const
816 {
817 	if (fTargetBlock == NULL)
818 		return -1;
819 
820 	// TODO: support selection in the text region as well
821 	if (fHexMode == HexModeNone)
822 		return -1;
823 
824 	int32 lineNumber = int32(point.y / fLineHeight);
825 	int32 charsPerBlock = _GetHexDigitsPerBlock() / 2;
826 	int32 totalHexBlocks = fTargetBlock->Size() / charsPerBlock;
827 	int32 lineCount = totalHexBlocks / fHexBlocksPerLine;
828 
829 	if (lineNumber >= lineCount)
830 		return -1;
831 
832 	point.x -= fHexLeft;
833 	if (point.x < 0)
834 		point.x = 0;
835 	else if (point.x > fHexRight)
836 		point.x = fHexRight;
837 
838 	float blockWidth = (charsPerBlock * 2 + 1) * fCharWidth;
839 	int32 containingBlock = int32(floor(point.x / blockWidth));
840 
841 	return fHexBlocksPerLine * charsPerBlock * lineNumber
842 		+ containingBlock * charsPerBlock;
843 }
844 
845 
846 BPoint
_GetPointForOffset(int32 offset) const847 MemoryView::_GetPointForOffset(int32 offset) const
848 {
849 	BPoint point;
850 	if (fHexMode == HexModeNone)
851 		return point;
852 
853 	int32 bytesPerLine = fHexBlocksPerLine * _GetHexDigitsPerBlock() / 2;
854 	int32 line = offset / bytesPerLine;
855 	int32 lineOffset = offset % bytesPerLine;
856 
857 	point.x = fHexLeft + (lineOffset * 2 * fCharWidth)
858 			+ (lineOffset * 2 * fCharWidth / _GetHexDigitsPerBlock());
859 	point.y = line * fLineHeight;
860 
861 	return point;
862 }
863 
864 
865 void
_RecalcBounds()866 MemoryView::_RecalcBounds()
867 {
868 	fHexLeft = 0;
869 	fHexRight = 0;
870 	fTextLeft = 0;
871 	fTextRight = 0;
872 
873 	// the left bound is determined by the space taken up by the actual
874 	// displayed addresses.
875 	float left = _GetAddressDisplayWidth();
876 	float width = Bounds().Width() - left;
877 
878 	if (fHexMode != HexModeNone) {
879 		int32 hexDigits = _GetHexDigitsPerBlock();
880 		int32 sizeFactor = 1 + hexDigits;
881 		if (fTextMode != TextModeNone) {
882 			float hexProportion = sizeFactor / (float)(sizeFactor
883 				+ hexDigits / 2);
884 			float hexWidth = width * hexProportion;
885 			fTextLeft = left + hexWidth;
886 			fHexLeft = left;
887 			// when sharing the display between hex and text,
888 			// we allocate a 2 character space to separate the views
889 			hexWidth -= 2 * fCharWidth;
890 			fHexRight = left + hexWidth;
891 		} else {
892 			fHexLeft = left;
893 			fHexRight = left + width;
894 		}
895 	} else if (fTextMode != TextModeNone) {
896 		fTextLeft = left;
897 		fTextRight = left + width;
898 	}
899 }
900 
901 
902 float
_GetAddressDisplayWidth() const903 MemoryView::_GetAddressDisplayWidth() const
904 {
905 	return (fTargetAddressSize + 2) * fCharWidth;
906 }
907 
908 
909 void
_GetEditCaretRect(BRect & rect) const910 MemoryView::_GetEditCaretRect(BRect& rect) const
911 {
912 	if (!fEditMode)
913 		return;
914 
915 	int32 byteOffset = fTargetAddress - fTargetBlock->BaseAddress();
916 	BPoint point = _GetPointForOffset(byteOffset);
917 	if (fEditLowNybble)
918 		point.x += fCharWidth;
919 
920 	rect.left = point.x;
921 	rect.right = point.x + fCharWidth;
922 	rect.top = point.y;
923 	rect.bottom = point.y + fLineHeight;
924 }
925 
926 
927 void
_GetSelectionRegion(BRegion & region) const928 MemoryView::_GetSelectionRegion(BRegion& region) const
929 {
930 	if (fHexMode == HexModeNone || fTargetBlock == NULL)
931 		return;
932 
933 	region.MakeEmpty();
934 	BPoint startPoint = _GetPointForOffset(fSelectionStart);
935 	BPoint endPoint = _GetPointForOffset(fSelectionEnd);
936 
937 	BRect rect;
938 	if (startPoint.y == endPoint.y) {
939 		// single line case
940 		rect.left = startPoint.x;
941 		rect.top = startPoint.y;
942 		rect.right = endPoint.x;
943 		rect.bottom = endPoint.y + fLineHeight;
944 		region.Include(rect);
945 	} else {
946 		float currentLine = startPoint.y;
947 
948 		// first line
949 		rect.left = startPoint.x;
950 		rect.top = startPoint.y;
951 		rect.right = fHexRight;
952 		rect.bottom = startPoint.y + fLineHeight;
953 		region.Include(rect);
954 		currentLine += fLineHeight;
955 
956 		// middle region
957 		if (currentLine < endPoint.y) {
958 			rect.left = fHexLeft;
959 			rect.top = currentLine;
960 			rect.right = fHexRight;
961 			rect.bottom = endPoint.y;
962 			region.Include(rect);
963 		}
964 
965 		rect.left = fHexLeft;
966 		rect.top = endPoint.y;
967 		rect.right = endPoint.x;
968 		rect.bottom = endPoint.y + fLineHeight;
969 		region.Include(rect);
970 	}
971 }
972 
973 
974 void
_GetSelectedText(BString & text) const975 MemoryView::_GetSelectedText(BString& text) const
976 {
977 	if (fSelectionStart == fSelectionEnd)
978 		return;
979 
980 	text.Truncate(0);
981 	const uint8* dataSource = fEditMode ? fEditableData : fTargetBlock->Data();
982 
983 	const char* data = (const char *)dataSource + fSelectionStart;
984 	int16 blockSize = _GetHexDigitsPerBlock() / 2;
985 	int32 count = (fSelectionEnd - fSelectionStart)
986 		/ blockSize;
987 
988 	char buffer[32];
989 	for (int32 i = 0; i < count; i++) {
990 		_GetNextHexBlock(buffer, sizeof(buffer), data);
991 		data += blockSize;
992 		text << buffer;
993 		if (i < count - 1)
994 			text << " ";
995 	}
996 }
997 
998 
999 void
_CopySelectionToClipboard()1000 MemoryView::_CopySelectionToClipboard()
1001 {
1002 	BString text;
1003 	_GetSelectedText(text);
1004 
1005 	if (text.Length() > 0) {
1006 		be_clipboard->Lock();
1007 		be_clipboard->Data()->RemoveData("text/plain");
1008 		be_clipboard->Data()->AddData ("text/plain",
1009 			B_MIME_TYPE, text.String(), text.Length());
1010 		be_clipboard->Commit();
1011 		be_clipboard->Unlock();
1012 	}
1013 }
1014 
1015 
1016 void
_HandleAutoScroll()1017 MemoryView::_HandleAutoScroll()
1018 {
1019 	BPoint point;
1020 	uint32 buttons;
1021 	GetMouse(&point, &buttons);
1022 	float difference = 0.0;
1023 	int factor = 0;
1024 	BRect visibleRect = Bounds();
1025 	if (point.y < visibleRect.top)
1026 		difference = point.y - visibleRect.top;
1027 	else if (point.y > visibleRect.bottom)
1028 		difference = point.y - visibleRect.bottom;
1029 	if (difference != 0.0) {
1030 		factor = (int)(ceilf(difference / fLineHeight));
1031 		_ScrollByLines(factor);
1032 	}
1033 
1034 	MouseMoved(point, B_OUTSIDE_VIEW, NULL);
1035 }
1036 
1037 
1038 void
_ScrollByLines(int32 lineCount)1039 MemoryView::_ScrollByLines(int32 lineCount)
1040 {
1041 	BScrollBar* vertical = ScrollBar(B_VERTICAL);
1042 	if (vertical == NULL)
1043 		return;
1044 
1045 	float value = vertical->Value();
1046 	vertical->SetValue(value + fLineHeight * lineCount);
1047 }
1048 
1049 
1050 void
_HandleContextMenu(BPoint point)1051 MemoryView::_HandleContextMenu(BPoint point)
1052 {
1053 	int32 offset = _GetOffsetAt(point);
1054 	if (offset < fSelectionStart || offset > fSelectionEnd)
1055 		return;
1056 
1057 	BPopUpMenu* menu = new(std::nothrow) BPopUpMenu("Options");
1058 	if (menu == NULL)
1059 		return;
1060 
1061 	ObjectDeleter<BPopUpMenu> menuDeleter(menu);
1062 	ObjectDeleter<BMenuItem> itemDeleter;
1063 	ObjectDeleter<BMessage> messageDeleter;
1064 	BMessage* message = NULL;
1065 	BMenuItem* item = NULL;
1066 	if (fSelectionEnd - fSelectionStart == fTargetAddressSize / 2) {
1067 		BMessage* message = new(std::nothrow) BMessage(MSG_INSPECT_ADDRESS);
1068 		if (message == NULL)
1069 			return;
1070 
1071 		target_addr_t address;
1072 		if (fTargetAddressSize == 8)
1073 			address = *((uint32*)(fTargetBlock->Data() + fSelectionStart));
1074 		else
1075 			address = *((uint64*)(fTargetBlock->Data() + fSelectionStart));
1076 
1077 		if (fCurrentEndianMode == EndianModeBigEndian)
1078 			address = B_HOST_TO_BENDIAN_INT64(address);
1079 		else
1080 			address = B_HOST_TO_LENDIAN_INT64(address);
1081 
1082 		messageDeleter.SetTo(message);
1083 		message->AddUInt64("address", address);
1084 		BMenuItem* item = new(std::nothrow) BMenuItem("Inspect", message);
1085 		if (item == NULL)
1086 			return;
1087 
1088 		messageDeleter.Detach();
1089 		itemDeleter.SetTo(item);
1090 		if (!menu->AddItem(item))
1091 			return;
1092 
1093 		item->SetTarget(Looper());
1094 		itemDeleter.Detach();
1095 	}
1096 
1097 	message = new(std::nothrow) BMessage(B_COPY);
1098 	if (message == NULL)
1099 		return;
1100 
1101 	messageDeleter.SetTo(message);
1102 	item = new(std::nothrow) BMenuItem("Copy", message);
1103 	if (item == NULL)
1104 		return;
1105 
1106 	messageDeleter.Detach();
1107 	itemDeleter.SetTo(item);
1108 	if (!menu->AddItem(item))
1109 		return;
1110 
1111 	item->SetTarget(this);
1112 	itemDeleter.Detach();
1113 	menuDeleter.Detach();
1114 
1115 	BPoint screenWhere(point);
1116 	ConvertToScreen(&screenWhere);
1117 	BRect mouseRect(screenWhere, screenWhere);
1118 	mouseRect.InsetBy(-4.0, -4.0);
1119 	menu->Go(screenWhere, true, false, mouseRect, true);
1120 }
1121 
1122 
1123 status_t
_SetupEditableData()1124 MemoryView::_SetupEditableData()
1125 {
1126 	fEditableData = new(std::nothrow) uint8[fTargetBlock->Size()];
1127 	if (fEditableData == NULL)
1128 		return B_NO_MEMORY;
1129 
1130 	memcpy(fEditableData, fTargetBlock->Data(), fTargetBlock->Size());
1131 
1132 	if (fHexMode != HexMode8BitInt) {
1133 		fHexMode = HexMode8BitInt;
1134 		if (fListener != NULL)
1135 			fListener->HexModeChanged(fHexMode);
1136 
1137 		_RecalcScrollBars();
1138 	}
1139 
1140 	return B_OK;
1141 }
1142 
1143 
1144 //#pragma mark - Listener
1145 
1146 
~Listener()1147 MemoryView::Listener::~Listener()
1148 {
1149 }
1150