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_RGBA32); 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 1140 BPoint point = fTypeEditorView->Frame().LeftTop(); 1141 if ((fTypeEditorView->ResizingMode() & B_FOLLOW_RIGHT) == 0) 1142 point.x = (rect.Width() - fTypeEditorView->Bounds().Width()) / 2; 1143 if ((fTypeEditorView->ResizingMode() & B_FOLLOW_BOTTOM) == 0) 1144 point.y = (rect.Height() - fTypeEditorView->Bounds().Height()) / 2; 1145 1146 fTypeEditorView->MoveTo(point); 1147 } 1148 1149 1150 // #pragma mark - ProbeView 1151 1152 1153 ProbeView::ProbeView(BRect rect, entry_ref *ref, const char *attribute, 1154 const BMessage *settings) 1155 : BView(rect, "probeView", B_FOLLOW_ALL, B_WILL_DRAW), 1156 fPrintSettings(NULL), 1157 fTypeView(NULL), 1158 fLastSearch(NULL) 1159 { 1160 fEditor.SetTo(*ref, attribute); 1161 1162 int32 baseType = kHexBase; 1163 float fontSize = 12.0f; 1164 if (settings != NULL) { 1165 settings->FindInt32("base_type", &baseType); 1166 settings->FindFloat("font_size", &fontSize); 1167 } 1168 1169 rect = Bounds(); 1170 fHeaderView = new HeaderView(rect, &fEditor.Ref(), fEditor); 1171 fHeaderView->ResizeToPreferred(); 1172 fHeaderView->SetBase((base_type)baseType); 1173 AddChild(fHeaderView); 1174 1175 rect = fHeaderView->Frame(); 1176 rect.top = rect.bottom + 3; 1177 rect.bottom = Bounds().bottom - B_H_SCROLL_BAR_HEIGHT; 1178 rect.right -= B_V_SCROLL_BAR_WIDTH; 1179 fDataView = new DataView(rect, fEditor); 1180 fDataView->SetBase((base_type)baseType); 1181 fDataView->SetFontSize(fontSize); 1182 1183 fScrollView = new BScrollView("scroller", fDataView, B_FOLLOW_ALL, 1184 B_WILL_DRAW, true, true); 1185 AddChild(fScrollView); 1186 1187 fDataView->UpdateScroller(); 1188 } 1189 1190 1191 ProbeView::~ProbeView() 1192 { 1193 } 1194 1195 1196 void 1197 ProbeView::UpdateSizeLimits() 1198 { 1199 if (Window() == NULL) 1200 return; 1201 1202 if (!fDataView->FontSizeFitsBounds()) { 1203 float width, height; 1204 fDataView->GetPreferredSize(&width, &height); 1205 1206 BRect frame = Window()->ConvertFromScreen(ConvertToScreen( 1207 fHeaderView->Frame())); 1208 1209 Window()->SetSizeLimits(250, width + B_V_SCROLL_BAR_WIDTH, 1210 200, height + frame.bottom + 4 + B_H_SCROLL_BAR_HEIGHT); 1211 } else 1212 Window()->SetSizeLimits(250, 32768, 200, 32768); 1213 1214 #ifdef HAIKU_TARGET_PLATFORM_BEOS 1215 // In Haiku and Dano, the window is resized automatically 1216 BRect bounds = Window()->Bounds(); 1217 float minWidth, maxWidth, minHeight, maxHeight; 1218 Window()->GetSizeLimits(&minWidth, &maxWidth, &minHeight, &maxHeight); 1219 if (maxWidth < bounds.Width() || maxHeight < bounds.Height()) { 1220 Window()->ResizeTo(MIN(maxWidth, bounds.Width()), MIN(maxHeight, 1221 bounds.Height())); 1222 } 1223 #endif 1224 } 1225 1226 1227 void 1228 ProbeView::DetachedFromWindow() 1229 { 1230 fEditorLooper->QuitFind(); 1231 1232 if (fEditorLooper->Lock()) 1233 fEditorLooper->Quit(); 1234 fEditorLooper = NULL; 1235 1236 fEditor.StopWatching(this); 1237 fDataView->StopWatching(fHeaderView, kDataViewCursorPosition); 1238 fDataView->StopWatching(this, kDataViewSelection); 1239 fDataView->StopWatching(this, kDataViewPreferredSize); 1240 be_clipboard->StopWatching(this); 1241 } 1242 1243 1244 void 1245 ProbeView::_UpdateAttributesMenu(BMenu *menu) 1246 { 1247 // remove old contents 1248 1249 for (int32 i = menu->CountItems(); i-- > 0;) { 1250 delete menu->RemoveItem(i); 1251 } 1252 1253 // add new items (sorted) 1254 1255 BNode node(&fEditor.AttributeRef()); 1256 if (node.InitCheck() == B_OK) { 1257 char attribute[B_ATTR_NAME_LENGTH]; 1258 node.RewindAttrs(); 1259 1260 while (node.GetNextAttrName(attribute) == B_OK) { 1261 attr_info info; 1262 if (node.GetAttrInfo(attribute, &info) != B_OK) 1263 continue; 1264 1265 char type[16]; 1266 type[0] = '['; 1267 get_type_string(type + 1, sizeof(type) - 2, info.type); 1268 strcat(type, "]"); 1269 1270 // find where to insert 1271 int32 i; 1272 for (i = 0; i < menu->CountItems(); i++) { 1273 if (strcasecmp(menu->ItemAt(i)->Label(), attribute) > 0) 1274 break; 1275 } 1276 1277 BMessage *message = new BMessage(B_REFS_RECEIVED); 1278 message->AddRef("refs", &fEditor.AttributeRef()); 1279 message->AddString("attributes", attribute); 1280 1281 menu->AddItem(new TypeMenuItem(attribute, type, message), i); 1282 } 1283 } 1284 1285 if (menu->CountItems() == 0) { 1286 // if there are no attributes, add an item to the menu 1287 // that says so 1288 BMenuItem *item = new BMenuItem("none", NULL); 1289 item->SetEnabled(false); 1290 menu->AddItem(item); 1291 } 1292 1293 menu->SetTargetForItems(be_app); 1294 } 1295 1296 1297 void 1298 ProbeView::AddSaveMenuItems(BMenu* menu, int32 index) 1299 { 1300 menu->AddItem(fSaveMenuItem = new BMenuItem("Save", 1301 new BMessage(B_SAVE_REQUESTED), 'S'), index); 1302 fSaveMenuItem->SetTarget(this); 1303 fSaveMenuItem->SetEnabled(false); 1304 //menu->AddItem(new BMenuItem("Save As" B_UTF8_ELLIPSIS, NULL), index); 1305 } 1306 1307 1308 void 1309 ProbeView::AddPrintMenuItems(BMenu* menu, int32 index) 1310 { 1311 BMenuItem *item; 1312 menu->AddItem(item = new BMenuItem("Page Setup" B_UTF8_ELLIPSIS, 1313 new BMessage(kMsgPageSetup)), index++); 1314 item->SetTarget(this); 1315 menu->AddItem(item = new BMenuItem("Print" B_UTF8_ELLIPSIS, 1316 new BMessage(kMsgPrint), 'P'), index++); 1317 item->SetTarget(this); 1318 } 1319 1320 1321 void 1322 ProbeView::AddViewAsMenuItems() 1323 { 1324 #if 0 1325 BMenuBar* bar = Window()->KeyMenuBar(); 1326 if (bar == NULL) 1327 return; 1328 1329 BMenuItem* item = bar->FindItem("View"); 1330 BMenu* menu = NULL; 1331 if (item != NULL) 1332 menu = item->Submenu(); 1333 else 1334 menu = bar->SubmenuAt(bar->CountItems() - 1); 1335 1336 if (menu == NULL) 1337 return; 1338 1339 menu->AddSeparatorItem(); 1340 1341 BMenu* subMenu = new BMenu("View As"); 1342 subMenu->SetRadioMode(true); 1343 1344 BMessage* message = new BMessage(kMsgViewAs); 1345 subMenu->AddItem(item = new BMenuItem("Raw", message)); 1346 item->SetMarked(true); 1347 1348 const char* name; 1349 for (int32 i = 0; GetNthTypeEditor(i, &name) == B_OK; i++) { 1350 message = new BMessage(kMsgViewAs); 1351 message->AddInt32("editor index", i); 1352 subMenu->AddItem(new BMenuItem(name, message)); 1353 } 1354 1355 subMenu->SetTargetForItems(this); 1356 menu->AddItem(new BMenuItem(subMenu)); 1357 #endif 1358 } 1359 1360 1361 void 1362 ProbeView::AttachedToWindow() 1363 { 1364 fEditorLooper = new EditorLooper(fEditor.Ref().name, fEditor, 1365 BMessenger(fDataView)); 1366 fEditorLooper->Run(); 1367 1368 fEditor.StartWatching(this); 1369 fDataView->StartWatching(fHeaderView, kDataViewCursorPosition); 1370 fDataView->StartWatching(this, kDataViewSelection); 1371 fDataView->StartWatching(this, kDataViewPreferredSize); 1372 be_clipboard->StartWatching(this); 1373 1374 // Add menu to window 1375 1376 BMenuBar *bar = Window()->KeyMenuBar(); 1377 if (bar == NULL) { 1378 // there is none? Well, but we really want to have one 1379 bar = new BMenuBar(BRect(0, 0, 0, 0), NULL); 1380 Window()->AddChild(bar); 1381 1382 MoveBy(0, bar->Bounds().Height()); 1383 ResizeBy(0, -bar->Bounds().Height()); 1384 1385 BMenu *menu = new BMenu(fEditor.IsAttribute() 1386 ? "Attribute" : fEditor.IsDevice() ? "Device" : "File"); 1387 AddSaveMenuItems(menu, 0); 1388 menu->AddSeparatorItem(); 1389 AddPrintMenuItems(menu, menu->CountItems()); 1390 menu->AddSeparatorItem(); 1391 1392 menu->AddItem(new BMenuItem("Close", new BMessage(B_CLOSE_REQUESTED), 1393 'W')); 1394 bar->AddItem(menu); 1395 } 1396 1397 // "Edit" menu 1398 1399 BMenu *menu = new BMenu("Edit"); 1400 BMenuItem *item; 1401 menu->AddItem(fUndoMenuItem = new BMenuItem("Undo", new BMessage(B_UNDO), 1402 'Z')); 1403 fUndoMenuItem->SetEnabled(fEditor.CanUndo()); 1404 fUndoMenuItem->SetTarget(fDataView); 1405 menu->AddItem(fRedoMenuItem = new BMenuItem("Redo", new BMessage(B_REDO), 1406 'Z', B_SHIFT_KEY)); 1407 fRedoMenuItem->SetEnabled(fEditor.CanRedo()); 1408 fRedoMenuItem->SetTarget(fDataView); 1409 menu->AddSeparatorItem(); 1410 menu->AddItem(item = new BMenuItem("Copy", new BMessage(B_COPY), 'C')); 1411 item->SetTarget(NULL, Window()); 1412 menu->AddItem(fPasteMenuItem = new BMenuItem("Paste", new BMessage(B_PASTE), 1413 'V')); 1414 fPasteMenuItem->SetTarget(NULL, Window()); 1415 _CheckClipboard(); 1416 menu->AddItem(item = new BMenuItem("Select All", new BMessage(B_SELECT_ALL), 1417 'A')); 1418 item->SetTarget(NULL, Window()); 1419 menu->AddSeparatorItem(); 1420 menu->AddItem(item = new BMenuItem("Find" B_UTF8_ELLIPSIS, 1421 new BMessage(kMsgOpenFindWindow), 'F')); 1422 item->SetTarget(this); 1423 menu->AddItem(fFindAgainMenuItem = new BMenuItem("Find Again", 1424 new BMessage(kMsgFind), 'G')); 1425 fFindAgainMenuItem->SetEnabled(false); 1426 fFindAgainMenuItem->SetTarget(this); 1427 bar->AddItem(menu); 1428 1429 // "Block" menu 1430 1431 menu = new BMenu("Block"); 1432 BMessage *message = new BMessage(kMsgPositionUpdate); 1433 message->AddInt32("delta", 1); 1434 menu->AddItem(item = new BMenuItem("Next", message, B_RIGHT_ARROW)); 1435 item->SetTarget(fHeaderView); 1436 message = new BMessage(kMsgPositionUpdate); 1437 message->AddInt32("delta", -1); 1438 menu->AddItem(item = new BMenuItem("Previous", message, B_LEFT_ARROW)); 1439 item->SetTarget(fHeaderView); 1440 menu->AddItem(item = new BMenuItem("Back", new BMessage(kMsgLastPosition), 1441 'J')); 1442 item->SetTarget(fHeaderView); 1443 1444 BMenu *subMenu = new BMenu("Selection"); 1445 message = new BMessage(kMsgPositionUpdate); 1446 message->AddInt64("block", 0); 1447 subMenu->AddItem(fNativeMenuItem = new BMenuItem("", message, 'K')); 1448 fNativeMenuItem->SetTarget(fHeaderView); 1449 message = new BMessage(*message); 1450 subMenu->AddItem(fSwappedMenuItem = new BMenuItem("", message, 'L')); 1451 fSwappedMenuItem->SetTarget(fHeaderView); 1452 menu->AddItem(new BMenuItem(subMenu)); 1453 _UpdateSelectionMenuItems(0, 0); 1454 menu->AddSeparatorItem(); 1455 1456 fBookmarkMenu = new BMenu("Bookmarks"); 1457 fBookmarkMenu->AddItem(item = new BMenuItem("Add", 1458 new BMessage(kMsgAddBookmark), 'B')); 1459 item->SetTarget(this); 1460 menu->AddItem(new BMenuItem(fBookmarkMenu)); 1461 bar->AddItem(menu); 1462 1463 // "Attributes" menu (it's only visible if the underlying 1464 // file system actually supports attributes) 1465 1466 BDirectory directory; 1467 BVolume volume; 1468 if (directory.SetTo(&fEditor.AttributeRef()) == B_OK 1469 && directory.IsRootDirectory()) 1470 directory.GetVolume(&volume); 1471 else 1472 fEditor.File().GetVolume(&volume); 1473 1474 if (!fEditor.IsAttribute() && volume.InitCheck() == B_OK 1475 && (volume.KnowsMime() || volume.KnowsAttr())) { 1476 bar->AddItem(menu = new BMenu("Attributes")); 1477 _UpdateAttributesMenu(menu); 1478 } 1479 1480 // "View" menu 1481 1482 menu = new BMenu("View"); 1483 1484 // Number Base (hex/decimal) 1485 1486 subMenu = new BMenu("Base"); 1487 message = new BMessage(kMsgBaseType); 1488 message->AddInt32("base_type", kDecimalBase); 1489 subMenu->AddItem(item = new BMenuItem("Decimal", message, 'D')); 1490 item->SetTarget(this); 1491 if (fHeaderView->Base() == kDecimalBase) 1492 item->SetMarked(true); 1493 1494 message = new BMessage(kMsgBaseType); 1495 message->AddInt32("base_type", kHexBase); 1496 subMenu->AddItem(item = new BMenuItem("Hex", message, 'H')); 1497 item->SetTarget(this); 1498 if (fHeaderView->Base() == kHexBase) 1499 item->SetMarked(true); 1500 1501 subMenu->SetRadioMode(true); 1502 menu->AddItem(new BMenuItem(subMenu)); 1503 1504 // Block Size 1505 1506 subMenu = new BMenu("BlockSize"); 1507 subMenu->SetRadioMode(true); 1508 const uint32 blockSizes[] = {512, 1024, 2048}; 1509 for (uint32 i = 0; i < sizeof(blockSizes) / sizeof(blockSizes[0]); i++) { 1510 char buffer[32]; 1511 snprintf(buffer, sizeof(buffer), "%ld%s", blockSizes[i], 1512 fEditor.IsDevice() && fEditor.BlockSize() == blockSizes[i] 1513 ? " (native)" : ""); 1514 subMenu->AddItem(item = new BMenuItem(buffer, 1515 message = new BMessage(kMsgBlockSize))); 1516 message->AddInt32("block_size", blockSizes[i]); 1517 if (fEditor.BlockSize() == blockSizes[i]) 1518 item->SetMarked(true); 1519 } 1520 if (subMenu->FindMarked() == NULL) { 1521 // if the device has some weird block size, we'll add it here, too 1522 char buffer[32]; 1523 snprintf(buffer, sizeof(buffer), "%ld (native)", fEditor.BlockSize()); 1524 subMenu->AddItem(item = new BMenuItem(buffer, 1525 message = new BMessage(kMsgBlockSize))); 1526 message->AddInt32("block_size", fEditor.BlockSize()); 1527 item->SetMarked(true); 1528 } 1529 subMenu->SetTargetForItems(this); 1530 menu->AddItem(new BMenuItem(subMenu)); 1531 menu->AddSeparatorItem(); 1532 1533 // Font Size 1534 1535 subMenu = new BMenu("Font Size"); 1536 subMenu->SetRadioMode(true); 1537 const int32 fontSizes[] = {9, 10, 11, 12, 13, 14, 18, 24, 36, 48}; 1538 int32 fontSize = int32(fDataView->FontSize() + 0.5); 1539 if (fDataView->FontSizeFitsBounds()) 1540 fontSize = 0; 1541 for (uint32 i = 0; i < sizeof(fontSizes) / sizeof(fontSizes[0]); i++) { 1542 char buffer[16]; 1543 snprintf(buffer, sizeof(buffer), "%ld", fontSizes[i]); 1544 subMenu->AddItem(item = new BMenuItem(buffer, 1545 message = new BMessage(kMsgFontSize))); 1546 message->AddFloat("font_size", fontSizes[i]); 1547 if (fontSizes[i] == fontSize) 1548 item->SetMarked(true); 1549 } 1550 subMenu->AddSeparatorItem(); 1551 subMenu->AddItem(item = new BMenuItem("Fit", 1552 message = new BMessage(kMsgFontSize))); 1553 message->AddFloat("font_size", 0.0f); 1554 if (fontSize == 0) 1555 item->SetMarked(true); 1556 1557 subMenu->SetTargetForItems(this); 1558 menu->AddItem(new BMenuItem(subMenu)); 1559 1560 bar->AddItem(menu); 1561 } 1562 1563 1564 void 1565 ProbeView::AllAttached() 1566 { 1567 fHeaderView->SetTarget(fEditorLooper); 1568 } 1569 1570 1571 void 1572 ProbeView::WindowActivated(bool active) 1573 { 1574 if (!active) 1575 return; 1576 1577 fDataView->MakeFocus(true); 1578 1579 // set this view as the current find panel's target 1580 BMessage target(kMsgFindTarget); 1581 target.AddMessenger("target", this); 1582 be_app_messenger.SendMessage(&target); 1583 } 1584 1585 1586 void 1587 ProbeView::_UpdateSelectionMenuItems(int64 start, int64 end) 1588 { 1589 int64 position = 0; 1590 const uint8 *data = fDataView->DataAt(start); 1591 if (data == NULL) { 1592 fNativeMenuItem->SetEnabled(false); 1593 fSwappedMenuItem->SetEnabled(false); 1594 return; 1595 } 1596 1597 // retrieve native endian position 1598 1599 int size; 1600 if (end < start + 8) 1601 size = end + 1 - start; 1602 else 1603 size = 8; 1604 1605 int64 bigEndianPosition = 0; 1606 memcpy(&bigEndianPosition, data, size); 1607 1608 position = B_BENDIAN_TO_HOST_INT64(bigEndianPosition) >> (8 * (8 - size)); 1609 1610 // update menu items 1611 1612 char buffer[128]; 1613 if (fDataView->Base() == kHexBase) 1614 snprintf(buffer, sizeof(buffer), "Native: 0x%0*Lx", size * 2, position); 1615 else 1616 snprintf(buffer, sizeof(buffer), "Native: %Ld (0x%0*Lx)", position, size * 2, position); 1617 1618 fNativeMenuItem->SetLabel(buffer); 1619 fNativeMenuItem->SetEnabled(position >= 0 && (position * fEditor.BlockSize()) < fEditor.FileSize()); 1620 fNativeMenuItem->Message()->ReplaceInt64("block", position); 1621 1622 position = B_SWAP_INT64(position) >> (8 * (8 - size)); 1623 if (fDataView->Base() == kHexBase) 1624 snprintf(buffer, sizeof(buffer), "Swapped: 0x%0*Lx", size * 2, position); 1625 else 1626 snprintf(buffer, sizeof(buffer), "Swapped: %Ld (0x%0*Lx)", position, size * 2, position); 1627 1628 fSwappedMenuItem->SetLabel(buffer); 1629 fSwappedMenuItem->SetEnabled(position >= 0 && (position * fEditor.BlockSize()) < fEditor.FileSize()); 1630 fSwappedMenuItem->Message()->ReplaceInt64("block", position); 1631 } 1632 1633 1634 void 1635 ProbeView::_UpdateBookmarkMenuItems() 1636 { 1637 for (int32 i = 2; i < fBookmarkMenu->CountItems(); i++) { 1638 BMenuItem *item = fBookmarkMenu->ItemAt(i); 1639 if (item == NULL) 1640 break; 1641 1642 BMessage *message = item->Message(); 1643 if (message == NULL) 1644 break; 1645 1646 off_t block = message->FindInt64("block"); 1647 1648 char buffer[128]; 1649 if (fDataView->Base() == kHexBase) 1650 snprintf(buffer, sizeof(buffer), "Block 0x%Lx", block); 1651 else 1652 snprintf(buffer, sizeof(buffer), "Block %Ld (0x%Lx)", block, block); 1653 1654 item->SetLabel(buffer); 1655 } 1656 } 1657 1658 1659 void 1660 ProbeView::_AddBookmark(off_t position) 1661 { 1662 int32 count = fBookmarkMenu->CountItems(); 1663 1664 if (count == 1) { 1665 fBookmarkMenu->AddSeparatorItem(); 1666 count++; 1667 } 1668 1669 // insert current position as bookmark 1670 1671 off_t block = position / fEditor.BlockSize(); 1672 1673 off_t bookmark = -1; 1674 BMenuItem *item; 1675 int32 i; 1676 for (i = 2; (item = fBookmarkMenu->ItemAt(i)) != NULL; i++) { 1677 BMessage *message = item->Message(); 1678 if (message != NULL && message->FindInt64("block", &bookmark) == B_OK) { 1679 if (block <= bookmark) 1680 break; 1681 } 1682 } 1683 1684 // the bookmark already exists 1685 if (block == bookmark) 1686 return; 1687 1688 char buffer[128]; 1689 if (fDataView->Base() == kHexBase) 1690 snprintf(buffer, sizeof(buffer), "Block 0x%Lx", block); 1691 else 1692 snprintf(buffer, sizeof(buffer), "Block %Ld (0x%Lx)", block, block); 1693 1694 BMessage *message; 1695 item = new BMenuItem(buffer, message = new BMessage(kMsgPositionUpdate)); 1696 item->SetTarget(fHeaderView); 1697 if (count < 12) 1698 item->SetShortcut('0' + count - 2, B_COMMAND_KEY); 1699 message->AddInt64("block", block); 1700 1701 fBookmarkMenu->AddItem(item, i); 1702 } 1703 1704 1705 void 1706 ProbeView::_RemoveTypeEditor() 1707 { 1708 if (fTypeView == NULL) 1709 return; 1710 1711 if (Parent() != NULL) 1712 Parent()->RemoveChild(fTypeView); 1713 else 1714 Window()->RemoveChild(fTypeView); 1715 1716 delete fTypeView; 1717 fTypeView = NULL; 1718 } 1719 1720 1721 void 1722 ProbeView::_SetTypeEditor(int32 index) 1723 { 1724 if (index == -1) { 1725 // remove type editor, show raw editor 1726 if (IsHidden()) 1727 Show(); 1728 1729 _RemoveTypeEditor(); 1730 } else { 1731 // hide raw editor, create and show type editor 1732 if (!IsHidden()) 1733 Hide(); 1734 1735 _RemoveTypeEditor(); 1736 1737 fTypeView = new TypeView(Frame(), "type shell", index, fEditor, 1738 B_FOLLOW_ALL); 1739 1740 if (Parent() != NULL) 1741 Parent()->AddChild(fTypeView); 1742 else 1743 Window()->AddChild(fTypeView); 1744 } 1745 } 1746 1747 1748 void 1749 ProbeView::_CheckClipboard() 1750 { 1751 if (!be_clipboard->Lock()) 1752 return; 1753 1754 bool hasData = false; 1755 BMessage *clip; 1756 if ((clip = be_clipboard->Data()) != NULL) { 1757 const void *data; 1758 ssize_t size; 1759 if (clip->FindData(B_FILE_MIME_TYPE, B_MIME_TYPE, &data, &size) == B_OK 1760 || clip->FindData("text/plain", B_MIME_TYPE, &data, &size) == B_OK) 1761 hasData = true; 1762 } 1763 1764 be_clipboard->Unlock(); 1765 1766 fPasteMenuItem->SetEnabled(hasData); 1767 } 1768 1769 1770 status_t 1771 ProbeView::_PageSetup() 1772 { 1773 BPrintJob printJob(Window()->Title()); 1774 if (fPrintSettings != NULL) 1775 printJob.SetSettings(new BMessage(*fPrintSettings)); 1776 1777 status_t status = printJob.ConfigPage(); 1778 if (status == B_OK) { 1779 // replace the print settings on success 1780 delete fPrintSettings; 1781 fPrintSettings = printJob.Settings(); 1782 } 1783 1784 return status; 1785 } 1786 1787 1788 void 1789 ProbeView::_Print() 1790 { 1791 if (fPrintSettings == NULL && _PageSetup() != B_OK) 1792 return; 1793 1794 BPrintJob printJob(Window()->Title()); 1795 printJob.SetSettings(new BMessage(*fPrintSettings)); 1796 1797 if (printJob.ConfigJob() == B_OK) { 1798 BRect rect = printJob.PrintableRect(); 1799 1800 float width, height; 1801 fDataView->GetPreferredSize(&width, &height); 1802 1803 printJob.BeginJob(); 1804 1805 fDataView->SetScale(rect.Width() / width); 1806 printJob.DrawView(fDataView, rect, rect.LeftTop()); 1807 fDataView->SetScale(1.0); 1808 printJob.SpoolPage(); 1809 1810 printJob.CommitJob(); 1811 } 1812 } 1813 1814 1815 status_t 1816 ProbeView::_Save() 1817 { 1818 status_t status = fEditor.Save(); 1819 if (status == B_OK) 1820 return B_OK; 1821 1822 char buffer[1024]; 1823 snprintf(buffer, sizeof(buffer), 1824 "Writing to the file failed:\n" 1825 "%s\n\n" 1826 "All changes will be lost when you quit.", 1827 strerror(status)); 1828 1829 (new BAlert("DiskProbe request", 1830 buffer, "Ok", NULL, NULL, 1831 B_WIDTH_AS_USUAL, B_WARNING_ALERT))->Go(NULL); 1832 1833 return status; 1834 } 1835 1836 1837 bool 1838 ProbeView::QuitRequested() 1839 { 1840 fEditorLooper->QuitFind(); 1841 1842 if (!fEditor.IsModified()) 1843 return true; 1844 1845 int32 chosen = (new BAlert("DiskProbe request", 1846 "Save changes before closing?", "Don't Save", "Cancel", "Save", 1847 B_WIDTH_AS_USUAL, B_WARNING_ALERT))->Go(); 1848 1849 if (chosen == 0) 1850 return true; 1851 if (chosen == 1) 1852 return false; 1853 1854 return _Save() == B_OK; 1855 } 1856 1857 1858 void 1859 ProbeView::MessageReceived(BMessage *message) 1860 { 1861 switch (message->what) { 1862 case B_SAVE_REQUESTED: 1863 _Save(); 1864 break; 1865 1866 case B_OBSERVER_NOTICE_CHANGE: { 1867 int32 what; 1868 if (message->FindInt32(B_OBSERVE_WHAT_CHANGE, &what) != B_OK) 1869 break; 1870 1871 switch (what) { 1872 case kDataViewSelection: 1873 { 1874 int64 start, end; 1875 if (message->FindInt64("start", &start) == B_OK 1876 && message->FindInt64("end", &end) == B_OK) 1877 _UpdateSelectionMenuItems(start, end); 1878 break; 1879 } 1880 case kDataViewPreferredSize: 1881 UpdateSizeLimits(); 1882 break; 1883 } 1884 break; 1885 } 1886 1887 case kMsgBaseType: 1888 { 1889 int32 type; 1890 if (message->FindInt32("base_type", &type) != B_OK) 1891 break; 1892 1893 fHeaderView->SetBase((base_type)type); 1894 fDataView->SetBase((base_type)type); 1895 1896 // The selection menu items depend on the base type as well 1897 int32 start, end; 1898 fDataView->GetSelection(start, end); 1899 _UpdateSelectionMenuItems(start, end); 1900 1901 _UpdateBookmarkMenuItems(); 1902 1903 // update the application's settings 1904 BMessage update(*message); 1905 update.what = kMsgSettingsChanged; 1906 be_app_messenger.SendMessage(&update); 1907 break; 1908 } 1909 1910 case kMsgFontSize: 1911 { 1912 float size; 1913 if (message->FindFloat("font_size", &size) != B_OK) 1914 break; 1915 1916 fDataView->SetFontSize(size); 1917 1918 // update the application's settings 1919 BMessage update(*message); 1920 update.what = kMsgSettingsChanged; 1921 be_app_messenger.SendMessage(&update); 1922 break; 1923 } 1924 1925 case kMsgBlockSize: 1926 { 1927 int32 blockSize; 1928 if (message->FindInt32("block_size", &blockSize) != B_OK) 1929 break; 1930 1931 BAutolock locker(fEditor); 1932 1933 if (fEditor.SetViewSize(blockSize) == B_OK 1934 && fEditor.SetBlockSize(blockSize) == B_OK) 1935 fHeaderView->SetTo(fEditor.ViewOffset(), blockSize); 1936 break; 1937 } 1938 1939 case kMsgViewAs: 1940 { 1941 int32 index; 1942 if (message->FindInt32("editor index", &index) != B_OK) 1943 index = -1; 1944 1945 _SetTypeEditor(index); 1946 break; 1947 } 1948 1949 case kMsgAddBookmark: 1950 _AddBookmark(fHeaderView->Position()); 1951 break; 1952 1953 case kMsgPrint: 1954 _Print(); 1955 break; 1956 1957 case kMsgPageSetup: 1958 _PageSetup(); 1959 break; 1960 1961 case kMsgOpenFindWindow: 1962 { 1963 fEditorLooper->QuitFind(); 1964 1965 // set this view as the current find panel's target 1966 BMessage find(*fFindAgainMenuItem->Message()); 1967 find.what = kMsgOpenFindWindow; 1968 find.AddMessenger("target", this); 1969 be_app_messenger.SendMessage(&find); 1970 break; 1971 } 1972 1973 case kMsgFind: 1974 { 1975 const uint8 *data; 1976 ssize_t size; 1977 if (message->FindData("data", B_RAW_TYPE, (const void **)&data, &size) != B_OK) { 1978 // search again for last pattern 1979 BMessage *itemMessage = fFindAgainMenuItem->Message(); 1980 if (itemMessage == NULL 1981 || itemMessage->FindData("data", B_RAW_TYPE, (const void **)&data, &size) != B_OK) { 1982 // this shouldn't ever happen, but well... 1983 beep(); 1984 break; 1985 } 1986 } else { 1987 // remember the search pattern 1988 fFindAgainMenuItem->SetMessage(new BMessage(*message)); 1989 fFindAgainMenuItem->SetEnabled(true); 1990 } 1991 1992 int32 start, end; 1993 fDataView->GetSelection(start, end); 1994 1995 BMessage find(*message); 1996 find.AddInt64("start", fHeaderView->Position() + start + 1); 1997 find.AddMessenger("progress_monitor", BMessenger(fHeaderView)); 1998 fEditorLooper->PostMessage(&find); 1999 break; 2000 } 2001 2002 case kMsgStopFind: 2003 fEditorLooper->QuitFind(); 2004 break; 2005 2006 case B_NODE_MONITOR: 2007 { 2008 switch (message->FindInt32("opcode")) { 2009 case B_STAT_CHANGED: 2010 fEditor.ForceUpdate(); 2011 break; 2012 case B_ATTR_CHANGED: 2013 { 2014 const char *name; 2015 if (message->FindString("attr", &name) != B_OK) 2016 break; 2017 2018 if (fEditor.IsAttribute()) { 2019 if (!strcmp(name, fEditor.Attribute())) 2020 fEditor.ForceUpdate(); 2021 } else { 2022 BMenuBar *bar = Window()->KeyMenuBar(); 2023 if (bar != NULL) { 2024 BMenuItem *item = bar->FindItem("Attributes"); 2025 if (item != NULL && item->Submenu() != NULL) 2026 _UpdateAttributesMenu(item->Submenu()); 2027 } 2028 } 2029 2030 // There might be a new icon 2031 if (!strcmp(name, "BEOS:TYPE") 2032 || !strcmp(name, "BEOS:M:STD_ICON") 2033 || !strcmp(name, "BEOS:L:STD_ICON") 2034 || !strcmp(name, "BEOS:ICON")) 2035 fHeaderView->UpdateIcon(); 2036 break; 2037 } 2038 } 2039 break; 2040 } 2041 2042 case B_CLIPBOARD_CHANGED: 2043 _CheckClipboard(); 2044 break; 2045 2046 case kMsgDataEditorStateChange: 2047 { 2048 bool enabled; 2049 if (message->FindBool("can_undo", &enabled) == B_OK) 2050 fUndoMenuItem->SetEnabled(enabled); 2051 2052 if (message->FindBool("can_redo", &enabled) == B_OK) 2053 fRedoMenuItem->SetEnabled(enabled); 2054 2055 if (message->FindBool("modified", &enabled) == B_OK) 2056 fSaveMenuItem->SetEnabled(enabled); 2057 break; 2058 } 2059 2060 default: 2061 BView::MessageReceived(message); 2062 } 2063 } 2064 2065