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