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