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