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