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