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