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