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