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