1 /* 2 * Copyright 2004-2018, Axel Dörfler, axeld@pinc-software.de. 3 * Distributed under the terms of the MIT License. 4 */ 5 6 7 #include "ProbeView.h" 8 9 #include <stdio.h> 10 #include <stdlib.h> 11 #include <strings.h> 12 13 #include <Alert.h> 14 #include <Application.h> 15 #include <Autolock.h> 16 #include <Beep.h> 17 #include <Bitmap.h> 18 #include <Box.h> 19 #include <Button.h> 20 #include <Catalog.h> 21 #include <ControlLook.h> 22 #include <Clipboard.h> 23 #include <Directory.h> 24 #include <Entry.h> 25 #include <ExpressionParser.h> 26 #include <fs_attr.h> 27 #include <GridView.h> 28 #include <GroupLayout.h> 29 #include <GroupLayoutBuilder.h> 30 #include <GroupView.h> 31 #include <LayoutBuilder.h> 32 #include <Locale.h> 33 #include <MenuBar.h> 34 #include <MenuItem.h> 35 #include <MessageQueue.h> 36 #include <NodeInfo.h> 37 #include <Node.h> 38 #include <NodeMonitor.h> 39 #include <Path.h> 40 #include <PrintJob.h> 41 #include <ScrollView.h> 42 #include <StringView.h> 43 #include <Slider.h> 44 #include <String.h> 45 #include <TextControl.h> 46 #include <Volume.h> 47 #include <Window.h> 48 49 #include "DataView.h" 50 #include "DiskProbe.h" 51 #include "TypeEditors.h" 52 53 54 #undef B_TRANSLATION_CONTEXT 55 #define B_TRANSLATION_CONTEXT "ProbeView" 56 57 static const uint32 kMsgSliderUpdate = 'slup'; 58 static const uint32 kMsgPositionUpdate = 'poup'; 59 static const uint32 kMsgLastPosition = 'lpos'; 60 static const uint32 kMsgFontSize = 'fnts'; 61 static const uint32 kMsgBlockSize = 'blks'; 62 static const uint32 kMsgAddBookmark = 'bmrk'; 63 static const uint32 kMsgPrint = 'prnt'; 64 static const uint32 kMsgPageSetup = 'pgsp'; 65 static const uint32 kMsgViewAs = 'vwas'; 66 67 static const uint32 kMsgStopFind = 'sfnd'; 68 69 70 class IconView : public BView { 71 public: 72 IconView(const entry_ref* ref, bool isDevice); 73 virtual ~IconView(); 74 75 virtual void AttachedToWindow(); 76 virtual void Draw(BRect updateRect); 77 78 void UpdateIcon(); 79 80 private: 81 entry_ref fRef; 82 bool fIsDevice; 83 BBitmap* fBitmap; 84 }; 85 86 87 class PositionSlider : public BSlider { 88 public: 89 PositionSlider(const char* name, 90 BMessage* message, off_t size, 91 uint32 blockSize); 92 virtual ~PositionSlider(); 93 94 off_t Position() const; 95 off_t Size() const { return fSize; } 96 uint32 BlockSize() const { return fBlockSize; } 97 98 virtual void SetPosition(float position); 99 void SetPosition(off_t position); 100 void SetSize(off_t size); 101 void SetBlockSize(uint32 blockSize); 102 103 private: 104 void Reset(); 105 106 private: 107 static const int32 kMaxSliderLimit = 0x7fffff80; 108 // this is the maximum value that BSlider seem to work with fine 109 110 off_t fSize; 111 uint32 fBlockSize; 112 }; 113 114 115 class HeaderView : public BGridView, public BInvoker { 116 public: 117 HeaderView(const entry_ref* ref, 118 DataEditor& editor); 119 virtual ~HeaderView(); 120 121 virtual void AttachedToWindow(); 122 virtual void DetachedFromWindow(); 123 virtual void MessageReceived(BMessage* message); 124 125 base_type Base() const { return fBase; } 126 void SetBase(base_type); 127 128 off_t CursorOffset() const 129 { return fPosition % fBlockSize; } 130 off_t Position() const { return fPosition; } 131 uint32 BlockSize() const { return fBlockSize; } 132 void SetTo(off_t position, uint32 blockSize); 133 134 void UpdateIcon(); 135 136 private: 137 void FormatValue(char* buffer, size_t bufferSize, 138 off_t value); 139 void UpdatePositionViews(bool all = true); 140 void UpdateOffsetViews(bool all = true); 141 void UpdateFileSizeView(); 142 void NotifyTarget(); 143 144 private: 145 const char* fAttribute; 146 off_t fFileSize; 147 uint32 fBlockSize; 148 base_type fBase; 149 off_t fPosition; 150 off_t fLastPosition; 151 152 BTextControl* fTypeControl; 153 BTextControl* fPositionControl; 154 BStringView* fPathView; 155 BStringView* fSizeView; 156 BTextControl* fOffsetControl; 157 BTextControl* fFileOffsetControl; 158 PositionSlider* fPositionSlider; 159 IconView* fIconView; 160 BButton* fStopButton; 161 }; 162 163 164 class TypeMenuItem : public BMenuItem { 165 public: 166 TypeMenuItem(const char* name, const char* type, 167 BMessage* message); 168 169 virtual void GetContentSize(float* _width, float* _height); 170 virtual void DrawContent(); 171 172 private: 173 BString fType; 174 }; 175 176 177 class EditorLooper : public BLooper { 178 public: 179 EditorLooper(const char* name, 180 DataEditor& editor, BMessenger messenger); 181 virtual ~EditorLooper(); 182 183 virtual void MessageReceived(BMessage* message); 184 185 bool FindIsRunning() const { return !fQuitFind; } 186 void Find(off_t startAt, const uint8* data, 187 size_t dataSize, bool caseInsensitive, 188 BMessenger progressMonitor); 189 void QuitFind(); 190 191 private: 192 DataEditor& fEditor; 193 BMessenger fMessenger; 194 volatile bool fQuitFind; 195 }; 196 197 198 class TypeView : public BView { 199 public: 200 TypeView(BRect rect, const char* name, 201 int32 index, DataEditor& editor, 202 int32 resizingMode); 203 virtual ~TypeView(); 204 205 virtual void FrameResized(float width, float height); 206 207 private: 208 BView* fTypeEditorView; 209 }; 210 211 212 // #pragma mark - utility functions 213 214 215 static void 216 get_type_string(char* buffer, size_t bufferSize, type_code type) 217 { 218 for (int32 i = 0; i < 4; i++) { 219 buffer[i] = type >> (24 - 8 * i); 220 if (buffer[i] < ' ' || buffer[i] == 0x7f) { 221 snprintf(buffer, bufferSize, "0x%04" B_PRIx32, type); 222 break; 223 } else if (i == 3) 224 buffer[4] = '\0'; 225 } 226 } 227 228 229 // #pragma mark - IconView 230 231 232 IconView::IconView(const entry_ref* ref, bool isDevice) 233 : BView(NULL, B_WILL_DRAW), 234 fRef(*ref), 235 fIsDevice(isDevice), 236 fBitmap(NULL) 237 { 238 UpdateIcon(); 239 240 if (fBitmap != NULL) 241 SetExplicitSize(fBitmap->Bounds().Size()); 242 } 243 244 245 IconView::~IconView() 246 { 247 delete fBitmap; 248 } 249 250 251 void 252 IconView::AttachedToWindow() 253 { 254 SetViewUIColor(B_PANEL_BACKGROUND_COLOR); 255 } 256 257 258 void 259 IconView::Draw(BRect updateRect) 260 { 261 if (fBitmap == NULL) 262 return; 263 264 SetDrawingMode(B_OP_ALPHA); 265 DrawBitmap(fBitmap, updateRect, updateRect); 266 SetDrawingMode(B_OP_COPY); 267 } 268 269 270 void 271 IconView::UpdateIcon() 272 { 273 if (fBitmap == NULL) { 274 fBitmap = new BBitmap(BRect(BPoint(0, 0), be_control_look->ComposeIconSize(B_LARGE_ICON)), 275 B_RGBA32); 276 } 277 278 if (fBitmap != NULL) { 279 status_t status = B_ERROR; 280 281 if (fIsDevice) { 282 BPath path(&fRef); 283 status = get_device_icon(path.Path(), fBitmap, B_LARGE_ICON); 284 } else { 285 status = BNodeInfo::GetTrackerIcon(&fRef, fBitmap, 286 (icon_size)(fBitmap->Bounds().IntegerWidth() + 1)); 287 } 288 289 if (status != B_OK) { 290 // Try to get generic icon 291 BMimeType type(B_FILE_MIME_TYPE); 292 status = type.GetIcon(fBitmap, B_LARGE_ICON); 293 } 294 295 if (status != B_OK) { 296 delete fBitmap; 297 fBitmap = NULL; 298 } 299 300 Invalidate(); 301 } 302 } 303 304 305 // #pragma mark - PositionSlider 306 307 308 PositionSlider::PositionSlider(const char* name, BMessage* message, 309 off_t size, uint32 blockSize) 310 : 311 BSlider(name, NULL, message, 0, kMaxSliderLimit, B_HORIZONTAL, 312 B_TRIANGLE_THUMB), 313 fSize(size), 314 fBlockSize(blockSize) 315 { 316 Reset(); 317 318 rgb_color color = ui_color(B_CONTROL_HIGHLIGHT_COLOR); 319 UseFillColor(true, &color); 320 } 321 322 323 PositionSlider::~PositionSlider() 324 { 325 } 326 327 328 void 329 PositionSlider::Reset() 330 { 331 SetKeyIncrementValue(int32(1.0 * kMaxSliderLimit / ((fSize - 1) / fBlockSize) + 0.5)); 332 SetEnabled(fSize > fBlockSize); 333 } 334 335 336 off_t 337 PositionSlider::Position() const 338 { 339 // ToDo: 340 // Note: this code is far from being perfect: depending on the file size, it has 341 // a maxium granularity that might be less than the actual block size demands... 342 // The only way to work around this that I can think of, is to replace the slider 343 // class completely with one that understands off_t values. 344 // For example, with a block size of 512 bytes, it should be good enough for about 345 // 1024 GB - and that's not really that far away these days. 346 347 return (off_t(1.0 * (fSize - 1) * Value() / kMaxSliderLimit + 0.5) / fBlockSize) * fBlockSize; 348 } 349 350 351 void 352 PositionSlider::SetPosition(float position) 353 { 354 BSlider::SetPosition(position); 355 } 356 357 358 void 359 PositionSlider::SetPosition(off_t position) 360 { 361 position /= fBlockSize; 362 SetValue(int32(1.0 * kMaxSliderLimit * position / ((fSize - 1) / fBlockSize) + 0.5)); 363 } 364 365 366 void 367 PositionSlider::SetSize(off_t size) 368 { 369 if (size == fSize) 370 return; 371 372 off_t position = Position(); 373 if (position >= size) 374 position = size - 1; 375 376 fSize = size; 377 Reset(); 378 SetPosition(position); 379 } 380 381 382 void 383 PositionSlider::SetBlockSize(uint32 blockSize) 384 { 385 if (blockSize == fBlockSize) 386 return; 387 388 off_t position = Position(); 389 fBlockSize = blockSize; 390 Reset(); 391 SetPosition(position); 392 } 393 394 395 // #pragma mark - HeaderView 396 397 398 HeaderView::HeaderView(const entry_ref* ref, DataEditor& editor) 399 : BGridView("probeHeader", B_USE_SMALL_SPACING, B_USE_SMALL_SPACING), 400 fAttribute(editor.Attribute()), 401 fFileSize(editor.FileSize()), 402 fBlockSize(editor.BlockSize()), 403 fBase(kHexBase), 404 fPosition(0), 405 fLastPosition(0) 406 { 407 SetViewUIColor(B_PANEL_BACKGROUND_COLOR); 408 GridLayout()->SetInsets(B_USE_WINDOW_SPACING, B_USE_WINDOW_SPACING, 409 B_USE_WINDOW_SPACING, B_USE_DEFAULT_SPACING); 410 411 fIconView = new IconView(ref, editor.IsDevice()); 412 GridLayout()->AddView(fIconView, 0, 0, 1, 2); 413 414 BGroupView* line = new BGroupView(B_HORIZONTAL); 415 GridLayout()->AddView(line, 1, 0); 416 417 BFont boldFont = *be_bold_font; 418 BFont plainFont = *be_plain_font; 419 boldFont.SetSize(ceilf(plainFont.Size() * 0.83)); 420 plainFont.SetSize(ceilf(plainFont.Size() * 0.83)); 421 422 BStringView* stringView = new BStringView( 423 B_EMPTY_STRING, editor.IsAttribute() 424 ? B_TRANSLATE("Attribute: ") : editor.IsDevice() 425 ? B_TRANSLATE("Device: ") : B_TRANSLATE("File: ")); 426 stringView->SetFont(&boldFont); 427 line->AddChild(stringView); 428 429 BPath path(ref); 430 BString string = path.Path(); 431 if (fAttribute != NULL) { 432 string.Prepend(" ("); 433 string.Prepend(fAttribute); 434 string.Append(")"); 435 } 436 fPathView = new BStringView(B_EMPTY_STRING, string.String()); 437 fPathView->SetFont(&plainFont); 438 line->AddChild(fPathView); 439 440 if (editor.IsAttribute()) { 441 stringView = new BStringView(B_EMPTY_STRING, 442 B_TRANSLATE("Attribute type: ")); 443 stringView->SetFont(&boldFont); 444 line->AddChild(stringView); 445 446 char buffer[16]; 447 get_type_string(buffer, sizeof(buffer), editor.Type()); 448 fTypeControl = new BTextControl(B_EMPTY_STRING, NULL, buffer, 449 new BMessage(kMsgPositionUpdate)); 450 fTypeControl->SetFont(&plainFont); 451 fTypeControl->TextView()->SetFontAndColor(&plainFont); 452 fTypeControl->SetEnabled(false); 453 // ToDo: for now 454 line->AddChild(fTypeControl); 455 456 } else 457 fTypeControl = NULL; 458 459 fStopButton = new BButton(B_EMPTY_STRING, 460 B_TRANSLATE("Stop"), new BMessage(kMsgStopFind)); 461 fStopButton->SetFont(&plainFont); 462 fStopButton->Hide(); 463 line->AddChild(fStopButton); 464 465 BGroupLayoutBuilder(line).AddGlue(); 466 467 line = new BGroupView(B_HORIZONTAL, B_USE_SMALL_SPACING); 468 GridLayout()->AddView(line, 1, 1); 469 470 stringView = new BStringView(B_EMPTY_STRING, B_TRANSLATE("Block: ")); 471 stringView->SetFont(&boldFont); 472 line->AddChild(stringView); 473 474 BMessage* msg = new BMessage(kMsgPositionUpdate); 475 msg->AddBool("fPositionControl", true); 476 // BTextControl oddities 477 fPositionControl = new BTextControl(B_EMPTY_STRING, NULL, "0x0", msg); 478 fPositionControl->SetDivider(0.0); 479 fPositionControl->SetFont(&plainFont); 480 fPositionControl->TextView()->SetFontAndColor(&plainFont); 481 fPositionControl->SetAlignment(B_ALIGN_LEFT, B_ALIGN_LEFT); 482 line->AddChild(fPositionControl); 483 484 fSizeView = new BStringView(B_EMPTY_STRING, B_TRANSLATE_COMMENT("of " 485 "0x0", "This is a part of 'Block 0xXXXX of 0x0026' message. In " 486 "languages without 'of' structure it can be replaced simply " 487 "with '/'.")); 488 fSizeView->SetFont(&plainFont); 489 line->AddChild(fSizeView); 490 UpdateFileSizeView(); 491 492 stringView = new BStringView(B_EMPTY_STRING, B_TRANSLATE("Offset: ")); 493 stringView->SetFont(&boldFont); 494 line->AddChild(stringView); 495 496 msg = new BMessage(kMsgPositionUpdate); 497 msg->AddBool("fOffsetControl", false); 498 fOffsetControl = new BTextControl(B_EMPTY_STRING, NULL, "0x0", msg); 499 fOffsetControl->SetDivider(0.0); 500 fOffsetControl->SetFont(&plainFont); 501 fOffsetControl->TextView()->SetFontAndColor(&plainFont); 502 fOffsetControl->SetAlignment(B_ALIGN_LEFT, B_ALIGN_LEFT); 503 line->AddChild(fOffsetControl); 504 UpdateOffsetViews(false); 505 506 stringView = new BStringView(B_EMPTY_STRING, editor.IsAttribute() 507 ? B_TRANSLATE("Attribute offset: ") : editor.IsDevice() 508 ? B_TRANSLATE("Device offset: ") : B_TRANSLATE("File offset: ")); 509 stringView->SetFont(&boldFont); 510 line->AddChild(stringView); 511 512 msg = new BMessage(kMsgPositionUpdate); 513 msg->AddBool("fFileOffsetControl", false); 514 fFileOffsetControl = new BTextControl(B_EMPTY_STRING, NULL, "0x0", msg); 515 fFileOffsetControl->SetDivider(0.0); 516 fFileOffsetControl->SetFont(&plainFont); 517 fFileOffsetControl->TextView()->SetFontAndColor(&plainFont); 518 fFileOffsetControl->SetAlignment(B_ALIGN_LEFT, B_ALIGN_LEFT); 519 line->AddChild(fFileOffsetControl); 520 521 BGroupLayoutBuilder(line).AddGlue(); 522 523 fPositionSlider = new PositionSlider("slider", 524 new BMessage(kMsgSliderUpdate), editor.FileSize(), editor.BlockSize()); 525 fPositionSlider->SetModificationMessage(new BMessage(kMsgSliderUpdate)); 526 fPositionSlider->SetBarThickness(8); 527 GridLayout()->AddView(fPositionSlider, 0, 2, 2, 1); 528 } 529 530 531 HeaderView::~HeaderView() 532 { 533 } 534 535 536 void 537 HeaderView::AttachedToWindow() 538 { 539 SetTarget(Window()); 540 541 fStopButton->SetTarget(Parent()); 542 fPositionControl->SetTarget(this); 543 fOffsetControl->SetTarget(this); 544 fFileOffsetControl->SetTarget(this); 545 fPositionSlider->SetTarget(this); 546 547 BMessage* message; 548 Window()->AddShortcut(B_HOME, B_COMMAND_KEY, 549 message = new BMessage(kMsgPositionUpdate), this); 550 message->AddInt64("block", 0); 551 Window()->AddShortcut(B_END, B_COMMAND_KEY, 552 message = new BMessage(kMsgPositionUpdate), this); 553 message->AddInt64("block", -1); 554 Window()->AddShortcut(B_PAGE_UP, B_COMMAND_KEY, 555 message = new BMessage(kMsgPositionUpdate), this); 556 message->AddInt32("delta", -1); 557 Window()->AddShortcut(B_PAGE_DOWN, B_COMMAND_KEY, 558 message = new BMessage(kMsgPositionUpdate), this); 559 message->AddInt32("delta", 1); 560 } 561 562 563 void 564 HeaderView::DetachedFromWindow() 565 { 566 Window()->RemoveShortcut(B_HOME, B_COMMAND_KEY); 567 Window()->RemoveShortcut(B_END, B_COMMAND_KEY); 568 Window()->RemoveShortcut(B_PAGE_UP, B_COMMAND_KEY); 569 Window()->RemoveShortcut(B_PAGE_DOWN, B_COMMAND_KEY); 570 } 571 572 573 void 574 HeaderView::UpdateIcon() 575 { 576 fIconView->UpdateIcon(); 577 } 578 579 580 void 581 HeaderView::FormatValue(char* buffer, size_t bufferSize, off_t value) 582 { 583 snprintf(buffer, bufferSize, fBase == kHexBase ? "0x%" B_PRIxOFF : "%" 584 B_PRIdOFF, value); 585 } 586 587 588 void 589 HeaderView::UpdatePositionViews(bool all) 590 { 591 char buffer[64]; 592 FormatValue(buffer, sizeof(buffer), fPosition / fBlockSize); 593 fPositionControl->SetText(buffer); 594 595 if (all) { 596 FormatValue(buffer, sizeof(buffer), fPosition); 597 fFileOffsetControl->SetText(buffer); 598 } 599 } 600 601 602 void 603 HeaderView::UpdateOffsetViews(bool all) 604 { 605 char buffer[64]; 606 FormatValue(buffer, sizeof(buffer), fPosition % fBlockSize); 607 fOffsetControl->SetText(buffer); 608 609 if (all) { 610 FormatValue(buffer, sizeof(buffer), fPosition); 611 fFileOffsetControl->SetText(buffer); 612 } 613 } 614 615 616 void 617 HeaderView::UpdateFileSizeView() 618 { 619 BString string(B_TRANSLATE("of ")); 620 char buffer[64]; 621 FormatValue(buffer, sizeof(buffer), 622 (fFileSize + fBlockSize - 1) / fBlockSize); 623 string << buffer; 624 625 fSizeView->SetText(string.String()); 626 } 627 628 629 void 630 HeaderView::SetBase(base_type type) 631 { 632 if (fBase == type) 633 return; 634 635 fBase = type; 636 637 UpdatePositionViews(); 638 UpdateOffsetViews(false); 639 UpdateFileSizeView(); 640 } 641 642 643 void 644 HeaderView::SetTo(off_t position, uint32 blockSize) 645 { 646 fPosition = position; 647 fLastPosition = (fLastPosition / fBlockSize) * blockSize; 648 fBlockSize = blockSize; 649 650 fPositionSlider->SetBlockSize(blockSize); 651 UpdatePositionViews(); 652 UpdateOffsetViews(false); 653 UpdateFileSizeView(); 654 } 655 656 657 void 658 HeaderView::NotifyTarget() 659 { 660 BMessage update(kMsgPositionUpdate); 661 update.AddInt64("position", fPosition); 662 Messenger().SendMessage(&update); 663 } 664 665 666 void 667 HeaderView::MessageReceived(BMessage* message) 668 { 669 switch (message->what) { 670 case B_OBSERVER_NOTICE_CHANGE: { 671 int32 what; 672 if (message->FindInt32(B_OBSERVE_WHAT_CHANGE, &what) != B_OK) 673 break; 674 675 switch (what) { 676 case kDataViewCursorPosition: 677 off_t offset; 678 if (message->FindInt64("position", &offset) == B_OK) { 679 fPosition = (fPosition / fBlockSize) * fBlockSize 680 + offset; 681 UpdateOffsetViews(); 682 } 683 break; 684 } 685 break; 686 } 687 688 case kMsgSliderUpdate: 689 { 690 // First, make sure we're only considering the most 691 // up-to-date message in the queue (which might not 692 // be this one). 693 // If there is another message of this type in the 694 // queue, we're just ignoring the current message. 695 696 if (Looper()->MessageQueue()->FindMessage(kMsgSliderUpdate, 0) 697 != NULL) 698 break; 699 700 // if nothing has changed, we can ignore this message as well 701 if (fPosition == fPositionSlider->Position()) 702 break; 703 704 fLastPosition = fPosition; 705 fPosition = fPositionSlider->Position(); 706 707 // update position text control 708 UpdatePositionViews(); 709 710 // notify our target 711 NotifyTarget(); 712 break; 713 } 714 715 case kMsgDataEditorFindProgress: 716 { 717 bool state; 718 if (message->FindBool("running", &state) == B_OK 719 && fFileSize > fBlockSize) { 720 fPositionSlider->SetEnabled(!state); 721 if (state) { 722 fStopButton->Show(); 723 } else { 724 fStopButton->Hide(); 725 } 726 } 727 728 off_t position; 729 if (message->FindInt64("position", &position) != B_OK) 730 break; 731 732 fPosition = (position / fBlockSize) * fBlockSize; 733 // round to block size 734 735 // update views 736 UpdatePositionViews(false); 737 fPositionSlider->SetPosition(fPosition); 738 break; 739 } 740 741 case kMsgPositionUpdate: 742 { 743 off_t lastPosition = fPosition; 744 745 off_t position; 746 int32 delta; 747 bool round = true; 748 if (message->FindInt64("position", &position) == B_OK) 749 fPosition = position; 750 else if (message->FindInt64("block", &position) == B_OK) { 751 if (position < 0) 752 position += (fFileSize - 1) / fBlockSize + 1; 753 fPosition = position * fBlockSize; 754 } else if (message->FindInt32("delta", &delta) == B_OK) { 755 fPosition += delta * off_t(fBlockSize); 756 } else { 757 try { 758 ExpressionParser parser; 759 parser.SetSupportHexInput(true); 760 if (message->FindBool("fPositionControl", &round) 761 == B_OK) { 762 fPosition = parser.EvaluateToInt64( 763 fPositionControl->Text()) * fBlockSize; 764 } else if (message->FindBool("fOffsetControl", &round) 765 == B_OK) { 766 fPosition = (fPosition / fBlockSize) * fBlockSize + 767 parser.EvaluateToInt64(fOffsetControl->Text()); 768 } else if (message->FindBool("fFileOffsetControl", &round) 769 == B_OK) { 770 fPosition = parser.EvaluateToInt64( 771 fFileOffsetControl->Text()); 772 } 773 } catch (...) { 774 beep(); 775 break; 776 } 777 } 778 779 fLastPosition = lastPosition; 780 781 if (round) 782 fPosition = (fPosition / fBlockSize) * fBlockSize; 783 // round to block size 784 785 if (fPosition < 0) 786 fPosition = 0; 787 else if (fPosition > ((fFileSize - 1) / fBlockSize) * fBlockSize) 788 fPosition = ((fFileSize - 1) / fBlockSize) * fBlockSize; 789 790 // update views 791 UpdatePositionViews(); 792 fPositionSlider->SetPosition(fPosition); 793 794 // notify our target 795 NotifyTarget(); 796 break; 797 } 798 799 case kMsgLastPosition: 800 { 801 fPosition = fLastPosition; 802 fLastPosition = fPositionSlider->Position(); 803 804 // update views 805 UpdatePositionViews(); 806 fPositionSlider->SetPosition(fPosition); 807 808 // notify our target 809 NotifyTarget(); 810 break; 811 } 812 813 case kMsgBaseType: 814 { 815 int32 type; 816 if (message->FindInt32("base", &type) != B_OK) 817 break; 818 819 SetBase((base_type)type); 820 break; 821 } 822 823 default: 824 BView::MessageReceived(message); 825 } 826 } 827 828 829 // #pragma mark - TypeMenuItem 830 831 832 /*! The TypeMenuItem is a BMenuItem that displays a type string at its 833 right border. 834 It is used to display the attribute and type in the attributes menu. 835 It does not mix nicely with short cuts. 836 */ 837 TypeMenuItem::TypeMenuItem(const char* name, const char* type, 838 BMessage* message) 839 : 840 BMenuItem(name, message), 841 fType(type) 842 { 843 } 844 845 846 void 847 TypeMenuItem::GetContentSize(float* _width, float* _height) 848 { 849 BMenuItem::GetContentSize(_width, _height); 850 851 if (_width) 852 *_width += Menu()->StringWidth(fType.String()); 853 } 854 855 856 void 857 TypeMenuItem::DrawContent() 858 { 859 // draw the label 860 BMenuItem::DrawContent(); 861 862 font_height fontHeight; 863 Menu()->GetFontHeight(&fontHeight); 864 865 // draw the type 866 BPoint point = ContentLocation(); 867 point.x = Frame().right - 4 - Menu()->StringWidth(fType.String()); 868 point.y += fontHeight.ascent; 869 870 Menu()->DrawString(fType.String(), point); 871 } 872 873 874 // #pragma mark - EditorLooper 875 876 877 /*! The purpose of this looper is to off-load the editor data loading from 878 the main window looper. 879 880 It will listen to the offset changes of the editor, let him update its 881 data, and will then synchronously notify the target. 882 That way, simple offset changes will not stop the main looper from 883 operating. Therefore, all offset updates for the editor will go through 884 this looper. 885 Also, it will run the find action in the editor. 886 */ 887 EditorLooper::EditorLooper(const char* name, DataEditor& editor, 888 BMessenger target) 889 : BLooper(name), 890 fEditor(editor), 891 fMessenger(target), 892 fQuitFind(true) 893 { 894 fEditor.StartWatching(this); 895 } 896 897 898 EditorLooper::~EditorLooper() 899 { 900 fEditor.StopWatching(this); 901 } 902 903 904 void 905 EditorLooper::MessageReceived(BMessage* message) 906 { 907 switch (message->what) { 908 case kMsgPositionUpdate: 909 { 910 // First, make sure we're only considering the most 911 // up-to-date message in the queue (which might not 912 // be this one). 913 // If there is another message of this type in the 914 // queue, we're just ignoring the current message. 915 916 if (Looper()->MessageQueue()->FindMessage(kMsgPositionUpdate, 0) != NULL) 917 break; 918 919 off_t position; 920 if (message->FindInt64("position", &position) == B_OK) { 921 BAutolock locker(fEditor); 922 fEditor.SetViewOffset(position); 923 924 BMessage message(kMsgSetSelection); 925 message.AddInt64("start", position - fEditor.ViewOffset()); 926 message.AddInt64("end", position - fEditor.ViewOffset()); 927 fMessenger.SendMessage(&message); 928 } 929 break; 930 } 931 932 case kMsgDataEditorParameterChange: 933 { 934 bool updated = false; 935 936 if (fEditor.Lock()) { 937 fEditor.UpdateIfNeeded(&updated); 938 fEditor.Unlock(); 939 } 940 941 if (updated) { 942 BMessage reply; 943 fMessenger.SendMessage(kMsgUpdateData, &reply); 944 // We are doing a synchronously transfer, to prevent 945 // that we're already locking the editor again when 946 // our target wants to get the editor data. 947 } 948 break; 949 } 950 951 case kMsgFind: 952 { 953 BMessenger progressMonitor; 954 message->FindMessenger("progress_monitor", &progressMonitor); 955 956 off_t startAt = 0; 957 message->FindInt64("start", &startAt); 958 959 bool caseInsensitive = !message->FindBool("case_sensitive"); 960 961 ssize_t dataSize; 962 const uint8* data; 963 if (message->FindData("data", B_RAW_TYPE, (const void**)&data, 964 &dataSize) == B_OK) 965 Find(startAt, data, dataSize, caseInsensitive, progressMonitor); 966 } 967 968 default: 969 BLooper::MessageReceived(message); 970 break; 971 } 972 } 973 974 975 void 976 EditorLooper::Find(off_t startAt, const uint8* data, size_t dataSize, 977 bool caseInsensitive, BMessenger progressMonitor) 978 { 979 fQuitFind = false; 980 981 BAutolock locker(fEditor); 982 983 bigtime_t startTime = system_time(); 984 985 off_t foundAt = fEditor.Find(startAt, data, dataSize, caseInsensitive, 986 true, progressMonitor, &fQuitFind); 987 if (foundAt >= B_OK) { 988 fEditor.SetViewOffset(foundAt); 989 990 // select the part in our target 991 BMessage message(kMsgSetSelection); 992 message.AddInt64("start", foundAt - fEditor.ViewOffset()); 993 message.AddInt64("end", foundAt + dataSize - 1 - fEditor.ViewOffset()); 994 fMessenger.SendMessage(&message); 995 } else if (foundAt == B_ENTRY_NOT_FOUND) { 996 if (system_time() > startTime + 8000000LL) { 997 // If the user had to wait more than 8 seconds for the result, 998 // we are trying to please him with a requester... 999 BAlert* alert = new BAlert(B_TRANSLATE("DiskProbe request"), 1000 B_TRANSLATE("Could not find search string."), 1001 B_TRANSLATE("OK"), NULL, NULL, B_WIDTH_AS_USUAL, 1002 B_WARNING_ALERT); 1003 alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE); 1004 alert->Go(NULL); 1005 } else 1006 beep(); 1007 } 1008 } 1009 1010 1011 void 1012 EditorLooper::QuitFind() 1013 { 1014 fQuitFind = true; 1015 // this will cleanly stop the find process 1016 } 1017 1018 1019 // #pragma mark - TypeView 1020 1021 1022 TypeView::TypeView(BRect rect, const char* name, int32 index, 1023 DataEditor& editor, int32 resizingMode) 1024 : BView(rect, name, resizingMode, B_FRAME_EVENTS) 1025 { 1026 SetViewUIColor(B_PANEL_BACKGROUND_COLOR); 1027 1028 fTypeEditorView = GetTypeEditorAt(index, Frame(), editor); 1029 if (fTypeEditorView == NULL) { 1030 AddChild(new BStringView(Bounds(), B_TRANSLATE("Type editor"), 1031 B_TRANSLATE("Type editor not supported"), B_FOLLOW_NONE)); 1032 } else 1033 AddChild(fTypeEditorView); 1034 1035 if ((fTypeEditorView->ResizingMode() & (B_FOLLOW_RIGHT | B_FOLLOW_BOTTOM)) 1036 != 0) { 1037 BRect rect = Bounds(); 1038 1039 BRect frame = fTypeEditorView->Frame(); 1040 rect.left = frame.left; 1041 rect.top = frame.top; 1042 if ((fTypeEditorView->ResizingMode() & B_FOLLOW_RIGHT) == 0) 1043 rect.right = frame.right; 1044 if ((fTypeEditorView->ResizingMode() & B_FOLLOW_BOTTOM) == 0) 1045 rect.bottom = frame.bottom; 1046 1047 fTypeEditorView->ResizeTo(rect.Width(), rect.Height()); 1048 } 1049 } 1050 1051 1052 TypeView::~TypeView() 1053 { 1054 } 1055 1056 1057 void 1058 TypeView::FrameResized(float width, float height) 1059 { 1060 BRect rect = Bounds(); 1061 1062 BPoint point = fTypeEditorView->Frame().LeftTop(); 1063 if ((fTypeEditorView->ResizingMode() & B_FOLLOW_RIGHT) == 0) 1064 point.x = (rect.Width() - fTypeEditorView->Bounds().Width()) / 2; 1065 if ((fTypeEditorView->ResizingMode() & B_FOLLOW_BOTTOM) == 0) 1066 point.y = (rect.Height() - fTypeEditorView->Bounds().Height()) / 2; 1067 1068 fTypeEditorView->MoveTo(point); 1069 } 1070 1071 1072 // #pragma mark - ProbeView 1073 1074 1075 ProbeView::ProbeView(entry_ref* ref, const char* attribute, 1076 const BMessage* settings) 1077 : BView("probeView", B_WILL_DRAW), 1078 fPrintSettings(NULL), 1079 fTypeView(NULL), 1080 fLastSearch(NULL) 1081 { 1082 fEditor.SetTo(*ref, attribute); 1083 1084 int32 baseType = kHexBase; 1085 float fontSize = be_plain_font->Size(); 1086 if (settings != NULL) { 1087 settings->FindInt32("base_type", &baseType); 1088 settings->FindFloat("font_size", &fontSize); 1089 } 1090 1091 fHeaderView = new HeaderView(&fEditor.Ref(), fEditor); 1092 fHeaderView->SetBase((base_type)baseType); 1093 1094 fDataView = new DataView(fEditor); 1095 fDataView->SetBase((base_type)baseType); 1096 fDataView->SetFontSize(fontSize); 1097 1098 fScrollView = new BScrollView("scroller", fDataView, B_WILL_DRAW, true, 1099 true); 1100 1101 BLayoutBuilder::Group<>(this, B_VERTICAL, 0) 1102 .SetInsets(-1, -1, -1, -1) 1103 .Add(fHeaderView) 1104 .AddGroup(B_VERTICAL, 0) 1105 .SetInsets(-1, 0, -1, -1) 1106 .Add(fScrollView) 1107 .End(); 1108 1109 fDataView->UpdateScroller(); 1110 } 1111 1112 1113 ProbeView::~ProbeView() 1114 { 1115 } 1116 1117 1118 void 1119 ProbeView::DetachedFromWindow() 1120 { 1121 fEditorLooper->QuitFind(); 1122 1123 if (fEditorLooper->Lock()) 1124 fEditorLooper->Quit(); 1125 fEditorLooper = NULL; 1126 1127 fEditor.StopWatching(this); 1128 fDataView->StopWatching(fHeaderView, kDataViewCursorPosition); 1129 fDataView->StopWatching(this, kDataViewSelection); 1130 fDataView->StopWatching(this, kDataViewPreferredSize); 1131 be_clipboard->StopWatching(this); 1132 } 1133 1134 1135 void 1136 ProbeView::_UpdateAttributesMenu(BMenu* menu) 1137 { 1138 // remove old contents 1139 1140 for (int32 i = menu->CountItems(); i-- > 0;) { 1141 delete menu->RemoveItem(i); 1142 } 1143 1144 // add new items (sorted) 1145 1146 BNode node(&fEditor.AttributeRef()); 1147 if (node.InitCheck() == B_OK) { 1148 char attribute[B_ATTR_NAME_LENGTH]; 1149 node.RewindAttrs(); 1150 1151 while (node.GetNextAttrName(attribute) == B_OK) { 1152 attr_info info; 1153 if (node.GetAttrInfo(attribute, &info) != B_OK) 1154 continue; 1155 1156 char type[16]; 1157 type[0] = '['; 1158 get_type_string(type + 1, sizeof(type) - 2, info.type); 1159 strcat(type, "]"); 1160 1161 // find where to insert 1162 int32 i; 1163 for (i = 0; i < menu->CountItems(); i++) { 1164 if (strcasecmp(menu->ItemAt(i)->Label(), attribute) > 0) 1165 break; 1166 } 1167 1168 BMessage* message = new BMessage(B_REFS_RECEIVED); 1169 message->AddRef("refs", &fEditor.AttributeRef()); 1170 message->AddString("attributes", attribute); 1171 1172 menu->AddItem(new TypeMenuItem(attribute, type, message), i); 1173 } 1174 } 1175 1176 if (menu->CountItems() == 0) { 1177 // if there are no attributes, add an item to the menu 1178 // that says so 1179 BMenuItem* item = new BMenuItem(B_TRANSLATE_COMMENT("none", 1180 "No attributes"), NULL); 1181 item->SetEnabled(false); 1182 menu->AddItem(item); 1183 } 1184 1185 menu->SetTargetForItems(be_app); 1186 } 1187 1188 1189 void 1190 ProbeView::AddSaveMenuItems(BMenu* menu, int32 index) 1191 { 1192 menu->AddItem(fSaveMenuItem = new BMenuItem(B_TRANSLATE("Save"), 1193 new BMessage(B_SAVE_REQUESTED), 'S'), index); 1194 fSaveMenuItem->SetTarget(this); 1195 fSaveMenuItem->SetEnabled(false); 1196 //menu->AddItem(new BMenuItem("Save As" B_UTF8_ELLIPSIS, NULL), index); 1197 } 1198 1199 1200 void 1201 ProbeView::AddPrintMenuItems(BMenu* menu, int32 index) 1202 { 1203 BMenuItem* item; 1204 menu->AddItem(item = new BMenuItem(B_TRANSLATE("Page setup" B_UTF8_ELLIPSIS), 1205 new BMessage(kMsgPageSetup)), index++); 1206 item->SetTarget(this); 1207 menu->AddItem(item = new BMenuItem(B_TRANSLATE("Print" B_UTF8_ELLIPSIS), 1208 new BMessage(kMsgPrint), 'P'), index++); 1209 item->SetTarget(this); 1210 } 1211 1212 1213 void 1214 ProbeView::AddViewAsMenuItems() 1215 { 1216 #if 0 1217 BMenuBar* bar = Window()->KeyMenuBar(); 1218 if (bar == NULL) 1219 return; 1220 1221 BMenuItem* item = bar->FindItem(B_TRANSLATE("View")); 1222 BMenu* menu = NULL; 1223 if (item != NULL) 1224 menu = item->Submenu(); 1225 else 1226 menu = bar->SubmenuAt(bar->CountItems() - 1); 1227 1228 if (menu == NULL) 1229 return; 1230 1231 menu->AddSeparatorItem(); 1232 1233 BMenu* subMenu = new BMenu(B_TRANSLATE("View As")); 1234 subMenu->SetRadioMode(true); 1235 1236 BMessage* message = new BMessage(kMsgViewAs); 1237 subMenu->AddItem(item = new BMenuItem(B_TRANSLATE("Raw"), message)); 1238 item->SetMarked(true); 1239 1240 const char* name; 1241 for (int32 i = 0; GetNthTypeEditor(i, &name) == B_OK; i++) { 1242 message = new BMessage(kMsgViewAs); 1243 message->AddInt32("editor index", i); 1244 subMenu->AddItem(new BMenuItem(name, message)); 1245 } 1246 1247 subMenu->SetTargetForItems(this); 1248 menu->AddItem(new BMenuItem(subMenu)); 1249 #endif 1250 } 1251 1252 1253 void 1254 ProbeView::AttachedToWindow() 1255 { 1256 BView::AttachedToWindow(); 1257 1258 fEditorLooper = new EditorLooper(fEditor.Ref().name, fEditor, 1259 BMessenger(fDataView)); 1260 fEditorLooper->Run(); 1261 1262 fEditor.StartWatching(this); 1263 fDataView->StartWatching(fHeaderView, kDataViewCursorPosition); 1264 fDataView->StartWatching(this, kDataViewSelection); 1265 fDataView->StartWatching(this, kDataViewPreferredSize); 1266 be_clipboard->StartWatching(this); 1267 1268 // Add menu to window 1269 1270 BMenuBar* bar = Window()->KeyMenuBar(); 1271 if (bar == NULL) { 1272 // there is none? Well, but we really want to have one 1273 bar = new BMenuBar(""); 1274 Window()->AddChild(bar); 1275 1276 BMenu* menu = new BMenu(fEditor.IsAttribute() 1277 ? B_TRANSLATE("Attribute") : fEditor.IsDevice() 1278 ? B_TRANSLATE("Device") : B_TRANSLATE("File")); 1279 AddSaveMenuItems(menu, 0); 1280 menu->AddSeparatorItem(); 1281 AddPrintMenuItems(menu, menu->CountItems()); 1282 menu->AddSeparatorItem(); 1283 1284 menu->AddItem(new BMenuItem(B_TRANSLATE("Close"), 1285 new BMessage(B_CLOSE_REQUESTED), 'W')); 1286 bar->AddItem(menu); 1287 } 1288 1289 // "Edit" menu 1290 1291 BMenu* menu = new BMenu(B_TRANSLATE("Edit")); 1292 BMenuItem* item; 1293 menu->AddItem(fUndoMenuItem = new BMenuItem(B_TRANSLATE("Undo"), 1294 new BMessage(B_UNDO), 'Z')); 1295 fUndoMenuItem->SetEnabled(fEditor.CanUndo()); 1296 fUndoMenuItem->SetTarget(fDataView); 1297 menu->AddItem(fRedoMenuItem = new BMenuItem(B_TRANSLATE("Redo"), 1298 new BMessage(B_REDO), 'Z', B_SHIFT_KEY)); 1299 fRedoMenuItem->SetEnabled(fEditor.CanRedo()); 1300 fRedoMenuItem->SetTarget(fDataView); 1301 menu->AddSeparatorItem(); 1302 menu->AddItem(item = new BMenuItem(B_TRANSLATE("Copy"), 1303 new BMessage(B_COPY), 'C')); 1304 item->SetTarget(NULL, Window()); 1305 menu->AddItem(fPasteMenuItem = new BMenuItem(B_TRANSLATE("Paste"), 1306 new BMessage(B_PASTE), 'V')); 1307 fPasteMenuItem->SetTarget(NULL, Window()); 1308 _CheckClipboard(); 1309 menu->AddItem(item = new BMenuItem(B_TRANSLATE("Select all"), 1310 new BMessage(B_SELECT_ALL), 'A')); 1311 item->SetTarget(NULL, Window()); 1312 menu->AddSeparatorItem(); 1313 menu->AddItem(item = new BMenuItem(B_TRANSLATE("Find" B_UTF8_ELLIPSIS), 1314 new BMessage(kMsgOpenFindWindow), 'F')); 1315 item->SetTarget(this); 1316 menu->AddItem(fFindAgainMenuItem = new BMenuItem(B_TRANSLATE("Find again"), 1317 new BMessage(kMsgFind), 'G')); 1318 fFindAgainMenuItem->SetEnabled(false); 1319 fFindAgainMenuItem->SetTarget(this); 1320 bar->AddItem(menu); 1321 1322 // "Block" menu 1323 1324 menu = new BMenu(B_TRANSLATE("Block")); 1325 BMessage* message = new BMessage(kMsgPositionUpdate); 1326 message->AddInt32("delta", 1); 1327 menu->AddItem(item = new BMenuItem(B_TRANSLATE("Next"), message, 1328 B_RIGHT_ARROW)); 1329 item->SetTarget(fHeaderView); 1330 message = new BMessage(kMsgPositionUpdate); 1331 message->AddInt32("delta", -1); 1332 menu->AddItem(item = new BMenuItem(B_TRANSLATE("Previous"), message, 1333 B_LEFT_ARROW)); 1334 item->SetTarget(fHeaderView); 1335 menu->AddItem(item = new BMenuItem(B_TRANSLATE("Back"), 1336 new BMessage(kMsgLastPosition), 'J')); 1337 item->SetTarget(fHeaderView); 1338 1339 BMenu* subMenu = new BMenu(B_TRANSLATE("Selection")); 1340 message = new BMessage(kMsgPositionUpdate); 1341 message->AddInt64("block", 0); 1342 subMenu->AddItem(fNativeMenuItem = new BMenuItem("", message, 'K')); 1343 fNativeMenuItem->SetTarget(fHeaderView); 1344 message = new BMessage(*message); 1345 subMenu->AddItem(fSwappedMenuItem = new BMenuItem("", message, 'L')); 1346 fSwappedMenuItem->SetTarget(fHeaderView); 1347 menu->AddItem(new BMenuItem(subMenu)); 1348 _UpdateSelectionMenuItems(0, 0); 1349 menu->AddSeparatorItem(); 1350 1351 fBookmarkMenu = new BMenu(B_TRANSLATE("Bookmarks")); 1352 fBookmarkMenu->AddItem(item = new BMenuItem(B_TRANSLATE("Add"), 1353 new BMessage(kMsgAddBookmark), 'B')); 1354 item->SetTarget(this); 1355 menu->AddItem(new BMenuItem(fBookmarkMenu)); 1356 bar->AddItem(menu); 1357 1358 // "Attributes" menu (it's only visible if the underlying 1359 // file system actually supports attributes) 1360 1361 BDirectory directory; 1362 BVolume volume; 1363 if (directory.SetTo(&fEditor.AttributeRef()) == B_OK 1364 && directory.IsRootDirectory()) 1365 directory.GetVolume(&volume); 1366 else 1367 fEditor.File().GetVolume(&volume); 1368 1369 if (!fEditor.IsAttribute() && volume.InitCheck() == B_OK 1370 && (volume.KnowsMime() || volume.KnowsAttr())) { 1371 bar->AddItem(menu = new BMenu(B_TRANSLATE("Attributes"))); 1372 _UpdateAttributesMenu(menu); 1373 } 1374 1375 // "View" menu 1376 1377 menu = new BMenu(B_TRANSLATE_COMMENT("View", 1378 "This is the last menubar item 'File Edit Block View'")); 1379 1380 // Number Base (hex/decimal) 1381 1382 subMenu = new BMenu(B_TRANSLATE_COMMENT("Base", "A menu item, the number " 1383 "that is basis for a system of calculation. The base 10 system is a " 1384 "decimal system. This is in the same menu window than 'Font size' " 1385 "and 'BlockSize'")); 1386 message = new BMessage(kMsgBaseType); 1387 message->AddInt32("base_type", kDecimalBase); 1388 subMenu->AddItem(item = new BMenuItem(B_TRANSLATE_COMMENT("Decimal", 1389 "A menu item, as short as possible, noun is recommended if it is " 1390 "shorter than adjective."), message, 'D')); 1391 item->SetTarget(this); 1392 if (fHeaderView->Base() == kDecimalBase) 1393 item->SetMarked(true); 1394 1395 message = new BMessage(kMsgBaseType); 1396 message->AddInt32("base_type", kHexBase); 1397 subMenu->AddItem(item = new BMenuItem(B_TRANSLATE_COMMENT("Hex", 1398 "A menu item, as short as possible, noun is recommended if it is " 1399 "shorter than adjective."), message, 'H')); 1400 item->SetTarget(this); 1401 if (fHeaderView->Base() == kHexBase) 1402 item->SetMarked(true); 1403 1404 subMenu->SetRadioMode(true); 1405 menu->AddItem(new BMenuItem(subMenu)); 1406 1407 // Block Size 1408 1409 subMenu = new BMenu(B_TRANSLATE_COMMENT("Block size", "Menu item. " 1410 "This is in the same menu window than 'Base' and 'Font size'")); 1411 subMenu->SetRadioMode(true); 1412 const uint32 blockSizes[] = {512, 1024, 2048, 4096}; 1413 for (uint32 i = 0; i < sizeof(blockSizes) / sizeof(blockSizes[0]); i++) { 1414 char buffer[32]; 1415 snprintf(buffer, sizeof(buffer), "%" B_PRId32 "%s", blockSizes[i], 1416 fEditor.IsDevice() && fEditor.BlockSize() == blockSizes[i] 1417 ? B_TRANSLATE(" (native)") : ""); 1418 subMenu->AddItem(item = new BMenuItem(buffer, 1419 message = new BMessage(kMsgBlockSize))); 1420 message->AddInt32("block_size", blockSizes[i]); 1421 if (fEditor.BlockSize() == blockSizes[i]) 1422 item->SetMarked(true); 1423 } 1424 if (subMenu->FindMarked() == NULL) { 1425 // if the device has some weird block size, we'll add it here, too 1426 char buffer[32]; 1427 snprintf(buffer, sizeof(buffer), B_TRANSLATE("%ld (native)"), 1428 fEditor.BlockSize()); 1429 subMenu->AddItem(item = new BMenuItem(buffer, 1430 message = new BMessage(kMsgBlockSize))); 1431 message->AddInt32("block_size", fEditor.BlockSize()); 1432 item->SetMarked(true); 1433 } 1434 subMenu->SetTargetForItems(this); 1435 menu->AddItem(new BMenuItem(subMenu)); 1436 menu->AddSeparatorItem(); 1437 1438 // Font Size 1439 1440 subMenu = new BMenu(B_TRANSLATE("Font size")); 1441 subMenu->SetRadioMode(true); 1442 const int32 fontSizes[] = {9, 10, 11, 12, 13, 14, 18, 24, 36, 48}; 1443 int32 fontSize = int32(fDataView->FontSize() + 0.5); 1444 if (fDataView->FontSizeFitsBounds()) 1445 fontSize = 0; 1446 for (uint32 i = 0; i < sizeof(fontSizes) / sizeof(fontSizes[0]); i++) { 1447 char buffer[16]; 1448 snprintf(buffer, sizeof(buffer), "%" B_PRId32, fontSizes[i]); 1449 subMenu->AddItem(item = new BMenuItem(buffer, 1450 message = new BMessage(kMsgFontSize))); 1451 message->AddFloat("font_size", fontSizes[i]); 1452 if (fontSizes[i] == fontSize) 1453 item->SetMarked(true); 1454 } 1455 subMenu->AddSeparatorItem(); 1456 subMenu->AddItem(item = new BMenuItem(B_TRANSLATE_COMMENT("Fit", 1457 "Size of fonts, fits to available room"), 1458 message = new BMessage(kMsgFontSize))); 1459 message->AddFloat("font_size", 0.0f); 1460 if (fontSize == 0) 1461 item->SetMarked(true); 1462 1463 subMenu->SetTargetForItems(this); 1464 menu->AddItem(new BMenuItem(subMenu)); 1465 1466 bar->AddItem(menu); 1467 } 1468 1469 1470 void 1471 ProbeView::AllAttached() 1472 { 1473 fHeaderView->SetTarget(fEditorLooper); 1474 } 1475 1476 1477 void 1478 ProbeView::WindowActivated(bool active) 1479 { 1480 if (!active) 1481 return; 1482 1483 fDataView->MakeFocus(true); 1484 1485 // set this view as the current find panel's target 1486 BMessage target(kMsgFindTarget); 1487 target.AddMessenger("target", this); 1488 be_app_messenger.SendMessage(&target); 1489 } 1490 1491 1492 void 1493 ProbeView::_UpdateSelectionMenuItems(int64 start, int64 end) 1494 { 1495 int64 position = 0; 1496 const uint8* data = fDataView->DataAt(start); 1497 if (data == NULL) { 1498 fNativeMenuItem->SetEnabled(false); 1499 fSwappedMenuItem->SetEnabled(false); 1500 return; 1501 } 1502 1503 // retrieve native endian position 1504 1505 int size; 1506 if (end < start + 8) 1507 size = end + 1 - start; 1508 else 1509 size = 8; 1510 1511 int64 bigEndianPosition = 0; 1512 memcpy(&bigEndianPosition, data, size); 1513 1514 position = B_BENDIAN_TO_HOST_INT64(bigEndianPosition) >> (8 * (8 - size)); 1515 1516 // update menu items 1517 1518 char buffer[128]; 1519 if (fDataView->Base() == kHexBase) { 1520 snprintf(buffer, sizeof(buffer), B_TRANSLATE("Native: 0x%0*Lx"), 1521 size * 2, position); 1522 } else { 1523 snprintf(buffer, sizeof(buffer), B_TRANSLATE("Native: %lld (0x%0*Lx)"), 1524 position, size * 2, position); 1525 } 1526 1527 fNativeMenuItem->SetLabel(buffer); 1528 fNativeMenuItem->SetEnabled(position >= 0 1529 && (off_t)(position * fEditor.BlockSize()) < fEditor.FileSize()); 1530 fNativeMenuItem->Message()->ReplaceInt64("block", position); 1531 1532 position = B_SWAP_INT64(position) >> (8 * (8 - size)); 1533 if (fDataView->Base() == kHexBase) { 1534 snprintf(buffer, sizeof(buffer), B_TRANSLATE("Swapped: 0x%0*Lx"), 1535 size * 2, position); 1536 } else { 1537 snprintf(buffer, sizeof(buffer), B_TRANSLATE("Swapped: %lld (0x%0*Lx)"), 1538 position, size * 2, position); 1539 } 1540 1541 fSwappedMenuItem->SetLabel(buffer); 1542 fSwappedMenuItem->SetEnabled(position >= 0 && (off_t)(position * fEditor.BlockSize()) < fEditor.FileSize()); 1543 fSwappedMenuItem->Message()->ReplaceInt64("block", position); 1544 } 1545 1546 1547 void 1548 ProbeView::_UpdateBookmarkMenuItems() 1549 { 1550 for (int32 i = 2; i < fBookmarkMenu->CountItems(); i++) { 1551 BMenuItem* item = fBookmarkMenu->ItemAt(i); 1552 if (item == NULL) 1553 break; 1554 1555 BMessage* message = item->Message(); 1556 if (message == NULL) 1557 break; 1558 1559 off_t block = message->FindInt64("block"); 1560 1561 char buffer[128]; 1562 if (fDataView->Base() == kHexBase) 1563 snprintf(buffer, sizeof(buffer), B_TRANSLATE("Block 0x%Lx"), block); 1564 else 1565 snprintf(buffer, sizeof(buffer), B_TRANSLATE("Block %lld (0x%Lx)"), block, block); 1566 1567 item->SetLabel(buffer); 1568 } 1569 } 1570 1571 1572 void 1573 ProbeView::_AddBookmark(off_t position) 1574 { 1575 int32 count = fBookmarkMenu->CountItems(); 1576 1577 if (count == 1) { 1578 fBookmarkMenu->AddSeparatorItem(); 1579 count++; 1580 } 1581 1582 // insert current position as bookmark 1583 1584 off_t block = position / fEditor.BlockSize(); 1585 1586 off_t bookmark = -1; 1587 BMenuItem* item; 1588 int32 i; 1589 for (i = 2; (item = fBookmarkMenu->ItemAt(i)) != NULL; i++) { 1590 BMessage* message = item->Message(); 1591 if (message != NULL && message->FindInt64("block", &bookmark) == B_OK) { 1592 if (block <= bookmark) 1593 break; 1594 } 1595 } 1596 1597 // the bookmark already exists 1598 if (block == bookmark) 1599 return; 1600 1601 char buffer[128]; 1602 if (fDataView->Base() == kHexBase) 1603 snprintf(buffer, sizeof(buffer), B_TRANSLATE("Block 0x%Lx"), block); 1604 else 1605 snprintf(buffer, sizeof(buffer), B_TRANSLATE("Block %lld (0x%Lx)"), block, block); 1606 1607 BMessage* message; 1608 item = new BMenuItem(buffer, message = new BMessage(kMsgPositionUpdate)); 1609 item->SetTarget(fHeaderView); 1610 if (count < 12) 1611 item->SetShortcut('0' + count - 2, B_COMMAND_KEY); 1612 message->AddInt64("block", block); 1613 1614 fBookmarkMenu->AddItem(item, i); 1615 } 1616 1617 1618 void 1619 ProbeView::_RemoveTypeEditor() 1620 { 1621 if (fTypeView == NULL) 1622 return; 1623 1624 if (Parent() != NULL) 1625 Parent()->RemoveChild(fTypeView); 1626 else 1627 Window()->RemoveChild(fTypeView); 1628 1629 delete fTypeView; 1630 fTypeView = NULL; 1631 } 1632 1633 1634 void 1635 ProbeView::_SetTypeEditor(int32 index) 1636 { 1637 if (index == -1) { 1638 // remove type editor, show raw editor 1639 if (IsHidden()) 1640 Show(); 1641 1642 _RemoveTypeEditor(); 1643 } else { 1644 // hide raw editor, create and show type editor 1645 if (!IsHidden()) 1646 Hide(); 1647 1648 _RemoveTypeEditor(); 1649 1650 fTypeView = new TypeView(Frame(), "type shell", index, fEditor, 1651 B_FOLLOW_ALL); 1652 1653 if (Parent() != NULL) 1654 Parent()->AddChild(fTypeView); 1655 else 1656 Window()->AddChild(fTypeView); 1657 } 1658 } 1659 1660 1661 void 1662 ProbeView::_CheckClipboard() 1663 { 1664 if (!be_clipboard->Lock()) 1665 return; 1666 1667 bool hasData = false; 1668 BMessage* clip; 1669 if ((clip = be_clipboard->Data()) != NULL) { 1670 const void* data; 1671 ssize_t size; 1672 if (clip->FindData(B_FILE_MIME_TYPE, B_MIME_TYPE, &data, &size) == B_OK 1673 || clip->FindData("text/plain", B_MIME_TYPE, &data, &size) == B_OK) 1674 hasData = true; 1675 } 1676 1677 be_clipboard->Unlock(); 1678 1679 fPasteMenuItem->SetEnabled(hasData); 1680 } 1681 1682 1683 status_t 1684 ProbeView::_PageSetup() 1685 { 1686 BPrintJob printJob(Window()->Title()); 1687 if (fPrintSettings != NULL) 1688 printJob.SetSettings(new BMessage(*fPrintSettings)); 1689 1690 status_t status = printJob.ConfigPage(); 1691 if (status == B_OK) { 1692 // replace the print settings on success 1693 delete fPrintSettings; 1694 fPrintSettings = printJob.Settings(); 1695 } 1696 1697 return status; 1698 } 1699 1700 1701 void 1702 ProbeView::_Print() 1703 { 1704 if (fPrintSettings == NULL && _PageSetup() != B_OK) 1705 return; 1706 1707 BPrintJob printJob(Window()->Title()); 1708 printJob.SetSettings(new BMessage(*fPrintSettings)); 1709 1710 if (printJob.ConfigJob() == B_OK) { 1711 BRect rect = printJob.PrintableRect(); 1712 1713 float width, height; 1714 fDataView->GetPreferredSize(&width, &height); 1715 1716 printJob.BeginJob(); 1717 1718 fDataView->SetScale(rect.Width() / width); 1719 printJob.DrawView(fDataView, rect, rect.LeftTop()); 1720 fDataView->SetScale(1.0); 1721 printJob.SpoolPage(); 1722 1723 printJob.CommitJob(); 1724 } 1725 } 1726 1727 1728 status_t 1729 ProbeView::_Save() 1730 { 1731 status_t status = fEditor.Save(); 1732 if (status == B_OK) 1733 return B_OK; 1734 1735 char buffer[1024]; 1736 snprintf(buffer, sizeof(buffer), 1737 B_TRANSLATE("Writing to the file failed:\n" 1738 "%s\n\n" 1739 "All changes will be lost when you quit."), 1740 strerror(status)); 1741 1742 BAlert* alert = new BAlert(B_TRANSLATE("DiskProbe request"), 1743 buffer, B_TRANSLATE("OK"), NULL, NULL, 1744 B_WIDTH_AS_USUAL, B_WARNING_ALERT); 1745 alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE); 1746 alert->Go(NULL); 1747 1748 return status; 1749 } 1750 1751 1752 bool 1753 ProbeView::QuitRequested() 1754 { 1755 fEditorLooper->QuitFind(); 1756 1757 if (!fEditor.IsModified()) 1758 return true; 1759 1760 BAlert* alert = new BAlert(B_TRANSLATE("DiskProbe request"), 1761 B_TRANSLATE("Save changes before closing?"), B_TRANSLATE("Cancel"), 1762 B_TRANSLATE("Don't save"), B_TRANSLATE("Save"), B_WIDTH_AS_USUAL, 1763 B_OFFSET_SPACING, B_WARNING_ALERT); 1764 alert->SetShortcut(0, B_ESCAPE); 1765 alert->SetShortcut(1, 'd'); 1766 alert->SetShortcut(2, 's'); 1767 int32 chosen = alert->Go(); 1768 1769 if (chosen == 0) 1770 return false; 1771 if (chosen == 1) 1772 return true; 1773 1774 return _Save() == B_OK; 1775 } 1776 1777 1778 void 1779 ProbeView::MessageReceived(BMessage* message) 1780 { 1781 switch (message->what) { 1782 case B_SAVE_REQUESTED: 1783 _Save(); 1784 break; 1785 1786 case B_OBSERVER_NOTICE_CHANGE: { 1787 int32 what; 1788 if (message->FindInt32(B_OBSERVE_WHAT_CHANGE, &what) != B_OK) 1789 break; 1790 1791 switch (what) { 1792 case kDataViewSelection: 1793 { 1794 int64 start, end; 1795 if (message->FindInt64("start", &start) == B_OK 1796 && message->FindInt64("end", &end) == B_OK) 1797 _UpdateSelectionMenuItems(start, end); 1798 break; 1799 } 1800 } 1801 break; 1802 } 1803 1804 case kMsgBaseType: 1805 { 1806 int32 type; 1807 if (message->FindInt32("base_type", &type) != B_OK) 1808 break; 1809 1810 fHeaderView->SetBase((base_type)type); 1811 fDataView->SetBase((base_type)type); 1812 1813 // The selection menu items depend on the base type as well 1814 int32 start, end; 1815 fDataView->GetSelection(start, end); 1816 _UpdateSelectionMenuItems(start, end); 1817 1818 _UpdateBookmarkMenuItems(); 1819 1820 // update the application's settings 1821 BMessage update(*message); 1822 update.what = kMsgSettingsChanged; 1823 be_app_messenger.SendMessage(&update); 1824 break; 1825 } 1826 1827 case kMsgFontSize: 1828 { 1829 float size; 1830 if (message->FindFloat("font_size", &size) != B_OK) 1831 break; 1832 1833 fDataView->SetFontSize(size); 1834 1835 // update the application's settings 1836 BMessage update(*message); 1837 update.what = kMsgSettingsChanged; 1838 be_app_messenger.SendMessage(&update); 1839 break; 1840 } 1841 1842 case kMsgBlockSize: 1843 { 1844 int32 blockSize; 1845 if (message->FindInt32("block_size", &blockSize) != B_OK) 1846 break; 1847 1848 BAutolock locker(fEditor); 1849 1850 if (fEditor.SetViewSize(blockSize) == B_OK 1851 && fEditor.SetBlockSize(blockSize) == B_OK) 1852 fHeaderView->SetTo(fEditor.ViewOffset(), blockSize); 1853 break; 1854 } 1855 1856 case kMsgViewAs: 1857 { 1858 int32 index; 1859 if (message->FindInt32("editor index", &index) != B_OK) 1860 index = -1; 1861 1862 _SetTypeEditor(index); 1863 break; 1864 } 1865 1866 case kMsgAddBookmark: 1867 _AddBookmark(fHeaderView->Position()); 1868 break; 1869 1870 case kMsgPrint: 1871 _Print(); 1872 break; 1873 1874 case kMsgPageSetup: 1875 _PageSetup(); 1876 break; 1877 1878 case kMsgOpenFindWindow: 1879 { 1880 fEditorLooper->QuitFind(); 1881 1882 // set this view as the current find panel's target 1883 BMessage find(*fFindAgainMenuItem->Message()); 1884 find.what = kMsgOpenFindWindow; 1885 find.AddMessenger("target", this); 1886 be_app_messenger.SendMessage(&find); 1887 break; 1888 } 1889 1890 case kMsgFind: 1891 { 1892 const uint8* data; 1893 ssize_t size; 1894 if (message->FindData("data", B_RAW_TYPE, (const void**)&data, 1895 &size) != B_OK) { 1896 // search again for last pattern 1897 BMessage* itemMessage = fFindAgainMenuItem->Message(); 1898 if (itemMessage == NULL || itemMessage->FindData("data", 1899 B_RAW_TYPE, (const void**)&data, &size) != B_OK) { 1900 // this shouldn't ever happen, but well... 1901 beep(); 1902 break; 1903 } 1904 } else { 1905 // remember the search pattern 1906 fFindAgainMenuItem->SetMessage(new BMessage(*message)); 1907 fFindAgainMenuItem->SetEnabled(true); 1908 } 1909 1910 int32 start, end; 1911 fDataView->GetSelection(start, end); 1912 1913 BMessage find(*message); 1914 find.AddInt64("start", fHeaderView->Position() + start + 1); 1915 find.AddMessenger("progress_monitor", BMessenger(fHeaderView)); 1916 fEditorLooper->PostMessage(&find); 1917 break; 1918 } 1919 1920 case kMsgStopFind: 1921 fEditorLooper->QuitFind(); 1922 break; 1923 1924 case B_NODE_MONITOR: 1925 { 1926 switch (message->FindInt32("opcode")) { 1927 case B_STAT_CHANGED: 1928 fEditor.ForceUpdate(); 1929 break; 1930 case B_ATTR_CHANGED: 1931 { 1932 const char* name; 1933 if (message->FindString("attr", &name) != B_OK) 1934 break; 1935 1936 if (fEditor.IsAttribute()) { 1937 if (!strcmp(name, fEditor.Attribute())) 1938 fEditor.ForceUpdate(); 1939 } else { 1940 BMenuBar* bar = Window()->KeyMenuBar(); 1941 if (bar != NULL) { 1942 BMenuItem* item = bar->FindItem("Attributes"); 1943 if (item != NULL && item->Submenu() != NULL) 1944 _UpdateAttributesMenu(item->Submenu()); 1945 } 1946 } 1947 1948 // There might be a new icon 1949 if (!strcmp(name, "BEOS:TYPE") 1950 || !strcmp(name, "BEOS:M:STD_ICON") 1951 || !strcmp(name, "BEOS:L:STD_ICON") 1952 || !strcmp(name, "BEOS:ICON")) 1953 fHeaderView->UpdateIcon(); 1954 break; 1955 } 1956 } 1957 break; 1958 } 1959 1960 case B_CLIPBOARD_CHANGED: 1961 _CheckClipboard(); 1962 break; 1963 1964 case kMsgDataEditorStateChange: 1965 { 1966 bool enabled; 1967 if (message->FindBool("can_undo", &enabled) == B_OK) 1968 fUndoMenuItem->SetEnabled(enabled); 1969 1970 if (message->FindBool("can_redo", &enabled) == B_OK) 1971 fRedoMenuItem->SetEnabled(enabled); 1972 1973 if (message->FindBool("modified", &enabled) == B_OK) 1974 fSaveMenuItem->SetEnabled(enabled); 1975 break; 1976 } 1977 1978 default: 1979 BView::MessageReceived(message); 1980 } 1981 } 1982 1983