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