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 32enum { 33 MSG_TARGET_ADDRESS_CHANGED = 'mtac', 34 MSG_VIEW_AUTOSCROLL = 'mvas' 35}; 36 37static const bigtime_t kScrollTimer = 10000LL; 38 39 40MemoryView::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 72MemoryView::~MemoryView() 73{ 74 if (fTargetBlock != NULL) 75 fTargetBlock->ReleaseReference(); 76 77 delete[] fEditableData; 78} 79 80 81/*static */ MemoryView* 82MemoryView::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 97void 98MemoryView::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 115void 116MemoryView::UnsetListener() 117{ 118 fListener = NULL; 119} 120 121 122status_t 123MemoryView::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 148void 149MemoryView::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 162void 163MemoryView::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 320void 321MemoryView::FrameResized(float width, float height) 322{ 323 BView::FrameResized(width, height); 324 _RecalcScrollBars(); 325 Invalidate(); 326} 327 328 329void 330MemoryView::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 462void 463MemoryView::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 473void 474MemoryView::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 556void 557MemoryView::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 589void 590MemoryView::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 628void 629MemoryView::MouseUp(BPoint point) 630{ 631 fTrackingMouse = false; 632 delete fScrollRunner; 633 fScrollRunner = NULL; 634} 635 636 637void 638MemoryView::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 658void 659MemoryView::TargetedByScrollView(BScrollView* scrollView) 660{ 661 BView::TargetedByScrollView(scrollView); 662 scrollView->ScrollBar(B_VERTICAL)->SetRange(0.0, 0.0); 663} 664 665 666BSize 667MemoryView::MinSize() 668{ 669 return BSize(0.0, 0.0); 670} 671 672 673BSize 674MemoryView::PreferredSize() 675{ 676 return MinSize(); 677} 678 679 680BSize 681MemoryView::MaxSize() 682{ 683 return BSize(B_SIZE_UNLIMITED, B_SIZE_UNLIMITED); 684} 685 686 687void 688MemoryView::_Init() 689{ 690 SetViewUIColor(B_PANEL_BACKGROUND_COLOR); 691} 692 693 694void 695MemoryView::_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 736void 737MemoryView::_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 814int32 815MemoryView::_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 846BPoint 847MemoryView::_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 865void 866MemoryView::_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 902float 903MemoryView::_GetAddressDisplayWidth() const 904{ 905 return (fTargetAddressSize + 2) * fCharWidth; 906} 907 908 909void 910MemoryView::_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 927void 928MemoryView::_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 974void 975MemoryView::_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 999void 1000MemoryView::_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 1016void 1017MemoryView::_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 1038void 1039MemoryView::_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 1050void 1051MemoryView::_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 1123status_t 1124MemoryView::_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 1147MemoryView::Listener::~Listener() 1148{ 1149} 1150