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