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