1 /* 2 ** Copyright 2004, Axel Dörfler, axeld@pinc-software.de. All rights reserved. 3 ** Distributed under the terms of the OpenBeOS License. 4 */ 5 6 7 #include "DataView.h" 8 #include "DataEditor.h" 9 10 #include <Window.h> 11 #include <ScrollBar.h> 12 #include <Autolock.h> 13 #include <Clipboard.h> 14 #include <Mime.h> 15 #include <Beep.h> 16 17 18 typedef uint32 addr_t; 19 // this one is not yet defined in a public place 20 21 static const uint32 kBlockSize = 16; 22 static const uint32 kHorizontalSpace = 8; 23 static const uint32 kVerticalSpace = 4; 24 25 static const uint32 kBlockSpace = 3; 26 static const uint32 kHexByteWidth = 3; 27 // these are determined by the implementation of DataView::ConvertLine() 28 29 30 /** This function checks if the buffer contains a valid ASCII 31 * string, following the convention from the DataView::ConvertLine() 32 * method: everything that's not replaced by a '.' there will be 33 * accepted. 34 */ 35 36 bool 37 is_valid_ascii(uint8 *data, size_t size) 38 { 39 for (size_t i = 0; i < size; i++) { 40 // accept a terminating null byte 41 if (i == size - 1 && data[0] == '\0') 42 return true; 43 44 if (data[i] < ' ' || data[i] == 0x7f || data[i] & 0x80) 45 return false; 46 } 47 48 return true; 49 } 50 51 52 // #pragma mark - 53 54 55 DataView::DataView(BRect rect, DataEditor &editor) 56 : BView(rect, "dataView", B_FOLLOW_ALL, B_WILL_DRAW | B_NAVIGABLE | B_FRAME_EVENTS), 57 fEditor(editor), 58 fFocus(kHexFocus), 59 fBase(kHexBase), 60 fIsActive(true), 61 fFitFontSize(false) 62 { 63 fPositionLength = 4; 64 fStart = fEnd = 0; 65 fMouseSelectionStart = -1; 66 67 if (fEditor.Lock()) { 68 fDataSize = fEditor.ViewSize(); 69 fOffset = fEditor.ViewOffset(); 70 fEditor.Unlock(); 71 } else 72 fDataSize = 512; 73 fData = (uint8 *)malloc(fDataSize); 74 75 SetFontSize(12.0); 76 // also sets the fixed font 77 78 UpdateFromEditor(); 79 } 80 81 82 DataView::~DataView() 83 { 84 } 85 86 87 void 88 DataView::DetachedFromWindow() 89 { 90 fEditor.StopWatching(this); 91 } 92 93 94 void 95 DataView::AttachedToWindow() 96 { 97 fEditor.StartWatching(this); 98 } 99 100 101 void 102 DataView::UpdateFromEditor(BMessage *message) 103 { 104 if (fData == NULL) 105 return; 106 107 BAutolock locker(fEditor); 108 109 fFileSize = fEditor.FileSize(); 110 111 // get the range of the changes 112 113 int32 start = 0, end = fDataSize - 1; 114 off_t offset, size; 115 if (message != NULL 116 && message->FindInt64("offset", &offset) == B_OK 117 && message->FindInt64("size", &size) == B_OK) { 118 if (offset > fOffset + fDataSize 119 || offset + size < fOffset) { 120 // the changes are not within our scope, so we can ignore them 121 return; 122 } 123 124 if (offset > fOffset) 125 start = offset - fOffset; 126 if (offset + size < fOffset + fDataSize) 127 end = offset + size - fOffset; 128 } 129 130 if (fOffset + fDataSize > fFileSize) 131 fSizeInView = fFileSize - fOffset; 132 else 133 fSizeInView = fDataSize; 134 135 const uint8 *data; 136 if (fEditor.GetViewBuffer(&data) == B_OK) 137 // ToDo: copy only the relevant part 138 memcpy(fData, data, fDataSize); 139 140 InvalidateRange(start, end); 141 142 // we notify our selection listeners also if the 143 // data in the selection has changed 144 if (start <= fEnd && end >= fStart) { 145 BMessage update; 146 update.AddInt64("start", fStart); 147 update.AddInt64("end", fEnd); 148 SendNotices(kDataViewSelection, &update); 149 } 150 } 151 152 153 void 154 DataView::MessageReceived(BMessage *message) 155 { 156 switch (message->what) { 157 case kMsgUpdateData: 158 case kMsgDataEditorUpdate: 159 UpdateFromEditor(message); 160 break; 161 162 case kMsgDataEditorParameterChange: 163 { 164 int32 viewSize; 165 off_t offset; 166 if (message->FindInt64("offset", &offset) == B_OK) { 167 fOffset = offset; 168 SetSelection(0, 0); 169 MakeVisible(0); 170 } 171 if (message->FindInt32("view_size", &viewSize) == B_OK) { 172 fDataSize = viewSize; 173 fData = (uint8 *)realloc(fData, fDataSize); 174 UpdateScroller(); 175 SendNotices(kDataViewPreferredSize); 176 } 177 if (message->FindInt64("file_size", &offset) == B_OK) 178 UpdateFromEditor(); 179 break; 180 } 181 182 case kMsgBaseType: 183 { 184 int32 type; 185 if (message->FindInt32("base", &type) != B_OK) 186 break; 187 188 SetBase((base_type)type); 189 break; 190 } 191 192 case kMsgSetSelection: 193 { 194 int64 start, end; 195 if (message->FindInt64("start", &start) != B_OK 196 || message->FindInt64("end", &end) != B_OK) 197 break; 198 199 SetSelection(start, end); 200 break; 201 } 202 203 case B_SELECT_ALL: 204 SetSelection(0, fDataSize - 1); 205 break; 206 207 case B_COPY: 208 Copy(); 209 break; 210 211 case B_PASTE: 212 Paste(); 213 break; 214 215 case B_UNDO: 216 fEditor.Undo(); 217 break; 218 219 case B_REDO: 220 fEditor.Redo(); 221 break; 222 223 default: 224 BView::MessageReceived(message); 225 } 226 } 227 228 229 void 230 DataView::Copy() 231 { 232 if (!be_clipboard->Lock()) 233 return; 234 235 be_clipboard->Clear(); 236 237 BMessage *clip; 238 if ((clip = be_clipboard->Data()) != NULL) { 239 uint8 *data = fData + fStart; 240 size_t length = fEnd + 1 - fStart; 241 242 clip->AddData(B_FILE_MIME_TYPE, B_MIME_TYPE, data, length); 243 244 if (is_valid_ascii(data, length)) 245 clip->AddData("text/plain", B_MIME_TYPE, data, length); 246 247 be_clipboard->Commit(); 248 } 249 250 be_clipboard->Unlock(); 251 } 252 253 254 void 255 DataView::Paste() 256 { 257 if (!be_clipboard->Lock()) 258 return; 259 260 const void *data; 261 ssize_t length; 262 BMessage *clip; 263 if ((clip = be_clipboard->Data()) != NULL 264 && (clip->FindData(B_FILE_MIME_TYPE, B_MIME_TYPE, &data, &length) == B_OK 265 || clip->FindData("text/plain", B_MIME_TYPE, &data, &length) == B_OK)) { 266 // we have valid data, but it could still be too 267 // large to to fit in the file 268 if (fOffset + fStart + length > fFileSize) 269 length = fFileSize - fOffset; 270 271 if (fEditor.Replace(fOffset + fStart, (const uint8 *)data, length) == B_OK) 272 SetSelection(fStart + length, fStart + length); 273 } else 274 beep(); 275 276 be_clipboard->Unlock(); 277 } 278 279 280 void 281 DataView::ConvertLine(char *line, off_t offset, const uint8 *buffer, size_t size) 282 { 283 if (size == 0) { 284 line[0] = '\0'; 285 return; 286 } 287 288 line += sprintf(line, fBase == kHexBase ? "%0*Lx: " : "%0*Ld: ", 289 (int)fPositionLength, offset); 290 291 for (uint32 i = 0; i < kBlockSize; i++) { 292 if (i >= size) { 293 strcpy(line, " "); 294 line += kHexByteWidth; 295 } else 296 line += sprintf(line, "%02x ", *(unsigned char *)(buffer + i)); 297 } 298 299 strcpy(line, " "); 300 line += 3; 301 302 for (uint32 i = 0; i < kBlockSize; i++) { 303 if (i < size) { 304 char c = buffer[i]; 305 306 if (c < ' ' || c == 0x7f) 307 *line++ = '.'; 308 else 309 *line++ = c; 310 } else 311 break; 312 } 313 314 *line = '\0'; 315 } 316 317 318 void 319 DataView::Draw(BRect updateRect) 320 { 321 if (fData == NULL || fFileSize == 0) 322 return; 323 324 // ToDo: take "updateRect" into account! 325 326 char line[255]; 327 BPoint location(kHorizontalSpace, kVerticalSpace + fAscent); 328 329 for (uint32 i = 0; i < fSizeInView; i += kBlockSize) { 330 ConvertLine(line, /*fOffset + */i, fData + i, fSizeInView - i); 331 DrawString(line, location); 332 333 location.y += fFontHeight; 334 } 335 336 DrawSelection(); 337 } 338 339 340 BRect 341 DataView::DataBounds(bool inView) const 342 { 343 return BRect(0, 0, 344 fCharWidth * (kBlockSize * 4 + fPositionLength + 6) + 2 * kHorizontalSpace, 345 fFontHeight * (((inView ? fSizeInView : fDataSize) + kBlockSize - 1) / kBlockSize) 346 + 2 * kVerticalSpace); 347 } 348 349 350 int32 351 DataView::PositionAt(view_focus focus, BPoint point, view_focus *_newFocus) 352 { 353 // clip the point into our data bounds 354 355 BRect bounds = DataBounds(true); 356 if (point.x < bounds.left) 357 point.x = bounds.left; 358 else if (point.x > bounds.right) 359 point.x = bounds.right; 360 361 if (point.y < bounds.top) 362 point.y = bounds.top; 363 else if (point.y >= bounds.bottom - kVerticalSpace) 364 point.y = bounds.bottom - kVerticalSpace - 1; 365 366 float left = fCharWidth * (fPositionLength + kBlockSpace) + kHorizontalSpace; 367 float hexWidth = fCharWidth * kBlockSize * kHexByteWidth; 368 float width = fCharWidth; 369 370 if (focus == kNoFocus) { 371 // find in which part the point is in 372 if (point.x < left - width / 2) 373 return -1; 374 375 if (point.x > left + hexWidth) 376 focus = kAsciiFocus; 377 else 378 focus = kHexFocus; 379 380 if (_newFocus) 381 *_newFocus = focus; 382 } 383 if (focus == kHexFocus) { 384 left -= width / 2; 385 width *= kHexByteWidth; 386 } else 387 left += hexWidth + (kBlockSpace * width); 388 389 int32 row = int32((point.y - kVerticalSpace) / fFontHeight); 390 int32 column = int32((point.x - left) / width); 391 if (column >= (int32)kBlockSize) 392 column = (int32)kBlockSize - 1; 393 else if (column < 0) 394 column = 0; 395 396 return row * kBlockSize + column; 397 } 398 399 400 BRect 401 DataView::SelectionFrame(view_focus which, int32 start, int32 end) 402 { 403 float spacing = 0; 404 float width = fCharWidth; 405 float byteWidth = fCharWidth; 406 float left; 407 408 if (which == kHexFocus) { 409 spacing = fCharWidth / 2; 410 left = width * (fPositionLength + kBlockSpace); 411 width *= kHexByteWidth; 412 byteWidth *= 2; 413 } else 414 left = width * (fPositionLength + 2 * kBlockSpace + kHexByteWidth * kBlockSize); 415 416 left += kHorizontalSpace; 417 float startInLine = (start % kBlockSize) * width; 418 float endInLine = (end % kBlockSize) * width + byteWidth - 1; 419 420 return BRect(left + startInLine - spacing, 421 kVerticalSpace + (start / kBlockSize) * fFontHeight, 422 left + endInLine + spacing, 423 kVerticalSpace + (end / kBlockSize + 1) * fFontHeight - 1); 424 } 425 426 427 void 428 DataView::DrawSelectionFrame(view_focus which) 429 { 430 if (fFileSize == 0) 431 return; 432 433 bool drawBlock = false; 434 bool drawLastLine = false; 435 BRect block, lastLine; 436 int32 spacing = 0; 437 if (which == kAsciiFocus) 438 spacing++; 439 440 // draw first line 441 442 int32 start = fStart % kBlockSize; 443 int32 first = (fStart / kBlockSize) * kBlockSize; 444 445 int32 end = fEnd; 446 if (end > first + (int32)kBlockSize - 1) 447 end = first + kBlockSize - 1; 448 449 BRect firstLine = SelectionFrame(which, first + start, end); 450 firstLine.right += spacing; 451 first += kBlockSize; 452 453 // draw block (and last line) if necessary 454 455 end = fEnd % kBlockSize; 456 int32 last = (fEnd / kBlockSize) * kBlockSize; 457 458 if (last >= first) { 459 if (end == kBlockSize - 1) 460 last += kBlockSize; 461 if (last > first) { 462 block = SelectionFrame(which, first, last - 1); 463 block.right += spacing; 464 drawBlock = true; 465 } 466 if (end != kBlockSize - 1) { 467 lastLine = SelectionFrame(which, last, last + end); 468 lastLine.right += spacing; 469 drawLastLine = true; 470 } 471 } 472 473 SetDrawingMode(B_OP_INVERT); 474 BeginLineArray(8); 475 476 // +******* 477 // | * 478 // +------+ 479 480 const rgb_color color = {0, 0, 0}; 481 float bottom; 482 if (drawBlock) 483 bottom = block.bottom; 484 else 485 bottom = firstLine.bottom; 486 487 AddLine(BPoint(firstLine.left + 1, firstLine.top), firstLine.RightTop(), color); 488 AddLine(BPoint(firstLine.right, firstLine.top + 1), BPoint(firstLine.right, bottom), color); 489 490 // *-------+ 491 // * | 492 // ********* 493 494 BRect rect; 495 if (start == 0 || !drawBlock && !drawLastLine) 496 rect = firstLine; 497 else if (drawBlock) 498 rect = block; 499 else 500 rect = lastLine; 501 502 if (drawBlock) 503 rect.bottom = block.bottom; 504 if (drawLastLine) { 505 rect.bottom = lastLine.bottom; 506 rect.right = lastLine.right; 507 } 508 rect.bottom++; 509 510 AddLine(rect.LeftTop(), rect.LeftBottom(), color); 511 AddLine(BPoint(rect.left + 1, rect.bottom), rect.RightBottom(), color); 512 513 // *--------+ 514 // * | 515 // +**** | 516 // | | 517 518 if (start && (drawLastLine || drawBlock)) { 519 AddLine(firstLine.LeftTop(), firstLine.LeftBottom(), color); 520 521 float right = firstLine.left; 522 if (!drawBlock && right > lastLine.right) 523 right = lastLine.right; 524 AddLine(BPoint(rect.left + 1, rect.top), BPoint(right, rect.top), color); 525 } 526 527 // | | 528 // | ***** 529 // | * 530 // +--------+ 531 532 if (drawLastLine) { 533 AddLine(lastLine.RightBottom(), BPoint(lastLine.right, lastLine.top + 1), color); 534 if (!drawBlock && lastLine.right <= firstLine.left) 535 lastLine.right = firstLine.left + (lastLine.right < firstLine.left ? 0 : 1); 536 AddLine(BPoint(lastLine.right, lastLine.top), BPoint(firstLine.right, lastLine.top), color); 537 } 538 539 EndLineArray(); 540 SetDrawingMode(B_OP_COPY); 541 } 542 543 544 void 545 DataView::DrawSelectionBlock(view_focus which) 546 { 547 if (fFileSize == 0) 548 return; 549 550 // draw first line 551 552 SetDrawingMode(B_OP_INVERT); 553 554 int32 start = fStart % kBlockSize; 555 int32 first = (fStart / kBlockSize) * kBlockSize; 556 557 int32 end = fEnd; 558 if (end > first + (int32)kBlockSize - 1) 559 end = first + kBlockSize - 1; 560 561 FillRect(SelectionFrame(which, first + start, end)); 562 first += kBlockSize; 563 564 // draw block (and last line) if necessary 565 566 end = fEnd % kBlockSize; 567 int32 last = (fEnd / kBlockSize) * kBlockSize; 568 569 if (last >= first) { 570 if (end == kBlockSize - 1) 571 last += kBlockSize; 572 573 if (last > first) 574 FillRect(SelectionFrame(which, first, last - 1)); 575 if (end != kBlockSize - 1) 576 FillRect(SelectionFrame(which, last, last + end)); 577 } 578 579 SetDrawingMode(B_OP_COPY); 580 } 581 582 583 void 584 DataView::DrawSelection() 585 { 586 if (IsFocus() && fIsActive) { 587 DrawSelectionBlock(fFocus); 588 DrawSelectionFrame(fFocus == kHexFocus ? kAsciiFocus : kHexFocus); 589 } else { 590 DrawSelectionFrame(kHexFocus); 591 DrawSelectionFrame(kAsciiFocus); 592 } 593 } 594 595 596 void 597 DataView::SetSelection(int32 start, int32 end, view_focus focus) 598 { 599 // correct the values if necessary 600 601 if (start > end) { 602 int32 temp = start; 603 start = end; 604 end = temp; 605 } 606 607 if (start > (int32)fSizeInView - 1) 608 start = (int32)fSizeInView - 1; 609 if (start < 0) 610 start = 0; 611 612 if (end > (int32)fSizeInView - 1) 613 end = (int32)fSizeInView - 1; 614 if (end < 0) 615 end = 0; 616 617 if (fStart == start && fEnd == end) { 618 // nothing has changed, no need to update 619 return; 620 } 621 622 // notify our listeners 623 if (fStart != start) { 624 BMessage update; 625 update.AddInt64("position", start); 626 SendNotices(kDataViewCursorPosition, &update); 627 } 628 629 BMessage update; 630 update.AddInt64("start", start); 631 update.AddInt64("end", end); 632 SendNotices(kDataViewSelection, &update); 633 634 // remove old selection 635 // ToDo: this could be drastically improved if only the changed 636 // parts are drawn! Of course, this would only work for the 637 // selection block, not the frame. 638 DrawSelection(); 639 640 if (focus != kNoFocus) 641 fFocus = focus; 642 fStart = start; 643 fEnd = end; 644 645 DrawSelection(); 646 647 fBitPosition = 0; 648 } 649 650 651 void 652 DataView::GetSelection(int32 &start, int32 &end) 653 { 654 start = fStart; 655 end = fEnd; 656 } 657 658 659 void 660 DataView::InvalidateRange(int32 start, int32 end) 661 { 662 if (start <= 0 && end >= int32(fDataSize) - 1) { 663 Invalidate(); 664 return; 665 } 666 667 int32 startLine = start / kBlockSize; 668 int32 endLine = end / kBlockSize; 669 670 if (endLine > startLine) { 671 start = startLine * kBlockSize; 672 end = (endLine + 1) * kBlockSize - 1; 673 } 674 675 // the part with focus 676 BRect rect = SelectionFrame(fFocus, start, end); 677 rect.bottom++; 678 rect.right++; 679 Invalidate(rect); 680 681 // the part without focus 682 rect = SelectionFrame(fFocus == kHexFocus ? kAsciiFocus : kHexFocus, start, end); 683 rect.bottom++; 684 rect.right++; 685 Invalidate(rect); 686 } 687 688 689 void 690 DataView::MakeVisible(int32 position) 691 { 692 if (position < 0 || position > int32(fDataSize) - 1) 693 return; 694 695 BRect frame = SelectionFrame(fFocus, position, position); 696 BRect bounds = Bounds(); 697 if (bounds.Contains(frame)) 698 return; 699 700 // special case the first and the last line and column, so that 701 // we can take kHorizontalSpace & kVerticalSpace into account 702 703 if ((position % kBlockSize) == 0) 704 frame.left -= kHorizontalSpace; 705 else if ((position % kBlockSize) == kBlockSize - 1) 706 frame.right += kHorizontalSpace; 707 708 if (position < int32(kBlockSize)) 709 frame.top -= kVerticalSpace; 710 else if (position > int32(fDataSize - kBlockSize)) 711 frame.bottom += kVerticalSpace; 712 713 // compute the scroll point 714 715 BPoint point = bounds.LeftTop(); 716 if (bounds.left > frame.left) 717 point.x = frame.left; 718 else if (bounds.right < frame.right) 719 point.x = frame.right - bounds.Width(); 720 721 if (bounds.top > frame.top) 722 point.y = frame.top; 723 else if (bounds.bottom < frame.bottom) 724 point.y = frame.bottom - bounds.Height(); 725 726 ScrollTo(point); 727 } 728 729 730 const uint8 * 731 DataView::DataAt(int32 start) 732 { 733 if (start < 0 || start >= int32(fSizeInView) || fData == NULL) 734 return NULL; 735 736 return fData + start; 737 } 738 739 740 void 741 DataView::SetBase(base_type type) 742 { 743 if (fBase == type) 744 return; 745 746 fBase = type; 747 Invalidate(); 748 } 749 750 751 void 752 DataView::SetFocus(view_focus which) 753 { 754 if (which == fFocus) 755 return; 756 757 DrawSelection(); 758 fFocus = which; 759 DrawSelection(); 760 } 761 762 763 void 764 DataView::SetActive(bool active) 765 { 766 if (active == fIsActive) 767 return; 768 769 fIsActive = active; 770 771 // only redraw the focussed part 772 773 if (IsFocus() && active) { 774 DrawSelectionFrame(fFocus); 775 DrawSelectionBlock(fFocus); 776 } else { 777 DrawSelectionBlock(fFocus); 778 DrawSelectionFrame(fFocus); 779 } 780 } 781 782 783 void 784 DataView::WindowActivated(bool active) 785 { 786 BView::WindowActivated(active); 787 SetActive(active); 788 } 789 790 791 void 792 DataView::MakeFocus(bool focus) 793 { 794 bool previous = IsFocus(); 795 BView::MakeFocus(focus); 796 797 if (focus == previous) 798 return; 799 800 if (Window()->IsActive() && focus) 801 SetActive(true); 802 else if (!Window()->IsActive() || !focus) 803 SetActive(false); 804 } 805 806 807 void 808 DataView::UpdateScroller() 809 { 810 float width, height; 811 GetPreferredSize(&width, &height); 812 813 BScrollBar *bar; 814 if ((bar = ScrollBar(B_HORIZONTAL)) != NULL) { 815 float delta = width - Bounds().Width(); 816 if (delta < 0) 817 delta = 0; 818 819 bar->SetRange(0, delta); 820 bar->SetSteps(fCharWidth, Bounds().Width()); 821 bar->SetProportion(Bounds().Width() / width); 822 } 823 if ((bar = ScrollBar(B_VERTICAL)) != NULL) { 824 float delta = height - Bounds().Height(); 825 if (delta < 0) 826 delta = 0; 827 828 bar->SetRange(0, delta); 829 bar->SetSteps(fFontHeight, Bounds().Height()); 830 bar->SetProportion(Bounds().Height() / height); 831 } 832 } 833 834 835 void 836 DataView::FrameResized(float width, float height) 837 { 838 if (fFitFontSize) { 839 // adapt the font size to fit in the view's bounds 840 float oldSize = FontSize(); 841 BFont font = be_fixed_font; 842 float steps = 0.5f; 843 844 float size; 845 for (size = 1.f; size < 100; size += steps) { 846 font.SetSize(size); 847 float charWidth = font.StringWidth("w"); 848 if (charWidth * (kBlockSize * 4 + fPositionLength + 6) + 2 * kHorizontalSpace > width) 849 break; 850 851 if (size > 6) 852 steps = 1.0f; 853 } 854 size -= steps; 855 font.SetSize(size); 856 857 if (oldSize != size) { 858 SetFont(&font); 859 Invalidate(); 860 } 861 } 862 863 UpdateScroller(); 864 } 865 866 867 void 868 DataView::MouseDown(BPoint where) 869 { 870 MakeFocus(true); 871 872 BMessage *message = Looper()->CurrentMessage(); 873 int32 buttons; 874 if (message == NULL || message->FindInt32("buttons", &buttons) != B_OK 875 || (buttons & B_PRIMARY_MOUSE_BUTTON) == 0) 876 return; 877 878 int32 modifiers = message->FindInt32("modifiers"); 879 880 view_focus newFocus; 881 fMouseSelectionStart = PositionAt(kNoFocus, where, &newFocus); 882 if (fMouseSelectionStart == -1) { 883 // "where" is outside the valid frame 884 return; 885 } 886 887 int32 selectionEnd = fMouseSelectionStart; 888 if (modifiers & B_SHIFT_KEY) { 889 // enlarge the current selection 890 if (fStart < selectionEnd) 891 fMouseSelectionStart = fStart; 892 else if (fEnd > selectionEnd) 893 fMouseSelectionStart = fEnd; 894 } 895 SetSelection(fMouseSelectionStart, selectionEnd, newFocus); 896 897 SetMouseEventMask(B_POINTER_EVENTS, 898 B_NO_POINTER_HISTORY | B_SUSPEND_VIEW_FOCUS | B_LOCK_WINDOW_FOCUS); 899 } 900 901 902 void 903 DataView::MouseMoved(BPoint where, uint32 transit, const BMessage */*dragMessage*/) 904 { 905 if (fMouseSelectionStart == -1) 906 return; 907 908 int32 end = PositionAt(fFocus, where); 909 if (end == -1) 910 return; 911 912 SetSelection(fMouseSelectionStart, end); 913 MakeVisible(end); 914 } 915 916 917 void 918 DataView::MouseUp(BPoint where) 919 { 920 fMouseSelectionStart = -1; 921 } 922 923 924 void 925 DataView::KeyDown(const char *bytes, int32 numBytes) 926 { 927 int32 modifiers; 928 if (Looper()->CurrentMessage() == NULL 929 || Looper()->CurrentMessage()->FindInt32("modifiers", &modifiers) != B_OK) 930 modifiers = ::modifiers(); 931 932 switch (bytes[0]) { 933 case B_LEFT_ARROW: 934 SetSelection(fStart - 1, modifiers & B_SHIFT_KEY ? fEnd : fStart - 1); 935 MakeVisible(fStart); 936 break; 937 case B_RIGHT_ARROW: 938 SetSelection(modifiers & B_SHIFT_KEY ? fStart : fEnd + 1, fEnd + 1); 939 MakeVisible(fEnd); 940 break; 941 case B_UP_ARROW: 942 { 943 int32 start = fStart - int32(kBlockSize); 944 if (start < 0) { 945 if (modifiers & B_SHIFT_KEY) 946 start = 0; 947 else 948 start = fStart; 949 } 950 951 SetSelection(start, modifiers & B_SHIFT_KEY ? fEnd : start); 952 MakeVisible(fStart); 953 break; 954 } 955 case B_DOWN_ARROW: 956 { 957 int32 end = fEnd + int32(kBlockSize); 958 if (end >= int32(fSizeInView)) { 959 if (modifiers & B_SHIFT_KEY) 960 end = int32(fSizeInView) - 1; 961 else 962 end = fEnd; 963 } 964 965 SetSelection(modifiers & B_SHIFT_KEY ? fStart : end, end); 966 MakeVisible(fEnd); 967 break; 968 } 969 970 case B_PAGE_UP: 971 { 972 // scroll one page up, but keep the same cursor column 973 974 BRect frame = SelectionFrame(fFocus, fStart, fStart); 975 frame.OffsetBy(0, -Bounds().Height()); 976 if (frame.top <= kVerticalSpace) 977 frame.top = kVerticalSpace + 1; 978 ScrollBy(0, -Bounds().Height()); 979 980 int32 position = PositionAt(fFocus, frame.LeftTop()); 981 SetSelection(position, position); 982 break; 983 } 984 case B_PAGE_DOWN: 985 { 986 // scroll one page down, but keep the same cursor column 987 988 BRect frame = SelectionFrame(fFocus, fStart, fStart); 989 frame.OffsetBy(0, Bounds().Height()); 990 991 float lastLine = DataBounds().Height() - 1 - kVerticalSpace; 992 if (frame.top > lastLine) 993 frame.top = lastLine; 994 ScrollBy(0, Bounds().Height()); 995 996 int32 position = PositionAt(fFocus, frame.LeftTop()); 997 SetSelection(position, position); 998 break; 999 } 1000 case B_HOME: 1001 SetSelection(0, 0); 1002 MakeVisible(fStart); 1003 break; 1004 case B_END: 1005 SetSelection(fDataSize - 1, fDataSize - 1); 1006 MakeVisible(fStart); 1007 break; 1008 case B_TAB: 1009 SetFocus(fFocus == kHexFocus ? kAsciiFocus : kHexFocus); 1010 MakeVisible(fStart); 1011 break; 1012 1013 case B_FUNCTION_KEY: 1014 // this is ignored 1015 break; 1016 1017 case B_BACKSPACE: 1018 if (fBitPosition == 0) 1019 SetSelection(fStart - 1, fStart - 1); 1020 1021 if (fFocus == kHexFocus) 1022 fBitPosition = (fBitPosition + 4) % 8; 1023 1024 // supposed to fall through 1025 case B_DELETE: 1026 SetSelection(fStart, fStart); 1027 // to make sure only the cursor is selected 1028 1029 if (fFocus == kHexFocus) { 1030 const uint8 *data = DataAt(fStart); 1031 if (data == NULL) 1032 break; 1033 1034 uint8 c = data[0] & (fBitPosition == 0 ? 0x0f : 0xf0); 1035 // mask out region to be cleared 1036 1037 fEditor.Replace(fOffset + fStart, &c, 1); 1038 } else 1039 fEditor.Replace(fOffset + fStart, (const uint8 *)"", 1); 1040 break; 1041 1042 default: 1043 if (fFocus == kHexFocus) { 1044 // only hexadecimal characters are allowed to be entered 1045 const uint8 *data = DataAt(fStart); 1046 uint8 c = bytes[0]; 1047 if (c >= 'A' && c <= 'F') 1048 c += 'A' - 'a'; 1049 const char *hexNumbers = "0123456789abcdef"; 1050 addr_t number; 1051 if (data == NULL || (number = (addr_t)strchr(hexNumbers, c)) == NULL) 1052 break; 1053 1054 SetSelection(fStart, fStart); 1055 // to make sure only the cursor is selected 1056 1057 number -= (addr_t)hexNumbers; 1058 fBitPosition = (fBitPosition + 4) % 8; 1059 1060 c = (data[0] & (fBitPosition ? 0x0f : 0xf0)) | (number << fBitPosition); 1061 // mask out overwritten region and bit-wise or the number to be inserted 1062 1063 if (fEditor.Replace(fOffset + fStart, &c, 1) == B_OK && fBitPosition == 0) 1064 SetSelection(fStart + 1, fStart + 1); 1065 } else { 1066 if (fEditor.Replace(fOffset + fStart, (const uint8 *)bytes, numBytes) == B_OK) 1067 SetSelection(fStart + 1, fStart + 1); 1068 } 1069 break; 1070 } 1071 } 1072 1073 1074 void 1075 DataView::SetFont(const BFont *font, uint32 properties) 1076 { 1077 if (!font->IsFixed()) 1078 return; 1079 1080 BView::SetFont(font, properties); 1081 1082 font_height fontHeight; 1083 font->GetHeight(&fontHeight); 1084 1085 fFontHeight = int32(fontHeight.ascent + fontHeight.descent + fontHeight.leading); 1086 fAscent = fontHeight.ascent; 1087 fCharWidth = font->StringWidth("w"); 1088 } 1089 1090 1091 float 1092 DataView::FontSize() const 1093 { 1094 BFont font; 1095 GetFont(&font); 1096 1097 return font.Size(); 1098 } 1099 1100 1101 void 1102 DataView::SetFontSize(float point) 1103 { 1104 bool fit = (point == 0.0f); 1105 if (fit) { 1106 if (!fFitFontSize) 1107 SendNotices(kDataViewPreferredSize); 1108 fFitFontSize = fit; 1109 1110 FrameResized(Bounds().Width(), Bounds().Height()); 1111 return; 1112 } 1113 1114 fFitFontSize = false; 1115 1116 BFont font = be_fixed_font; 1117 font.SetSize(point); 1118 1119 SetFont(&font); 1120 UpdateScroller(); 1121 Invalidate(); 1122 1123 SendNotices(kDataViewPreferredSize); 1124 } 1125 1126 1127 void 1128 DataView::GetPreferredSize(float *_width, float *_height) 1129 { 1130 BRect bounds = DataBounds(); 1131 1132 if (_width) 1133 *_width = bounds.Width(); 1134 1135 if (_height) 1136 *_height = bounds.Height(); 1137 } 1138 1139