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