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