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, (long long int)position); 1522 } else { 1523 snprintf(buffer, sizeof(buffer), B_TRANSLATE("Native: %lld (0x%0*Lx)"), 1524 (long long int)position, size * 2, (long long int)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, (long long int)position); 1536 } else { 1537 snprintf(buffer, sizeof(buffer), B_TRANSLATE("Swapped: %lld (0x%0*Lx)"), 1538 (long long int)position, size * 2, (long long int)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"), (long long unsigned)block); 1564 else { 1565 snprintf(buffer, sizeof(buffer), B_TRANSLATE("Block %lld (0x%Lx)"), 1566 (long long int)block, (long long unsigned)block); 1567 } 1568 1569 item->SetLabel(buffer); 1570 } 1571 } 1572 1573 1574 void 1575 ProbeView::_AddBookmark(off_t position) 1576 { 1577 int32 count = fBookmarkMenu->CountItems(); 1578 1579 if (count == 1) { 1580 fBookmarkMenu->AddSeparatorItem(); 1581 count++; 1582 } 1583 1584 // insert current position as bookmark 1585 1586 off_t block = position / fEditor.BlockSize(); 1587 1588 off_t bookmark = -1; 1589 BMenuItem* item; 1590 int32 i; 1591 for (i = 2; (item = fBookmarkMenu->ItemAt(i)) != NULL; i++) { 1592 BMessage* message = item->Message(); 1593 if (message != NULL && message->FindInt64("block", &bookmark) == B_OK) { 1594 if (block <= bookmark) 1595 break; 1596 } 1597 } 1598 1599 // the bookmark already exists 1600 if (block == bookmark) 1601 return; 1602 1603 char buffer[128]; 1604 if (fDataView->Base() == kHexBase) 1605 snprintf(buffer, sizeof(buffer), B_TRANSLATE("Block 0x%Lx"), (long long unsigned)block); 1606 else { 1607 snprintf(buffer, sizeof(buffer), B_TRANSLATE("Block %lld (0x%Lx)"), 1608 (long long int)block, (long long unsigned)block); 1609 } 1610 1611 BMessage* message; 1612 item = new BMenuItem(buffer, message = new BMessage(kMsgPositionUpdate)); 1613 item->SetTarget(fHeaderView); 1614 if (count < 12) 1615 item->SetShortcut('0' + count - 2, B_COMMAND_KEY); 1616 message->AddInt64("block", block); 1617 1618 fBookmarkMenu->AddItem(item, i); 1619 } 1620 1621 1622 void 1623 ProbeView::_RemoveTypeEditor() 1624 { 1625 if (fTypeView == NULL) 1626 return; 1627 1628 if (Parent() != NULL) 1629 Parent()->RemoveChild(fTypeView); 1630 else 1631 Window()->RemoveChild(fTypeView); 1632 1633 delete fTypeView; 1634 fTypeView = NULL; 1635 } 1636 1637 1638 void 1639 ProbeView::_SetTypeEditor(int32 index) 1640 { 1641 if (index == -1) { 1642 // remove type editor, show raw editor 1643 if (IsHidden()) 1644 Show(); 1645 1646 _RemoveTypeEditor(); 1647 } else { 1648 // hide raw editor, create and show type editor 1649 if (!IsHidden()) 1650 Hide(); 1651 1652 _RemoveTypeEditor(); 1653 1654 fTypeView = new TypeView(Frame(), "type shell", index, fEditor, 1655 B_FOLLOW_ALL); 1656 1657 if (Parent() != NULL) 1658 Parent()->AddChild(fTypeView); 1659 else 1660 Window()->AddChild(fTypeView); 1661 } 1662 } 1663 1664 1665 void 1666 ProbeView::_CheckClipboard() 1667 { 1668 if (!be_clipboard->Lock()) 1669 return; 1670 1671 bool hasData = false; 1672 BMessage* clip; 1673 if ((clip = be_clipboard->Data()) != NULL) { 1674 const void* data; 1675 ssize_t size; 1676 if (clip->FindData(B_FILE_MIME_TYPE, B_MIME_TYPE, &data, &size) == B_OK 1677 || clip->FindData("text/plain", B_MIME_TYPE, &data, &size) == B_OK) 1678 hasData = true; 1679 } 1680 1681 be_clipboard->Unlock(); 1682 1683 fPasteMenuItem->SetEnabled(hasData); 1684 } 1685 1686 1687 status_t 1688 ProbeView::_PageSetup() 1689 { 1690 BPrintJob printJob(Window()->Title()); 1691 if (fPrintSettings != NULL) 1692 printJob.SetSettings(new BMessage(*fPrintSettings)); 1693 1694 status_t status = printJob.ConfigPage(); 1695 if (status == B_OK) { 1696 // replace the print settings on success 1697 delete fPrintSettings; 1698 fPrintSettings = printJob.Settings(); 1699 } 1700 1701 return status; 1702 } 1703 1704 1705 void 1706 ProbeView::_Print() 1707 { 1708 if (fPrintSettings == NULL && _PageSetup() != B_OK) 1709 return; 1710 1711 BPrintJob printJob(Window()->Title()); 1712 printJob.SetSettings(new BMessage(*fPrintSettings)); 1713 1714 if (printJob.ConfigJob() == B_OK) { 1715 BRect rect = printJob.PrintableRect(); 1716 1717 float width, height; 1718 fDataView->GetPreferredSize(&width, &height); 1719 1720 printJob.BeginJob(); 1721 1722 fDataView->SetScale(rect.Width() / width); 1723 printJob.DrawView(fDataView, rect, rect.LeftTop()); 1724 fDataView->SetScale(1.0); 1725 printJob.SpoolPage(); 1726 1727 printJob.CommitJob(); 1728 } 1729 } 1730 1731 1732 status_t 1733 ProbeView::_Save() 1734 { 1735 status_t status = fEditor.Save(); 1736 if (status == B_OK) 1737 return B_OK; 1738 1739 char buffer[1024]; 1740 snprintf(buffer, sizeof(buffer), 1741 B_TRANSLATE("Writing to the file failed:\n" 1742 "%s\n\n" 1743 "All changes will be lost when you quit."), 1744 strerror(status)); 1745 1746 BAlert* alert = new BAlert(B_TRANSLATE("DiskProbe request"), 1747 buffer, B_TRANSLATE("OK"), NULL, NULL, 1748 B_WIDTH_AS_USUAL, B_WARNING_ALERT); 1749 alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE); 1750 alert->Go(NULL); 1751 1752 return status; 1753 } 1754 1755 1756 bool 1757 ProbeView::QuitRequested() 1758 { 1759 fEditorLooper->QuitFind(); 1760 1761 if (!fEditor.IsModified()) 1762 return true; 1763 1764 BAlert* alert = new BAlert(B_TRANSLATE("DiskProbe request"), 1765 B_TRANSLATE("Save changes before closing?"), B_TRANSLATE("Cancel"), 1766 B_TRANSLATE("Don't save"), B_TRANSLATE("Save"), B_WIDTH_AS_USUAL, 1767 B_OFFSET_SPACING, B_WARNING_ALERT); 1768 alert->SetShortcut(0, B_ESCAPE); 1769 alert->SetShortcut(1, 'd'); 1770 alert->SetShortcut(2, 's'); 1771 int32 chosen = alert->Go(); 1772 1773 if (chosen == 0) 1774 return false; 1775 if (chosen == 1) 1776 return true; 1777 1778 return _Save() == B_OK; 1779 } 1780 1781 1782 void 1783 ProbeView::MessageReceived(BMessage* message) 1784 { 1785 switch (message->what) { 1786 case B_SAVE_REQUESTED: 1787 _Save(); 1788 break; 1789 1790 case B_OBSERVER_NOTICE_CHANGE: { 1791 int32 what; 1792 if (message->FindInt32(B_OBSERVE_WHAT_CHANGE, &what) != B_OK) 1793 break; 1794 1795 switch (what) { 1796 case kDataViewSelection: 1797 { 1798 int64 start, end; 1799 if (message->FindInt64("start", &start) == B_OK 1800 && message->FindInt64("end", &end) == B_OK) 1801 _UpdateSelectionMenuItems(start, end); 1802 break; 1803 } 1804 } 1805 break; 1806 } 1807 1808 case kMsgBaseType: 1809 { 1810 int32 type; 1811 if (message->FindInt32("base_type", &type) != B_OK) 1812 break; 1813 1814 fHeaderView->SetBase((base_type)type); 1815 fDataView->SetBase((base_type)type); 1816 1817 // The selection menu items depend on the base type as well 1818 int32 start, end; 1819 fDataView->GetSelection(start, end); 1820 _UpdateSelectionMenuItems(start, end); 1821 1822 _UpdateBookmarkMenuItems(); 1823 1824 // update the application's settings 1825 BMessage update(*message); 1826 update.what = kMsgSettingsChanged; 1827 be_app_messenger.SendMessage(&update); 1828 break; 1829 } 1830 1831 case kMsgFontSize: 1832 { 1833 float size; 1834 if (message->FindFloat("font_size", &size) != B_OK) 1835 break; 1836 1837 fDataView->SetFontSize(size); 1838 1839 // update the application's settings 1840 BMessage update(*message); 1841 update.what = kMsgSettingsChanged; 1842 be_app_messenger.SendMessage(&update); 1843 break; 1844 } 1845 1846 case kMsgBlockSize: 1847 { 1848 int32 blockSize; 1849 if (message->FindInt32("block_size", &blockSize) != B_OK) 1850 break; 1851 1852 BAutolock locker(fEditor); 1853 1854 if (fEditor.SetViewSize(blockSize) == B_OK 1855 && fEditor.SetBlockSize(blockSize) == B_OK) 1856 fHeaderView->SetTo(fEditor.ViewOffset(), blockSize); 1857 break; 1858 } 1859 1860 case kMsgViewAs: 1861 { 1862 int32 index; 1863 if (message->FindInt32("editor index", &index) != B_OK) 1864 index = -1; 1865 1866 _SetTypeEditor(index); 1867 break; 1868 } 1869 1870 case kMsgAddBookmark: 1871 _AddBookmark(fHeaderView->Position()); 1872 break; 1873 1874 case kMsgPrint: 1875 _Print(); 1876 break; 1877 1878 case kMsgPageSetup: 1879 _PageSetup(); 1880 break; 1881 1882 case kMsgOpenFindWindow: 1883 { 1884 fEditorLooper->QuitFind(); 1885 1886 // set this view as the current find panel's target 1887 BMessage find(*fFindAgainMenuItem->Message()); 1888 find.what = kMsgOpenFindWindow; 1889 find.AddMessenger("target", this); 1890 be_app_messenger.SendMessage(&find); 1891 break; 1892 } 1893 1894 case kMsgFind: 1895 { 1896 const uint8* data; 1897 ssize_t size; 1898 if (message->FindData("data", B_RAW_TYPE, (const void**)&data, 1899 &size) != B_OK) { 1900 // search again for last pattern 1901 BMessage* itemMessage = fFindAgainMenuItem->Message(); 1902 if (itemMessage == NULL || itemMessage->FindData("data", 1903 B_RAW_TYPE, (const void**)&data, &size) != B_OK) { 1904 // this shouldn't ever happen, but well... 1905 beep(); 1906 break; 1907 } 1908 } else { 1909 // remember the search pattern 1910 fFindAgainMenuItem->SetMessage(new BMessage(*message)); 1911 fFindAgainMenuItem->SetEnabled(true); 1912 } 1913 1914 int32 start, end; 1915 fDataView->GetSelection(start, end); 1916 1917 BMessage find(*message); 1918 find.AddInt64("start", fHeaderView->Position() + start + 1); 1919 find.AddMessenger("progress_monitor", BMessenger(fHeaderView)); 1920 fEditorLooper->PostMessage(&find); 1921 break; 1922 } 1923 1924 case kMsgStopFind: 1925 fEditorLooper->QuitFind(); 1926 break; 1927 1928 case B_NODE_MONITOR: 1929 { 1930 switch (message->FindInt32("opcode")) { 1931 case B_STAT_CHANGED: 1932 fEditor.ForceUpdate(); 1933 break; 1934 case B_ATTR_CHANGED: 1935 { 1936 const char* name; 1937 if (message->FindString("attr", &name) != B_OK) 1938 break; 1939 1940 if (fEditor.IsAttribute()) { 1941 if (!strcmp(name, fEditor.Attribute())) 1942 fEditor.ForceUpdate(); 1943 } else { 1944 BMenuBar* bar = Window()->KeyMenuBar(); 1945 if (bar != NULL) { 1946 BMenuItem* item = bar->FindItem("Attributes"); 1947 if (item != NULL && item->Submenu() != NULL) 1948 _UpdateAttributesMenu(item->Submenu()); 1949 } 1950 } 1951 1952 // There might be a new icon 1953 if (!strcmp(name, "BEOS:TYPE") 1954 || !strcmp(name, "BEOS:M:STD_ICON") 1955 || !strcmp(name, "BEOS:L:STD_ICON") 1956 || !strcmp(name, "BEOS:ICON")) 1957 fHeaderView->UpdateIcon(); 1958 break; 1959 } 1960 } 1961 break; 1962 } 1963 1964 case B_CLIPBOARD_CHANGED: 1965 _CheckClipboard(); 1966 break; 1967 1968 case kMsgDataEditorStateChange: 1969 { 1970 bool enabled; 1971 if (message->FindBool("can_undo", &enabled) == B_OK) 1972 fUndoMenuItem->SetEnabled(enabled); 1973 1974 if (message->FindBool("can_redo", &enabled) == B_OK) 1975 fRedoMenuItem->SetEnabled(enabled); 1976 1977 if (message->FindBool("modified", &enabled) == B_OK) 1978 fSaveMenuItem->SetEnabled(enabled); 1979 break; 1980 } 1981 1982 default: 1983 BView::MessageReceived(message); 1984 } 1985 } 1986 1987