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