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