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