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