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