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