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