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