btrfs: Attempt to fix GCC2 build.
[haiku.git] / src / apps / debugger / user_interface / gui / inspector_window / MemoryView.cpp
blob18bb9f57097654fe3dc5c8d3a6032cfd87cacff6
1 /*
2 * Copyright 2011-2015, Rene Gollent, rene@gollent.com. All rights reserved.
3 * Distributed under the terms of the MIT License.
4 */
7 #include "MemoryView.h"
9 #include <algorithm>
11 #include <ctype.h>
12 #include <stdio.h>
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>
25 #include "Architecture.h"
26 #include "AutoDeleter.h"
27 #include "MessageCodes.h"
28 #include "Team.h"
29 #include "TeamMemoryBlock.h"
32 enum {
33 MSG_TARGET_ADDRESS_CHANGED = 'mtac',
34 MSG_VIEW_AUTOSCROLL = 'mvas'
37 static const bigtime_t kScrollTimer = 10000LL;
40 MemoryView::MemoryView(::Team* team, Listener* listener)
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)
64 Architecture* architecture = team->GetArchitecture();
65 fTargetAddressSize = architecture->AddressSize() * 2;
66 fCurrentEndianMode = architecture->IsBigEndian()
67 ? EndianModeBigEndian : EndianModeLittleEndian;
72 MemoryView::~MemoryView()
74 if (fTargetBlock != NULL)
75 fTargetBlock->ReleaseReference();
77 delete[] fEditableData;
81 /*static */ MemoryView*
82 MemoryView::Create(::Team* team, Listener* listener)
84 MemoryView* self = new MemoryView(team, listener);
86 try {
87 self->_Init();
88 } catch(...) {
89 delete self;
90 throw;
93 return self;
97 void
98 MemoryView::SetTargetAddress(TeamMemoryBlock* block, target_addr_t address)
100 fTargetAddress = address;
101 if (block != fTargetBlock) {
102 if (fTargetBlock != NULL)
103 fTargetBlock->ReleaseReference();
105 fTargetBlock = block;
106 if (block != NULL)
107 fTargetBlock->AcquireReference();
110 MakeFocus(true);
111 BMessenger(this).SendMessage(MSG_TARGET_ADDRESS_CHANGED);
115 void
116 MemoryView::UnsetListener()
118 fListener = NULL;
122 status_t
123 MemoryView::SetEditMode(bool enabled)
125 if (fTargetBlock == NULL)
126 return B_BAD_VALUE;
127 else if (fEditMode == enabled)
128 return B_OK;
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;
141 fEditMode = enabled;
142 Invalidate();
144 return B_OK;
148 void
149 MemoryView::AttachedToWindow()
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);
162 void
163 MemoryView::Draw(BRect rect)
165 rect = Bounds();
167 float divider = (fTargetAddressSize + 1) * fCharWidth;
168 StrokeLine(BPoint(divider, rect.top),
169 BPoint(divider, rect.bottom));
171 if (fTargetBlock == NULL)
172 return;
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));
182 char buffer[32];
183 char textbuffer[512];
185 const char* dataSource = (const char*)(fEditMode ? fEditableData
186 : fTargetBlock->Data());
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 rgb_color addressColor = tint_color(HighColor(), B_LIGHTEN_1_TINT);
198 rgb_color dataColor = HighColor();
199 font_height fh;
200 GetFontHeight(&fh);
201 target_addr_t lineAddress = fTargetBlock->BaseAddress() + startLine
202 * currentCharsPerLine;
203 bool highlightBlock = false;
204 rgb_color highlightColor;
205 for (; currentAddress < maxAddress && drawPoint.y < rect.bottom
206 + fLineHeight; drawPoint.y += fLineHeight) {
207 drawPoint.x = 1.0;
208 snprintf(buffer, sizeof(buffer), "%0*" B_PRIx64,
209 (int)fTargetAddressSize, lineAddress);
210 PushState();
211 SetHighColor(tint_color(HighColor(), B_LIGHTEN_1_TINT));
212 DrawString(buffer, drawPoint);
213 drawPoint.x += fCharWidth * (fTargetAddressSize + 2);
214 PopState();
215 if (fHexMode != HexModeNone) {
216 if (currentAddress + (currentBlocksPerLine * blockByteSize)
217 > maxAddress) {
218 currentCharsPerLine = maxAddress - currentAddress;
219 currentBlocksPerLine = currentCharsPerLine
220 / blockByteSize;
223 for (int32 j = 0; j < currentBlocksPerLine; j++) {
224 const char* blockAddress = currentAddress + (j
225 * blockByteSize);
226 _GetNextHexBlock(buffer, sizeof(buffer), blockAddress);
228 highlightBlock = false;
229 if (fEditMode)
231 int32 offset = blockAddress - dataSource;
232 for (uint32 i = 0; i < blockByteSize; i++) {
233 if (fEditedOffsets.count(offset + i) != 0) {
234 highlightBlock = true;
235 highlightColor.set_to(0, 216, 0);
236 break;
240 } else if (targetAddress >= blockAddress && targetAddress <
241 blockAddress + blockByteSize) {
242 highlightBlock = true;
243 highlightColor.set_to(216, 0, 0);
246 if (highlightBlock) {
247 PushState();
248 SetHighColor(highlightColor);
251 DrawString(buffer, drawPoint);
253 if (highlightBlock)
254 PopState();
256 drawPoint.x += fCharWidth * hexBlockSize;
259 if (currentBlocksPerLine < fHexBlocksPerLine)
260 drawPoint.x += fCharWidth * hexBlockSize
261 * (fHexBlocksPerLine - currentBlocksPerLine);
264 if (fTextMode != TextModeNone) {
265 drawPoint.x += fCharWidth;
266 for (int32 j = 0; j < currentCharsPerLine; j++) {
267 // filter non-printable characters
268 textbuffer[j] = currentAddress[j] > 32 ? currentAddress[j]
269 : '.';
271 textbuffer[fTextCharsPerLine] = '\0';
272 DrawString(textbuffer, drawPoint);
273 if (targetAddress >= currentAddress && targetAddress
274 < currentAddress + currentCharsPerLine) {
275 PushState();
276 SetHighColor(B_TRANSPARENT_COLOR);
277 SetDrawingMode(B_OP_INVERT);
278 uint32 blockAddress = uint32(targetAddress - currentAddress);
279 if (fHexMode != HexModeNone)
280 blockAddress &= ~(blockByteSize - 1);
281 float startX = drawPoint.x + fCharWidth * blockAddress;
282 float endX = startX;
283 if (fHexMode != HexModeNone)
284 endX += fCharWidth * ((hexBlockSize - 1) / 2);
285 else
286 endX += fCharWidth;
287 FillRect(BRect(startX, drawPoint.y - fh.ascent, endX,
288 drawPoint.y + fh.descent));
289 PopState();
292 if (currentBlocksPerLine > 0) {
293 currentAddress += currentBlocksPerLine * blockByteSize;
294 lineAddress += currentBlocksPerLine * blockByteSize;
295 } else {
296 currentAddress += fTextCharsPerLine;
297 lineAddress += fTextCharsPerLine;
301 if (fSelectionStart != fSelectionEnd) {
302 PushState();
303 BRegion selectionRegion;
304 _GetSelectionRegion(selectionRegion);
305 SetDrawingMode(B_OP_INVERT);
306 FillRegion(&selectionRegion, B_SOLID_HIGH);
307 PopState();
310 if (fEditMode) {
311 PushState();
312 BRect caretRect;
313 _GetEditCaretRect(caretRect);
314 SetDrawingMode(B_OP_INVERT);
315 FillRect(caretRect, B_SOLID_HIGH);
316 PopState();
322 void
323 MemoryView::FrameResized(float width, float height)
325 BView::FrameResized(width, height);
326 _RecalcScrollBars();
327 Invalidate();
331 void
332 MemoryView::KeyDown(const char* bytes, int32 numBytes)
334 bool handled = true;
335 if (fTargetBlock != NULL) {
336 target_addr_t newAddress = fTargetAddress;
337 target_addr_t maxAddress = fTargetBlock->BaseAddress()
338 + fTargetBlock->Size() - 1;
339 int32 blockSize = 1;
340 if (fHexMode != HexModeNone)
341 blockSize = 1 << (fHexMode - 1);
342 int32 lineCount = int32(Bounds().Height() / fLineHeight);
344 switch(bytes[0]) {
345 case B_UP_ARROW:
347 newAddress -= blockSize * fHexBlocksPerLine;
348 break;
350 case B_DOWN_ARROW:
352 newAddress += blockSize * fHexBlocksPerLine;
353 break;
355 case B_LEFT_ARROW:
357 if (fEditMode) {
358 if (!fEditLowNybble)
359 newAddress--;
360 fEditLowNybble = !fEditLowNybble;
361 if (newAddress == fTargetAddress)
362 Invalidate();
363 } else
364 newAddress -= blockSize;
365 break;
367 case B_RIGHT_ARROW:
369 if (fEditMode) {
370 if (fEditLowNybble)
371 newAddress++;
372 fEditLowNybble = !fEditLowNybble;
373 if (newAddress == fTargetAddress)
374 Invalidate();
375 } else
376 newAddress += blockSize;
377 break;
379 case B_PAGE_UP:
381 newAddress -= (blockSize * fHexBlocksPerLine) * lineCount;
382 break;
384 case B_PAGE_DOWN:
386 newAddress += (blockSize * fHexBlocksPerLine) * lineCount;
387 break;
389 case B_HOME:
391 newAddress = fTargetBlock->BaseAddress();
392 fEditLowNybble = false;
393 break;
395 case B_END:
397 newAddress = maxAddress;
398 fEditLowNybble = true;
399 break;
401 default:
403 if (fEditMode && isxdigit(bytes[0]))
405 int value = 0;
406 if (isdigit(bytes[0]))
407 value = bytes[0] - '0';
408 else
409 value = (int)strtol(bytes, NULL, 16);
411 int32 byteOffset = fTargetAddress
412 - fTargetBlock->BaseAddress();
414 if (fEditLowNybble)
415 value = (fEditableData[byteOffset] & 0xf0) | value;
416 else {
417 value = (fEditableData[byteOffset] & 0x0f)
418 | (value << 4);
421 fEditableData[byteOffset] = value;
423 if (fEditableData[byteOffset]
424 != fTargetBlock->Data()[byteOffset]) {
425 fEditedOffsets.insert(byteOffset);
426 } else
427 fEditedOffsets.erase(byteOffset);
429 if (fEditLowNybble) {
430 if (newAddress < maxAddress) {
431 newAddress++;
432 fEditLowNybble = false;
434 } else
435 fEditLowNybble = true;
437 Invalidate();
438 } else
439 handled = false;
441 break;
445 if (handled) {
446 if (newAddress < fTargetBlock->BaseAddress())
447 newAddress = fTargetAddress;
448 else if (newAddress > maxAddress)
449 newAddress = maxAddress;
451 if (newAddress != fTargetAddress) {
452 fTargetAddress = newAddress;
453 BMessenger(this).SendMessage(MSG_TARGET_ADDRESS_CHANGED);
456 } else
457 handled = false;
459 if (!handled)
460 BView::KeyDown(bytes, numBytes);
464 void
465 MemoryView::MakeFocus(bool isFocused)
467 BScrollView* parent = dynamic_cast<BScrollView*>(Parent());
468 if (parent != NULL)
469 parent->SetBorderHighlighted(isFocused);
471 BView::MakeFocus(isFocused);
475 void
476 MemoryView::MessageReceived(BMessage* message)
478 switch(message->what) {
479 case B_COPY:
481 _CopySelectionToClipboard();
482 break;
484 case MSG_TARGET_ADDRESS_CHANGED:
486 _RecalcScrollBars();
487 ScrollToSelection();
488 Invalidate();
489 if (fListener != NULL)
490 fListener->TargetAddressChanged(fTargetAddress);
491 break;
493 case MSG_SET_HEX_MODE:
495 // while editing, hex view changes are disallowed.
496 if (fEditMode)
497 break;
499 int32 mode;
500 if (message->FindInt32("mode", &mode) == B_OK) {
501 if (fHexMode == mode)
502 break;
504 fHexMode = mode;
505 _RecalcScrollBars();
506 Invalidate();
508 if (fListener != NULL)
509 fListener->HexModeChanged(mode);
511 break;
513 case MSG_SET_ENDIAN_MODE:
515 int32 mode;
516 if (message->FindInt32("mode", &mode) == B_OK) {
517 if (fCurrentEndianMode == mode)
518 break;
520 fCurrentEndianMode = mode;
521 Invalidate();
523 if (fListener != NULL)
524 fListener->EndianModeChanged(mode);
526 break;
528 case MSG_SET_TEXT_MODE:
530 int32 mode;
531 if (message->FindInt32("mode", &mode) == B_OK) {
532 if (fTextMode == mode)
533 break;
535 fTextMode = mode;
536 _RecalcScrollBars();
537 Invalidate();
539 if (fListener != NULL)
540 fListener->TextModeChanged(mode);
542 break;
544 case MSG_VIEW_AUTOSCROLL:
546 _HandleAutoScroll();
547 break;
549 default:
551 BView::MessageReceived(message);
552 break;
558 void
559 MemoryView::MouseDown(BPoint point)
561 if (!IsFocus())
562 MakeFocus(true);
564 if (fTargetBlock == NULL)
565 return;
567 int32 buttons;
568 if (Looper()->CurrentMessage()->FindInt32("buttons", &buttons) != B_OK)
569 buttons = B_PRIMARY_MOUSE_BUTTON;
571 if (buttons == B_SECONDARY_MOUSE_BUTTON) {
572 _HandleContextMenu(point);
573 return;
576 int32 offset = _GetOffsetAt(point);
577 if (offset < fSelectionStart || offset > fSelectionEnd) {
578 BRegion oldSelectionRegion;
579 _GetSelectionRegion(oldSelectionRegion);
580 fSelectionBase = offset;
581 fSelectionStart = fSelectionBase;
582 fSelectionEnd = fSelectionBase;
583 Invalidate(oldSelectionRegion.Frame());
586 SetMouseEventMask(B_POINTER_EVENTS, B_NO_POINTER_HISTORY);
587 fTrackingMouse = true;
591 void
592 MemoryView::MouseMoved(BPoint point, uint32 transit, const BMessage* message)
594 if (!fTrackingMouse)
595 return;
597 BRegion oldSelectionRegion;
598 _GetSelectionRegion(oldSelectionRegion);
599 int32 offset = _GetOffsetAt(point);
600 if (offset < fSelectionBase) {
601 fSelectionStart = offset;
602 fSelectionEnd = fSelectionBase;
603 } else {
604 fSelectionStart = fSelectionBase;
605 fSelectionEnd = offset;
608 BRegion region;
609 _GetSelectionRegion(region);
610 region.Include(&oldSelectionRegion);
611 Invalidate(region.Frame());
613 switch (transit) {
614 case B_EXITED_VIEW:
615 fScrollRunner = new BMessageRunner(BMessenger(this),
616 new BMessage(MSG_VIEW_AUTOSCROLL), kScrollTimer);
617 break;
619 case B_ENTERED_VIEW:
620 delete fScrollRunner;
621 fScrollRunner = NULL;
622 break;
624 default:
625 break;
630 void
631 MemoryView::MouseUp(BPoint point)
633 fTrackingMouse = false;
634 delete fScrollRunner;
635 fScrollRunner = NULL;
639 void
640 MemoryView::ScrollToSelection()
642 if (fTargetBlock != NULL) {
643 target_addr_t offset = fTargetAddress - fTargetBlock->BaseAddress();
644 int32 lineNumber = 0;
645 if (fHexBlocksPerLine > 0) {
646 lineNumber = offset / (fHexBlocksPerLine
647 * (_GetHexDigitsPerBlock() / 2));
648 } else if (fTextCharsPerLine > 0)
649 lineNumber = offset / fTextCharsPerLine;
651 float y = lineNumber * fLineHeight;
652 if (y < Bounds().top)
653 ScrollTo(0.0, y);
654 else if (y + fLineHeight > Bounds().bottom)
655 ScrollTo(0.0, y + fLineHeight - Bounds().Height());
660 void
661 MemoryView::TargetedByScrollView(BScrollView* scrollView)
663 BView::TargetedByScrollView(scrollView);
664 scrollView->ScrollBar(B_VERTICAL)->SetRange(0.0, 0.0);
668 BSize
669 MemoryView::MinSize()
671 return BSize(0.0, 0.0);
675 BSize
676 MemoryView::PreferredSize()
678 return MinSize();
682 BSize
683 MemoryView::MaxSize()
685 return BSize(B_SIZE_UNLIMITED, B_SIZE_UNLIMITED);
689 void
690 MemoryView::_Init()
692 SetViewUIColor(B_PANEL_BACKGROUND_COLOR);
696 void
697 MemoryView::_RecalcScrollBars()
699 float max = 0.0;
700 BScrollBar *scrollBar = ScrollBar(B_VERTICAL);
701 if (fTargetBlock != NULL) {
702 int32 hexDigits = _GetHexDigitsPerBlock();
703 int32 sizeFactor = 1 + hexDigits;
704 _RecalcBounds();
706 float hexWidth = fHexRight - fHexLeft;
707 int32 nybblesPerLine = int32(hexWidth / fCharWidth);
708 fHexBlocksPerLine = 0;
709 fTextCharsPerLine = 0;
710 if (fHexMode != HexModeNone) {
711 fHexBlocksPerLine = nybblesPerLine / sizeFactor;
712 fHexBlocksPerLine &= ~1;
713 fHexRight = fHexLeft + (fHexBlocksPerLine * sizeFactor
714 * fCharWidth);
715 if (fTextMode != TextModeNone)
716 fTextCharsPerLine = fHexBlocksPerLine * hexDigits / 2;
717 } else if (fTextMode != TextModeNone)
718 fTextCharsPerLine = int32((fTextRight - fTextLeft) / fCharWidth);
719 int32 lineCount = 0;
720 float totalHeight = 0.0;
721 if (fHexBlocksPerLine > 0) {
722 lineCount = fTargetBlock->Size() / (fHexBlocksPerLine
723 * hexDigits / 2);
724 } else if (fTextCharsPerLine > 0)
725 lineCount = fTargetBlock->Size() / fTextCharsPerLine;
727 totalHeight = lineCount * fLineHeight;
728 if (totalHeight > 0.0) {
729 BRect bounds = Bounds();
730 max = totalHeight - bounds.Height();
731 scrollBar->SetProportion(bounds.Height() / totalHeight);
732 scrollBar->SetSteps(fLineHeight, bounds.Height());
735 scrollBar->SetRange(0.0, max);
738 void
739 MemoryView::_GetNextHexBlock(char* buffer, int32 bufferSize,
740 const char* address) const
742 switch(fHexMode) {
743 case HexMode8BitInt:
745 snprintf(buffer, bufferSize, "%02" B_PRIx8,
746 *((const uint8*)address));
747 break;
749 case HexMode16BitInt:
751 uint16 data = *((const uint16*)address);
752 switch(fCurrentEndianMode)
754 case EndianModeBigEndian:
756 data = B_HOST_TO_BENDIAN_INT16(data);
758 break;
760 case EndianModeLittleEndian:
762 data = B_HOST_TO_LENDIAN_INT16(data);
764 break;
766 snprintf(buffer, bufferSize, "%04" B_PRIx16,
767 data);
768 break;
770 case HexMode32BitInt:
772 uint32 data = *((const uint32*)address);
773 switch(fCurrentEndianMode)
775 case EndianModeBigEndian:
777 data = B_HOST_TO_BENDIAN_INT32(data);
779 break;
781 case EndianModeLittleEndian:
783 data = B_HOST_TO_LENDIAN_INT32(data);
785 break;
787 snprintf(buffer, bufferSize, "%08" B_PRIx32,
788 data);
789 break;
791 case HexMode64BitInt:
793 uint64 data = *((const uint64*)address);
794 switch(fCurrentEndianMode)
796 case EndianModeBigEndian:
798 data = B_HOST_TO_BENDIAN_INT64(data);
800 break;
802 case EndianModeLittleEndian:
804 data = B_HOST_TO_LENDIAN_INT64(data);
806 break;
808 snprintf(buffer, bufferSize, "%0*" B_PRIx64,
809 16, data);
810 break;
816 int32
817 MemoryView::_GetOffsetAt(BPoint point) const
819 if (fTargetBlock == NULL)
820 return -1;
822 // TODO: support selection in the text region as well
823 if (fHexMode == HexModeNone)
824 return -1;
826 int32 lineNumber = int32(point.y / fLineHeight);
827 int32 charsPerBlock = _GetHexDigitsPerBlock() / 2;
828 int32 totalHexBlocks = fTargetBlock->Size() / charsPerBlock;
829 int32 lineCount = totalHexBlocks / fHexBlocksPerLine;
831 if (lineNumber >= lineCount)
832 return -1;
834 point.x -= fHexLeft;
835 if (point.x < 0)
836 point.x = 0;
837 else if (point.x > fHexRight)
838 point.x = fHexRight;
840 float blockWidth = (charsPerBlock * 2 + 1) * fCharWidth;
841 int32 containingBlock = int32(floor(point.x / blockWidth));
843 return fHexBlocksPerLine * charsPerBlock * lineNumber
844 + containingBlock * charsPerBlock;
848 BPoint
849 MemoryView::_GetPointForOffset(int32 offset) const
851 BPoint point;
852 if (fHexMode == HexModeNone)
853 return point;
855 int32 bytesPerLine = fHexBlocksPerLine * _GetHexDigitsPerBlock() / 2;
856 int32 line = offset / bytesPerLine;
857 int32 lineOffset = offset % bytesPerLine;
859 point.x = fHexLeft + (lineOffset * 2 * fCharWidth)
860 + (lineOffset * 2 * fCharWidth / _GetHexDigitsPerBlock());
861 point.y = line * fLineHeight;
863 return point;
867 void
868 MemoryView::_RecalcBounds()
870 fHexLeft = 0;
871 fHexRight = 0;
872 fTextLeft = 0;
873 fTextRight = 0;
875 // the left bound is determined by the space taken up by the actual
876 // displayed addresses.
877 float left = _GetAddressDisplayWidth();
878 float width = Bounds().Width() - left;
880 if (fHexMode != HexModeNone) {
881 int32 hexDigits = _GetHexDigitsPerBlock();
882 int32 sizeFactor = 1 + hexDigits;
883 if (fTextMode != TextModeNone) {
884 float hexProportion = sizeFactor / (float)(sizeFactor
885 + hexDigits / 2);
886 float hexWidth = width * hexProportion;
887 fTextLeft = left + hexWidth;
888 fHexLeft = left;
889 // when sharing the display between hex and text,
890 // we allocate a 2 character space to separate the views
891 hexWidth -= 2 * fCharWidth;
892 fHexRight = left + hexWidth;
893 } else {
894 fHexLeft = left;
895 fHexRight = left + width;
897 } else if (fTextMode != TextModeNone) {
898 fTextLeft = left;
899 fTextRight = left + width;
904 float
905 MemoryView::_GetAddressDisplayWidth() const
907 return (fTargetAddressSize + 2) * fCharWidth;
911 void
912 MemoryView::_GetEditCaretRect(BRect& rect) const
914 if (!fEditMode)
915 return;
917 int32 byteOffset = fTargetAddress - fTargetBlock->BaseAddress();
918 BPoint point = _GetPointForOffset(byteOffset);
919 if (fEditLowNybble)
920 point.x += fCharWidth;
922 rect.left = point.x;
923 rect.right = point.x + fCharWidth;
924 rect.top = point.y;
925 rect.bottom = point.y + fLineHeight;
929 void
930 MemoryView::_GetSelectionRegion(BRegion& region) const
932 if (fHexMode == HexModeNone || fTargetBlock == NULL)
933 return;
935 region.MakeEmpty();
936 BPoint startPoint = _GetPointForOffset(fSelectionStart);
937 BPoint endPoint = _GetPointForOffset(fSelectionEnd);
939 BRect rect;
940 if (startPoint.y == endPoint.y) {
941 // single line case
942 rect.left = startPoint.x;
943 rect.top = startPoint.y;
944 rect.right = endPoint.x;
945 rect.bottom = endPoint.y + fLineHeight;
946 region.Include(rect);
947 } else {
948 float currentLine = startPoint.y;
950 // first line
951 rect.left = startPoint.x;
952 rect.top = startPoint.y;
953 rect.right = fHexRight;
954 rect.bottom = startPoint.y + fLineHeight;
955 region.Include(rect);
956 currentLine += fLineHeight;
958 // middle region
959 if (currentLine < endPoint.y) {
960 rect.left = fHexLeft;
961 rect.top = currentLine;
962 rect.right = fHexRight;
963 rect.bottom = endPoint.y;
964 region.Include(rect);
967 rect.left = fHexLeft;
968 rect.top = endPoint.y;
969 rect.right = endPoint.x;
970 rect.bottom = endPoint.y + fLineHeight;
971 region.Include(rect);
976 void
977 MemoryView::_GetSelectedText(BString& text) const
979 if (fSelectionStart == fSelectionEnd)
980 return;
982 text.Truncate(0);
983 const uint8* dataSource = fEditMode ? fEditableData : fTargetBlock->Data();
985 const char* data = (const char *)dataSource + fSelectionStart;
986 int16 blockSize = _GetHexDigitsPerBlock() / 2;
987 int32 count = (fSelectionEnd - fSelectionStart)
988 / blockSize;
990 char buffer[32];
991 for (int32 i = 0; i < count; i++) {
992 _GetNextHexBlock(buffer, sizeof(buffer), data);
993 data += blockSize;
994 text << buffer;
995 if (i < count - 1)
996 text << " ";
1001 void
1002 MemoryView::_CopySelectionToClipboard()
1004 BString text;
1005 _GetSelectedText(text);
1007 if (text.Length() > 0) {
1008 be_clipboard->Lock();
1009 be_clipboard->Data()->RemoveData("text/plain");
1010 be_clipboard->Data()->AddData ("text/plain",
1011 B_MIME_TYPE, text.String(), text.Length());
1012 be_clipboard->Commit();
1013 be_clipboard->Unlock();
1018 void
1019 MemoryView::_HandleAutoScroll()
1021 BPoint point;
1022 uint32 buttons;
1023 GetMouse(&point, &buttons);
1024 float difference = 0.0;
1025 int factor = 0;
1026 BRect visibleRect = Bounds();
1027 if (point.y < visibleRect.top)
1028 difference = point.y - visibleRect.top;
1029 else if (point.y > visibleRect.bottom)
1030 difference = point.y - visibleRect.bottom;
1031 if (difference != 0.0) {
1032 factor = (int)(ceilf(difference / fLineHeight));
1033 _ScrollByLines(factor);
1036 MouseMoved(point, B_OUTSIDE_VIEW, NULL);
1040 void
1041 MemoryView::_ScrollByLines(int32 lineCount)
1043 BScrollBar* vertical = ScrollBar(B_VERTICAL);
1044 if (vertical == NULL)
1045 return;
1047 float value = vertical->Value();
1048 vertical->SetValue(value + fLineHeight * lineCount);
1052 void
1053 MemoryView::_HandleContextMenu(BPoint point)
1055 int32 offset = _GetOffsetAt(point);
1056 if (offset < fSelectionStart || offset > fSelectionEnd)
1057 return;
1059 BPopUpMenu* menu = new(std::nothrow) BPopUpMenu("Options");
1060 if (menu == NULL)
1061 return;
1063 ObjectDeleter<BPopUpMenu> menuDeleter(menu);
1064 ObjectDeleter<BMenuItem> itemDeleter;
1065 ObjectDeleter<BMessage> messageDeleter;
1066 BMessage* message = NULL;
1067 BMenuItem* item = NULL;
1068 if (fSelectionEnd - fSelectionStart == fTargetAddressSize / 2) {
1069 BMessage* message = new(std::nothrow) BMessage(MSG_INSPECT_ADDRESS);
1070 if (message == NULL)
1071 return;
1073 target_addr_t address;
1074 if (fTargetAddressSize == 8)
1075 address = *((uint32*)(fTargetBlock->Data() + fSelectionStart));
1076 else
1077 address = *((uint64*)(fTargetBlock->Data() + fSelectionStart));
1079 if (fCurrentEndianMode == EndianModeBigEndian)
1080 address = B_HOST_TO_BENDIAN_INT64(address);
1081 else
1082 address = B_HOST_TO_LENDIAN_INT64(address);
1084 messageDeleter.SetTo(message);
1085 message->AddUInt64("address", address);
1086 BMenuItem* item = new(std::nothrow) BMenuItem("Inspect", message);
1087 if (item == NULL)
1088 return;
1090 messageDeleter.Detach();
1091 itemDeleter.SetTo(item);
1092 if (!menu->AddItem(item))
1093 return;
1095 item->SetTarget(Looper());
1096 itemDeleter.Detach();
1099 message = new(std::nothrow) BMessage(B_COPY);
1100 if (message == NULL)
1101 return;
1103 messageDeleter.SetTo(message);
1104 item = new(std::nothrow) BMenuItem("Copy", message);
1105 if (item == NULL)
1106 return;
1108 messageDeleter.Detach();
1109 itemDeleter.SetTo(item);
1110 if (!menu->AddItem(item))
1111 return;
1113 item->SetTarget(this);
1114 itemDeleter.Detach();
1115 menuDeleter.Detach();
1117 BPoint screenWhere(point);
1118 ConvertToScreen(&screenWhere);
1119 BRect mouseRect(screenWhere, screenWhere);
1120 mouseRect.InsetBy(-4.0, -4.0);
1121 menu->Go(screenWhere, true, false, mouseRect, true);
1125 status_t
1126 MemoryView::_SetupEditableData()
1128 fEditableData = new(std::nothrow) uint8[fTargetBlock->Size()];
1129 if (fEditableData == NULL)
1130 return B_NO_MEMORY;
1132 memcpy(fEditableData, fTargetBlock->Data(), fTargetBlock->Size());
1134 if (fHexMode != HexMode8BitInt) {
1135 fHexMode = HexMode8BitInt;
1136 if (fListener != NULL)
1137 fListener->HexModeChanged(fHexMode);
1139 _RecalcScrollBars();
1142 return B_OK;
1146 //#pragma mark - Listener
1149 MemoryView::Listener::~Listener()