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