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