xref: /haiku/src/apps/diskprobe/ProbeView.cpp (revision 820dca4df6c7bf955c46e8f6521b9408f50b2900)
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%04lx", 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%Lx" : "%Ld", value);
709 }
710 
711 
712 void
713 HeaderView::UpdatePositionViews(bool all)
714 {
715 	char buffer[64];
716 	FormatValue(buffer, sizeof(buffer), fPosition / fBlockSize);
717 	fPositionControl->SetText(buffer);
718 
719 	if (all) {
720 		FormatValue(buffer, sizeof(buffer), fPosition + fOffset);
721 		fFileOffsetView->SetText(buffer);
722 	}
723 }
724 
725 
726 void
727 HeaderView::UpdateOffsetViews(bool all)
728 {
729 	char buffer[64];
730 	FormatValue(buffer, sizeof(buffer), fOffset);
731 	fOffsetView->SetText(buffer);
732 
733 	if (all) {
734 		FormatValue(buffer, sizeof(buffer), fPosition + fOffset);
735 		fFileOffsetView->SetText(buffer);
736 	}
737 }
738 
739 
740 void
741 HeaderView::UpdateFileSizeView()
742 {
743 	BString string(B_TRANSLATE("of "));
744 	char buffer[64];
745 	FormatValue(buffer, sizeof(buffer),
746 		(fFileSize + fBlockSize - 1) / fBlockSize);
747 	string << buffer;
748 
749 	fSizeView->SetText(string.String());
750 }
751 
752 
753 void
754 HeaderView::SetBase(base_type type)
755 {
756 	if (fBase == type)
757 		return;
758 
759 	fBase = type;
760 
761 	UpdatePositionViews();
762 	UpdateOffsetViews(false);
763 	UpdateFileSizeView();
764 }
765 
766 
767 void
768 HeaderView::SetTo(off_t position, uint32 blockSize)
769 {
770 	fPosition = position;
771 	fLastPosition = (fLastPosition / fBlockSize) * blockSize;
772 	fBlockSize = blockSize;
773 
774 	fPositionSlider->SetBlockSize(blockSize);
775 	UpdatePositionViews();
776 	UpdateOffsetViews(false);
777 	UpdateFileSizeView();
778 }
779 
780 
781 void
782 HeaderView::NotifyTarget()
783 {
784 	BMessage update(kMsgPositionUpdate);
785 	update.AddInt64("position", fPosition);
786 	Messenger().SendMessage(&update);
787 }
788 
789 
790 void
791 HeaderView::MessageReceived(BMessage *message)
792 {
793 	switch (message->what) {
794 		case B_OBSERVER_NOTICE_CHANGE: {
795 			int32 what;
796 			if (message->FindInt32(B_OBSERVE_WHAT_CHANGE, &what) != B_OK)
797 				break;
798 
799 			switch (what) {
800 				case kDataViewCursorPosition:
801 					off_t offset;
802 					if (message->FindInt64("position", &offset) == B_OK) {
803 						fOffset = offset;
804 						UpdateOffsetViews();
805 					}
806 					break;
807 			}
808 			break;
809 		}
810 
811 		case kMsgSliderUpdate:
812 		{
813 			// First, make sure we're only considering the most
814 			// up-to-date message in the queue (which might not
815 			// be this one).
816 			// If there is another message of this type in the
817 			// queue, we're just ignoring the current message.
818 
819 			if (Looper()->MessageQueue()->FindMessage(kMsgSliderUpdate, 0)
820 					!= NULL)
821 				break;
822 
823 			// if nothing has changed, we can ignore this message as well
824 			if (fPosition == fPositionSlider->Position())
825 				break;
826 
827 			fLastPosition = fPosition;
828 			fPosition = fPositionSlider->Position();
829 
830 			// update position text control
831 			UpdatePositionViews();
832 
833 			// notify our target
834 			NotifyTarget();
835 			break;
836 		}
837 
838 		case kMsgDataEditorFindProgress:
839 		{
840 			bool state;
841 			if (message->FindBool("running", &state) == B_OK
842 				&& fFileSize > fBlockSize) {
843 				fPositionSlider->SetEnabled(!state);
844 				if (state) {
845 					fPathView->ResizeBy(-fStopButton->Bounds().Width(), 0);
846 					fStopButton->Show();
847 				} else {
848 					fStopButton->Hide();
849 					fPathView->ResizeBy(fStopButton->Bounds().Width(), 0);
850 				}
851 			}
852 
853 			off_t position;
854 			if (message->FindInt64("position", &position) != B_OK)
855 				break;
856 
857 			fPosition = (position / fBlockSize) * fBlockSize;
858 				// round to block size
859 
860 			// update views
861 			UpdatePositionViews(false);
862 			fPositionSlider->SetPosition(fPosition);
863 			break;
864 		}
865 
866 		case kMsgPositionUpdate:
867 		{
868 			off_t lastPosition = fPosition;
869 
870 			off_t position;
871 			int32 delta;
872 			if (message->FindInt64("position", &position) == B_OK)
873 				fPosition = position;
874 			else if (message->FindInt64("block", &position) == B_OK) {
875 				if (position < 0)
876 					position += (fFileSize - 1) / fBlockSize + 1;
877 				fPosition = position * fBlockSize;
878 			} else if (message->FindInt32("delta", &delta) == B_OK) {
879 				fPosition += delta * off_t(fBlockSize);
880 			} else {
881 				try {
882 					ExpressionParser parser;
883 					parser.SetSupportHexInput(true);
884 					fPosition = parser.EvaluateToInt64(
885 						fPositionControl->Text()) * fBlockSize;
886 				} catch (...) {
887 					beep();
888 					break;
889 				}
890 			}
891 
892 			fLastPosition = lastPosition;
893 			fPosition = (fPosition / fBlockSize) * fBlockSize;
894 				// round to block size
895 
896 			if (fPosition < 0)
897 				fPosition = 0;
898 			else if (fPosition > ((fFileSize - 1) / fBlockSize) * fBlockSize)
899 				fPosition = ((fFileSize - 1) / fBlockSize) * fBlockSize;
900 
901 			// update views
902 			UpdatePositionViews();
903 			fPositionSlider->SetPosition(fPosition);
904 
905 			// notify our target
906 			NotifyTarget();
907 			break;
908 		}
909 
910 		case kMsgLastPosition:
911 		{
912 			fPosition = fLastPosition;
913 			fLastPosition = fPositionSlider->Position();
914 
915 			// update views
916 			UpdatePositionViews();
917 			fPositionSlider->SetPosition(fPosition);
918 
919 			// notify our target
920 			NotifyTarget();
921 			break;
922 		}
923 
924 		case kMsgBaseType:
925 		{
926 			int32 type;
927 			if (message->FindInt32("base", &type) != B_OK)
928 				break;
929 
930 			SetBase((base_type)type);
931 			break;
932 		}
933 
934 		default:
935 			BView::MessageReceived(message);
936 	}
937 }
938 
939 
940 //	#pragma mark - TypeMenuItem
941 
942 
943 /*!	The TypeMenuItem is a BMenuItem that displays a type string at its
944 	right border.
945 	It is used to display the attribute and type in the attributes menu.
946 	It does not mix nicely with short cuts.
947 */
948 TypeMenuItem::TypeMenuItem(const char *name, const char *type,
949 		BMessage *message)
950 	: BMenuItem(name, message),
951 	fType(type)
952 {
953 }
954 
955 
956 void
957 TypeMenuItem::GetContentSize(float *_width, float *_height)
958 {
959 	BMenuItem::GetContentSize(_width, _height);
960 
961 	if (_width)
962 		*_width += Menu()->StringWidth(fType.String());
963 }
964 
965 
966 void
967 TypeMenuItem::DrawContent()
968 {
969 	// draw the label
970 	BMenuItem::DrawContent();
971 
972 	font_height fontHeight;
973 	Menu()->GetFontHeight(&fontHeight);
974 
975 	// draw the type
976 	BPoint point = ContentLocation();
977 	point.x = Frame().right - 4 - Menu()->StringWidth(fType.String());
978 	point.y += fontHeight.ascent;
979 
980 #ifdef HAIKU_TARGET_PLATFORM_BEOS
981 	Menu()->SetDrawingMode(B_OP_ALPHA);
982 #endif
983 
984 	Menu()->DrawString(fType.String(), point);
985 }
986 
987 
988 //	#pragma mark - EditorLooper
989 
990 
991 /*!	The purpose of this looper is to off-load the editor data loading from
992 	the main window looper.
993 
994 	It will listen to the offset changes of the editor, let him update its
995 	data, and will then synchronously notify the target.
996 	That way, simple offset changes will not stop the main looper from
997 	operating. Therefore, all offset updates for the editor will go through
998 	this looper.
999 	Also, it will run the find action in the editor.
1000 */
1001 EditorLooper::EditorLooper(const char *name, DataEditor &editor,
1002 		BMessenger target)
1003 	: BLooper(name),
1004 	fEditor(editor),
1005 	fMessenger(target),
1006 	fQuitFind(true)
1007 {
1008 	fEditor.StartWatching(this);
1009 }
1010 
1011 
1012 EditorLooper::~EditorLooper()
1013 {
1014 	fEditor.StopWatching(this);
1015 }
1016 
1017 
1018 void
1019 EditorLooper::MessageReceived(BMessage *message)
1020 {
1021 	switch (message->what) {
1022 		case kMsgPositionUpdate:
1023 		{
1024 			// First, make sure we're only considering the most
1025 			// up-to-date message in the queue (which might not
1026 			// be this one).
1027 			// If there is another message of this type in the
1028 			// queue, we're just ignoring the current message.
1029 
1030 			if (Looper()->MessageQueue()->FindMessage(kMsgPositionUpdate, 0) != NULL)
1031 				break;
1032 
1033 			off_t position;
1034 			if (message->FindInt64("position", &position) == B_OK) {
1035 				BAutolock locker(fEditor);
1036 				fEditor.SetViewOffset(position);
1037 			}
1038 			break;
1039 		}
1040 
1041 		case kMsgDataEditorParameterChange:
1042 		{
1043 			bool updated = false;
1044 
1045 			if (fEditor.Lock()) {
1046 				fEditor.UpdateIfNeeded(&updated);
1047 				fEditor.Unlock();
1048 			}
1049 
1050 			if (updated) {
1051 				BMessage reply;
1052 				fMessenger.SendMessage(kMsgUpdateData, &reply);
1053 					// We are doing a synchronously transfer, to prevent
1054 					// that we're already locking the editor again when
1055 					// our target wants to get the editor data.
1056 			}
1057 			break;
1058 		}
1059 
1060 		case kMsgFind:
1061 		{
1062 			BMessenger progressMonitor;
1063 			message->FindMessenger("progress_monitor", &progressMonitor);
1064 
1065 			off_t startAt = 0;
1066 			message->FindInt64("start", &startAt);
1067 
1068 			bool caseInsensitive = !message->FindBool("case_sensitive");
1069 
1070 			ssize_t dataSize;
1071 			const uint8 *data;
1072 			if (message->FindData("data", B_RAW_TYPE, (const void **)&data,
1073 					&dataSize) == B_OK)
1074 				Find(startAt, data, dataSize, caseInsensitive, progressMonitor);
1075 		}
1076 
1077 		default:
1078 			BLooper::MessageReceived(message);
1079 			break;
1080 	}
1081 }
1082 
1083 
1084 void
1085 EditorLooper::Find(off_t startAt, const uint8 *data, size_t dataSize,
1086 	bool caseInsensitive, BMessenger progressMonitor)
1087 {
1088 	fQuitFind = false;
1089 
1090 	BAutolock locker(fEditor);
1091 
1092 	bigtime_t startTime = system_time();
1093 
1094 	off_t foundAt = fEditor.Find(startAt, data, dataSize, caseInsensitive,
1095 						true, progressMonitor, &fQuitFind);
1096 	if (foundAt >= B_OK) {
1097 		fEditor.SetViewOffset(foundAt);
1098 
1099 		// select the part in our target
1100 		BMessage message(kMsgSetSelection);
1101 		message.AddInt64("start", foundAt - fEditor.ViewOffset());
1102 		message.AddInt64("end", foundAt + dataSize - 1 - fEditor.ViewOffset());
1103 		fMessenger.SendMessage(&message);
1104 	} else if (foundAt == B_ENTRY_NOT_FOUND) {
1105 		if (system_time() > startTime + 8000000LL) {
1106 			// If the user had to wait more than 8 seconds for the result,
1107 			// we are trying to please him with a requester...
1108 			BAlert* alert = new BAlert(B_TRANSLATE("DiskProbe request"),
1109 				B_TRANSLATE("Could not find search string."),
1110 				B_TRANSLATE("OK"), NULL, NULL, B_WIDTH_AS_USUAL,
1111 				B_WARNING_ALERT);
1112 			alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
1113 			alert->Go(NULL);
1114 		} else
1115 			beep();
1116 	}
1117 }
1118 
1119 
1120 void
1121 EditorLooper::QuitFind()
1122 {
1123 	fQuitFind = true;
1124 		// this will cleanly stop the find process
1125 }
1126 
1127 
1128 //	#pragma mark - TypeView
1129 
1130 
1131 TypeView::TypeView(BRect rect, const char* name, int32 index,
1132 		DataEditor& editor, int32 resizingMode)
1133 	: BView(rect, name, resizingMode, B_FRAME_EVENTS)
1134 {
1135 	SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR));
1136 
1137 	fTypeEditorView = GetTypeEditorAt(index, Frame(), editor);
1138 	if (fTypeEditorView == NULL) {
1139 		AddChild(new BStringView(Bounds(), B_TRANSLATE("Type editor"),
1140 			B_TRANSLATE("Type editor not supported"), B_FOLLOW_NONE));
1141 	} else
1142 		AddChild(fTypeEditorView);
1143 
1144 	if ((fTypeEditorView->ResizingMode() & (B_FOLLOW_RIGHT | B_FOLLOW_BOTTOM))
1145 			!= 0) {
1146 		BRect rect = Bounds();
1147 
1148 		BRect frame = fTypeEditorView->Frame();
1149 		rect.left = frame.left;
1150 		rect.top = frame.top;
1151 		if ((fTypeEditorView->ResizingMode() & B_FOLLOW_RIGHT) == 0)
1152 			rect.right = frame.right;
1153 		if ((fTypeEditorView->ResizingMode() & B_FOLLOW_BOTTOM) == 0)
1154 			rect.bottom = frame.bottom;
1155 
1156 		fTypeEditorView->ResizeTo(rect.Width(), rect.Height());
1157 	}
1158 }
1159 
1160 
1161 TypeView::~TypeView()
1162 {
1163 }
1164 
1165 
1166 void
1167 TypeView::FrameResized(float width, float height)
1168 {
1169 	BRect rect = Bounds();
1170 
1171 	BPoint point = fTypeEditorView->Frame().LeftTop();
1172 	if ((fTypeEditorView->ResizingMode() & B_FOLLOW_RIGHT) == 0)
1173 		point.x = (rect.Width() - fTypeEditorView->Bounds().Width()) / 2;
1174 	if ((fTypeEditorView->ResizingMode() & B_FOLLOW_BOTTOM) == 0)
1175 		point.y = (rect.Height() - fTypeEditorView->Bounds().Height()) / 2;
1176 
1177 	fTypeEditorView->MoveTo(point);
1178 }
1179 
1180 
1181 //	#pragma mark - ProbeView
1182 
1183 
1184 ProbeView::ProbeView(BRect rect, entry_ref *ref, const char *attribute,
1185 		const BMessage *settings)
1186 	: BView(rect, "probeView", B_FOLLOW_ALL, B_WILL_DRAW),
1187 	fPrintSettings(NULL),
1188 	fTypeView(NULL),
1189 	fLastSearch(NULL)
1190 {
1191 	fEditor.SetTo(*ref, attribute);
1192 
1193 	int32 baseType = kHexBase;
1194 	float fontSize = 12.0f;
1195 	if (settings != NULL) {
1196 		settings->FindInt32("base_type", &baseType);
1197 		settings->FindFloat("font_size", &fontSize);
1198 	}
1199 
1200 	rect = Bounds();
1201 	fHeaderView = new HeaderView(rect, &fEditor.Ref(), fEditor);
1202 	fHeaderView->ResizeToPreferred();
1203 	fHeaderView->SetBase((base_type)baseType);
1204 	AddChild(fHeaderView);
1205 
1206 	rect = fHeaderView->Frame();
1207 	rect.top = rect.bottom + 3;
1208 	rect.bottom = Bounds().bottom - B_H_SCROLL_BAR_HEIGHT;
1209 	rect.right -= B_V_SCROLL_BAR_WIDTH;
1210 	fDataView = new DataView(rect, fEditor);
1211 	fDataView->SetBase((base_type)baseType);
1212 	fDataView->SetFontSize(fontSize);
1213 
1214 	fScrollView = new BScrollView("scroller", fDataView, B_FOLLOW_ALL,
1215 		B_WILL_DRAW, true, true);
1216 	AddChild(fScrollView);
1217 
1218 	fDataView->UpdateScroller();
1219 }
1220 
1221 
1222 ProbeView::~ProbeView()
1223 {
1224 }
1225 
1226 
1227 void
1228 ProbeView::UpdateSizeLimits()
1229 {
1230 	if (Window() == NULL)
1231 		return;
1232 
1233 	if (!fDataView->FontSizeFitsBounds()) {
1234 		float width, height;
1235 		fDataView->GetPreferredSize(&width, &height);
1236 
1237 		BRect frame = Window()->ConvertFromScreen(ConvertToScreen(
1238 			fHeaderView->Frame()));
1239 
1240 		Window()->SetSizeLimits(250, width + B_V_SCROLL_BAR_WIDTH,
1241 			200, height + frame.bottom + 4 + B_H_SCROLL_BAR_HEIGHT);
1242 	} else
1243 		Window()->SetSizeLimits(250, 32768, 200, 32768);
1244 
1245 #ifdef HAIKU_TARGET_PLATFORM_BEOS
1246 	// In Haiku and Dano, the window is resized automatically
1247 	BRect bounds = Window()->Bounds();
1248 	float minWidth, maxWidth, minHeight, maxHeight;
1249 	Window()->GetSizeLimits(&minWidth, &maxWidth, &minHeight, &maxHeight);
1250 	if (maxWidth < bounds.Width() || maxHeight < bounds.Height()) {
1251 		Window()->ResizeTo(MIN(maxWidth, bounds.Width()), MIN(maxHeight,
1252 			bounds.Height()));
1253 	}
1254 #endif
1255 }
1256 
1257 
1258 void
1259 ProbeView::DetachedFromWindow()
1260 {
1261 	fEditorLooper->QuitFind();
1262 
1263 	if (fEditorLooper->Lock())
1264 		fEditorLooper->Quit();
1265 	fEditorLooper = NULL;
1266 
1267 	fEditor.StopWatching(this);
1268 	fDataView->StopWatching(fHeaderView, kDataViewCursorPosition);
1269 	fDataView->StopWatching(this, kDataViewSelection);
1270 	fDataView->StopWatching(this, kDataViewPreferredSize);
1271 	be_clipboard->StopWatching(this);
1272 }
1273 
1274 
1275 void
1276 ProbeView::_UpdateAttributesMenu(BMenu *menu)
1277 {
1278 	// remove old contents
1279 
1280 	for (int32 i = menu->CountItems(); i-- > 0;) {
1281 		delete menu->RemoveItem(i);
1282 	}
1283 
1284 	// add new items (sorted)
1285 
1286 	BNode node(&fEditor.AttributeRef());
1287 	if (node.InitCheck() == B_OK) {
1288 		char attribute[B_ATTR_NAME_LENGTH];
1289 		node.RewindAttrs();
1290 
1291 		while (node.GetNextAttrName(attribute) == B_OK) {
1292 			attr_info info;
1293 			if (node.GetAttrInfo(attribute, &info) != B_OK)
1294 				continue;
1295 
1296 			char type[16];
1297 			type[0] = '[';
1298 			get_type_string(type + 1, sizeof(type) - 2, info.type);
1299 			strcat(type, "]");
1300 
1301 			// find where to insert
1302 			int32 i;
1303 			for (i = 0; i < menu->CountItems(); i++) {
1304 				if (strcasecmp(menu->ItemAt(i)->Label(), attribute) > 0)
1305 					break;
1306 			}
1307 
1308 			BMessage *message = new BMessage(B_REFS_RECEIVED);
1309 			message->AddRef("refs", &fEditor.AttributeRef());
1310 			message->AddString("attributes", attribute);
1311 
1312 			menu->AddItem(new TypeMenuItem(attribute, type, message), i);
1313 		}
1314 	}
1315 
1316 	if (menu->CountItems() == 0) {
1317 		// if there are no attributes, add an item to the menu
1318 		// that says so
1319 		BMenuItem *item = new BMenuItem(B_TRANSLATE_COMMENT("none",
1320 			"No attributes"), NULL);
1321 		item->SetEnabled(false);
1322 		menu->AddItem(item);
1323 	}
1324 
1325 	menu->SetTargetForItems(be_app);
1326 }
1327 
1328 
1329 void
1330 ProbeView::AddSaveMenuItems(BMenu* menu, int32 index)
1331 {
1332 	menu->AddItem(fSaveMenuItem = new BMenuItem(B_TRANSLATE("Save"),
1333 		new BMessage(B_SAVE_REQUESTED), 'S'), index);
1334 	fSaveMenuItem->SetTarget(this);
1335 	fSaveMenuItem->SetEnabled(false);
1336 	//menu->AddItem(new BMenuItem("Save As" B_UTF8_ELLIPSIS, NULL), index);
1337 }
1338 
1339 
1340 void
1341 ProbeView::AddPrintMenuItems(BMenu* menu, int32 index)
1342 {
1343 	BMenuItem *item;
1344 	menu->AddItem(item = new BMenuItem(B_TRANSLATE("Page setup" B_UTF8_ELLIPSIS),
1345 		new BMessage(kMsgPageSetup)), index++);
1346 	item->SetTarget(this);
1347 	menu->AddItem(item = new BMenuItem(B_TRANSLATE("Print" B_UTF8_ELLIPSIS),
1348 		new BMessage(kMsgPrint), 'P'), index++);
1349 	item->SetTarget(this);
1350 }
1351 
1352 
1353 void
1354 ProbeView::AddViewAsMenuItems()
1355 {
1356 #if 0
1357 	BMenuBar* bar = Window()->KeyMenuBar();
1358 	if (bar == NULL)
1359 		return;
1360 
1361 	BMenuItem* item = bar->FindItem(B_TRANSLATE("View"));
1362 	BMenu* menu = NULL;
1363 	if (item != NULL)
1364 		menu = item->Submenu();
1365 	else
1366 		menu = bar->SubmenuAt(bar->CountItems() - 1);
1367 
1368 	if (menu == NULL)
1369 		return;
1370 
1371 	menu->AddSeparatorItem();
1372 
1373 	BMenu* subMenu = new BMenu(B_TRANSLATE("View As"));
1374 	subMenu->SetRadioMode(true);
1375 
1376 	BMessage* message = new BMessage(kMsgViewAs);
1377 	subMenu->AddItem(item = new BMenuItem(B_TRANSLATE("Raw"), message));
1378 	item->SetMarked(true);
1379 
1380 	const char* name;
1381 	for (int32 i = 0; GetNthTypeEditor(i, &name) == B_OK; i++) {
1382 		message = new BMessage(kMsgViewAs);
1383 		message->AddInt32("editor index", i);
1384 		subMenu->AddItem(new BMenuItem(name, message));
1385 	}
1386 
1387 	subMenu->SetTargetForItems(this);
1388 	menu->AddItem(new BMenuItem(subMenu));
1389 #endif
1390 }
1391 
1392 
1393 void
1394 ProbeView::AttachedToWindow()
1395 {
1396 	fEditorLooper = new EditorLooper(fEditor.Ref().name, fEditor,
1397 		BMessenger(fDataView));
1398 	fEditorLooper->Run();
1399 
1400 	fEditor.StartWatching(this);
1401 	fDataView->StartWatching(fHeaderView, kDataViewCursorPosition);
1402 	fDataView->StartWatching(this, kDataViewSelection);
1403 	fDataView->StartWatching(this, kDataViewPreferredSize);
1404 	be_clipboard->StartWatching(this);
1405 
1406 	// Add menu to window
1407 
1408 	BMenuBar *bar = Window()->KeyMenuBar();
1409 	if (bar == NULL) {
1410 		// there is none? Well, but we really want to have one
1411 		bar = new BMenuBar(BRect(0, 0, 0, 0), NULL);
1412 		Window()->AddChild(bar);
1413 
1414 		MoveBy(0, bar->Bounds().Height());
1415 		ResizeBy(0, -bar->Bounds().Height());
1416 
1417 		BMenu *menu = new BMenu(fEditor.IsAttribute()
1418 			? B_TRANSLATE("Attribute") : fEditor.IsDevice() ? B_TRANSLATE("Device") : B_TRANSLATE("File"));
1419 		AddSaveMenuItems(menu, 0);
1420 		menu->AddSeparatorItem();
1421 		AddPrintMenuItems(menu, menu->CountItems());
1422 		menu->AddSeparatorItem();
1423 
1424 		menu->AddItem(new BMenuItem(B_TRANSLATE("Close"), new BMessage(B_CLOSE_REQUESTED),
1425 			'W'));
1426 		bar->AddItem(menu);
1427 	}
1428 
1429 	// "Edit" menu
1430 
1431 	BMenu *menu = new BMenu(B_TRANSLATE("Edit"));
1432 	BMenuItem *item;
1433 	menu->AddItem(fUndoMenuItem = new BMenuItem(B_TRANSLATE("Undo"), new BMessage(B_UNDO),
1434 		'Z'));
1435 	fUndoMenuItem->SetEnabled(fEditor.CanUndo());
1436 	fUndoMenuItem->SetTarget(fDataView);
1437 	menu->AddItem(fRedoMenuItem = new BMenuItem(B_TRANSLATE("Redo"), new BMessage(B_REDO),
1438 		'Z', B_SHIFT_KEY));
1439 	fRedoMenuItem->SetEnabled(fEditor.CanRedo());
1440 	fRedoMenuItem->SetTarget(fDataView);
1441 	menu->AddSeparatorItem();
1442 	menu->AddItem(item = new BMenuItem(B_TRANSLATE("Copy"), new BMessage(B_COPY), 'C'));
1443 	item->SetTarget(NULL, Window());
1444 	menu->AddItem(fPasteMenuItem = new BMenuItem(B_TRANSLATE("Paste"), new BMessage(B_PASTE),
1445 		'V'));
1446 	fPasteMenuItem->SetTarget(NULL, Window());
1447 	_CheckClipboard();
1448 	menu->AddItem(item = new BMenuItem(B_TRANSLATE("Select all"), new BMessage(B_SELECT_ALL),
1449 		'A'));
1450 	item->SetTarget(NULL, Window());
1451 	menu->AddSeparatorItem();
1452 	menu->AddItem(item = new BMenuItem(B_TRANSLATE("Find" B_UTF8_ELLIPSIS),
1453 		new BMessage(kMsgOpenFindWindow), 'F'));
1454 	item->SetTarget(this);
1455 	menu->AddItem(fFindAgainMenuItem = new BMenuItem(B_TRANSLATE("Find again"),
1456 		new BMessage(kMsgFind), 'G'));
1457 	fFindAgainMenuItem->SetEnabled(false);
1458 	fFindAgainMenuItem->SetTarget(this);
1459 	bar->AddItem(menu);
1460 
1461 	// "Block" menu
1462 
1463 	menu = new BMenu(B_TRANSLATE("Block"));
1464 	BMessage *message = new BMessage(kMsgPositionUpdate);
1465 	message->AddInt32("delta", 1);
1466 	menu->AddItem(item = new BMenuItem(B_TRANSLATE("Next"), message, B_RIGHT_ARROW));
1467 	item->SetTarget(fHeaderView);
1468 	message = new BMessage(kMsgPositionUpdate);
1469 	message->AddInt32("delta", -1);
1470 	menu->AddItem(item = new BMenuItem(B_TRANSLATE("Previous"), message, B_LEFT_ARROW));
1471 	item->SetTarget(fHeaderView);
1472 	menu->AddItem(item = new BMenuItem(B_TRANSLATE("Back"), new BMessage(kMsgLastPosition),
1473 		'J'));
1474 	item->SetTarget(fHeaderView);
1475 
1476 	BMenu *subMenu = new BMenu(B_TRANSLATE("Selection"));
1477 	message = new BMessage(kMsgPositionUpdate);
1478 	message->AddInt64("block", 0);
1479 	subMenu->AddItem(fNativeMenuItem = new BMenuItem("", message, 'K'));
1480 	fNativeMenuItem->SetTarget(fHeaderView);
1481 	message = new BMessage(*message);
1482 	subMenu->AddItem(fSwappedMenuItem = new BMenuItem("", message, 'L'));
1483 	fSwappedMenuItem->SetTarget(fHeaderView);
1484 	menu->AddItem(new BMenuItem(subMenu));
1485 	_UpdateSelectionMenuItems(0, 0);
1486 	menu->AddSeparatorItem();
1487 
1488 	fBookmarkMenu = new BMenu(B_TRANSLATE("Bookmarks"));
1489 	fBookmarkMenu->AddItem(item = new BMenuItem(B_TRANSLATE("Add"),
1490 		new BMessage(kMsgAddBookmark), 'B'));
1491 	item->SetTarget(this);
1492 	menu->AddItem(new BMenuItem(fBookmarkMenu));
1493 	bar->AddItem(menu);
1494 
1495 	// "Attributes" menu (it's only visible if the underlying
1496 	// file system actually supports attributes)
1497 
1498 	BDirectory directory;
1499 	BVolume volume;
1500 	if (directory.SetTo(&fEditor.AttributeRef()) == B_OK
1501 		&& directory.IsRootDirectory())
1502 		directory.GetVolume(&volume);
1503 	else
1504 		fEditor.File().GetVolume(&volume);
1505 
1506 	if (!fEditor.IsAttribute() && volume.InitCheck() == B_OK
1507 		&& (volume.KnowsMime() || volume.KnowsAttr())) {
1508 		bar->AddItem(menu = new BMenu(B_TRANSLATE("Attributes")));
1509 		_UpdateAttributesMenu(menu);
1510 	}
1511 
1512 	// "View" menu
1513 
1514 	menu = new BMenu(B_TRANSLATE_COMMENT("View",
1515 		"This is the last menubar item 'File Edit Block View'"));
1516 
1517 	// Number Base (hex/decimal)
1518 
1519 	subMenu = new BMenu(B_TRANSLATE_COMMENT("Base", "A menu item, the number "
1520 		"that is basis for a system of calculation. The base 10 system is a "
1521 		"decimal system. This is in the same menu window than 'Font size' "
1522 		"and 'BlockSize'"));
1523 	message = new BMessage(kMsgBaseType);
1524 	message->AddInt32("base_type", kDecimalBase);
1525 	subMenu->AddItem(item = new BMenuItem(B_TRANSLATE_COMMENT("Decimal",
1526 		"A menu item, as short as possible, noun is recommended if it is "
1527 		"shorter than adjective."), message, 'D'));
1528 	item->SetTarget(this);
1529 	if (fHeaderView->Base() == kDecimalBase)
1530 		item->SetMarked(true);
1531 
1532 	message = new BMessage(kMsgBaseType);
1533 	message->AddInt32("base_type", kHexBase);
1534 	subMenu->AddItem(item = new BMenuItem(B_TRANSLATE_COMMENT("Hex",
1535 		"A menu item, as short as possible, noun is recommended if it is "
1536 		"shorter than adjective."), message, 'H'));
1537 	item->SetTarget(this);
1538 	if (fHeaderView->Base() == kHexBase)
1539 		item->SetMarked(true);
1540 
1541 	subMenu->SetRadioMode(true);
1542 	menu->AddItem(new BMenuItem(subMenu));
1543 
1544 	// Block Size
1545 
1546 	subMenu = new BMenu(B_TRANSLATE_COMMENT("BlockSize", "A menu item, a "
1547 		"shortened form from 'block size'. This is in the same menu window"
1548 		"than 'Base' and 'Font size'"));
1549 	subMenu->SetRadioMode(true);
1550 	const uint32 blockSizes[] = {512, 1024, 2048};
1551 	for (uint32 i = 0; i < sizeof(blockSizes) / sizeof(blockSizes[0]); i++) {
1552 		char buffer[32];
1553 		snprintf(buffer, sizeof(buffer), "%ld%s", blockSizes[i],
1554 			fEditor.IsDevice() && fEditor.BlockSize() == blockSizes[i]
1555 			? B_TRANSLATE(" (native)") : "");
1556 		subMenu->AddItem(item = new BMenuItem(buffer,
1557 			message = new BMessage(kMsgBlockSize)));
1558 		message->AddInt32("block_size", blockSizes[i]);
1559 		if (fEditor.BlockSize() == blockSizes[i])
1560 			item->SetMarked(true);
1561 	}
1562 	if (subMenu->FindMarked() == NULL) {
1563 		// if the device has some weird block size, we'll add it here, too
1564 		char buffer[32];
1565 		snprintf(buffer, sizeof(buffer), B_TRANSLATE("%ld (native)"), fEditor.BlockSize());
1566 		subMenu->AddItem(item = new BMenuItem(buffer,
1567 			message = new BMessage(kMsgBlockSize)));
1568 		message->AddInt32("block_size", fEditor.BlockSize());
1569 		item->SetMarked(true);
1570 	}
1571 	subMenu->SetTargetForItems(this);
1572 	menu->AddItem(new BMenuItem(subMenu));
1573 	menu->AddSeparatorItem();
1574 
1575 	// Font Size
1576 
1577 	subMenu = new BMenu(B_TRANSLATE("Font size"));
1578 	subMenu->SetRadioMode(true);
1579 	const int32 fontSizes[] = {9, 10, 11, 12, 13, 14, 18, 24, 36, 48};
1580 	int32 fontSize = int32(fDataView->FontSize() + 0.5);
1581 	if (fDataView->FontSizeFitsBounds())
1582 		fontSize = 0;
1583 	for (uint32 i = 0; i < sizeof(fontSizes) / sizeof(fontSizes[0]); i++) {
1584 		char buffer[16];
1585 		snprintf(buffer, sizeof(buffer), "%ld", fontSizes[i]);
1586 		subMenu->AddItem(item = new BMenuItem(buffer,
1587 			message = new BMessage(kMsgFontSize)));
1588 		message->AddFloat("font_size", fontSizes[i]);
1589 		if (fontSizes[i] == fontSize)
1590 			item->SetMarked(true);
1591 	}
1592 	subMenu->AddSeparatorItem();
1593 	subMenu->AddItem(item = new BMenuItem(B_TRANSLATE_COMMENT("Fit",
1594 		"Size of fonts, fits to available room"),
1595 		message = new BMessage(kMsgFontSize)));
1596 	message->AddFloat("font_size", 0.0f);
1597 	if (fontSize == 0)
1598 		item->SetMarked(true);
1599 
1600 	subMenu->SetTargetForItems(this);
1601 	menu->AddItem(new BMenuItem(subMenu));
1602 
1603 	bar->AddItem(menu);
1604 }
1605 
1606 
1607 void
1608 ProbeView::AllAttached()
1609 {
1610 	fHeaderView->SetTarget(fEditorLooper);
1611 }
1612 
1613 
1614 void
1615 ProbeView::WindowActivated(bool active)
1616 {
1617 	if (!active)
1618 		return;
1619 
1620 	fDataView->MakeFocus(true);
1621 
1622 	// set this view as the current find panel's target
1623 	BMessage target(kMsgFindTarget);
1624 	target.AddMessenger("target", this);
1625 	be_app_messenger.SendMessage(&target);
1626 }
1627 
1628 
1629 void
1630 ProbeView::_UpdateSelectionMenuItems(int64 start, int64 end)
1631 {
1632 	int64 position = 0;
1633 	const uint8 *data = fDataView->DataAt(start);
1634 	if (data == NULL) {
1635 		fNativeMenuItem->SetEnabled(false);
1636 		fSwappedMenuItem->SetEnabled(false);
1637 		return;
1638 	}
1639 
1640 	// retrieve native endian position
1641 
1642 	int size;
1643 	if (end < start + 8)
1644 		size = end + 1 - start;
1645 	else
1646 		size = 8;
1647 
1648 	int64 bigEndianPosition = 0;
1649 	memcpy(&bigEndianPosition, data, size);
1650 
1651 	position = B_BENDIAN_TO_HOST_INT64(bigEndianPosition) >> (8 * (8 - size));
1652 
1653 	// update menu items
1654 
1655 	char buffer[128];
1656 	if (fDataView->Base() == kHexBase)
1657 		snprintf(buffer, sizeof(buffer), B_TRANSLATE("Native: 0x%0*Lx"), size * 2, position);
1658 	else
1659 		snprintf(buffer, sizeof(buffer), B_TRANSLATE("Native: %Ld (0x%0*Lx)"), position, size * 2, position);
1660 
1661 	fNativeMenuItem->SetLabel(buffer);
1662 	fNativeMenuItem->SetEnabled(position >= 0 && (position * fEditor.BlockSize()) < fEditor.FileSize());
1663 	fNativeMenuItem->Message()->ReplaceInt64("block", position);
1664 
1665 	position = B_SWAP_INT64(position) >> (8 * (8 - size));
1666 	if (fDataView->Base() == kHexBase)
1667 		snprintf(buffer, sizeof(buffer), B_TRANSLATE("Swapped: 0x%0*Lx"), size * 2, position);
1668 	else
1669 		snprintf(buffer, sizeof(buffer), B_TRANSLATE("Swapped: %Ld (0x%0*Lx)"), position, size * 2, position);
1670 
1671 	fSwappedMenuItem->SetLabel(buffer);
1672 	fSwappedMenuItem->SetEnabled(position >= 0 && (position * fEditor.BlockSize()) < fEditor.FileSize());
1673 	fSwappedMenuItem->Message()->ReplaceInt64("block", position);
1674 }
1675 
1676 
1677 void
1678 ProbeView::_UpdateBookmarkMenuItems()
1679 {
1680 	for (int32 i = 2; i < fBookmarkMenu->CountItems(); i++) {
1681 		BMenuItem *item = fBookmarkMenu->ItemAt(i);
1682 		if (item == NULL)
1683 			break;
1684 
1685 		BMessage *message = item->Message();
1686 		if (message == NULL)
1687 			break;
1688 
1689 		off_t block = message->FindInt64("block");
1690 
1691 		char buffer[128];
1692 		if (fDataView->Base() == kHexBase)
1693 			snprintf(buffer, sizeof(buffer), B_TRANSLATE("Block 0x%Lx"), block);
1694 		else
1695 			snprintf(buffer, sizeof(buffer), B_TRANSLATE("Block %Ld (0x%Lx)"), block, block);
1696 
1697 		item->SetLabel(buffer);
1698 	}
1699 }
1700 
1701 
1702 void
1703 ProbeView::_AddBookmark(off_t position)
1704 {
1705 	int32 count = fBookmarkMenu->CountItems();
1706 
1707 	if (count == 1) {
1708 		fBookmarkMenu->AddSeparatorItem();
1709 		count++;
1710 	}
1711 
1712 	// insert current position as bookmark
1713 
1714 	off_t block = position / fEditor.BlockSize();
1715 
1716 	off_t bookmark = -1;
1717 	BMenuItem *item;
1718 	int32 i;
1719 	for (i = 2; (item = fBookmarkMenu->ItemAt(i)) != NULL; i++) {
1720 		BMessage *message = item->Message();
1721 		if (message != NULL && message->FindInt64("block", &bookmark) == B_OK) {
1722 			if (block <= bookmark)
1723 				break;
1724 		}
1725 	}
1726 
1727 	// the bookmark already exists
1728 	if (block == bookmark)
1729 		return;
1730 
1731 	char buffer[128];
1732 	if (fDataView->Base() == kHexBase)
1733 		snprintf(buffer, sizeof(buffer), B_TRANSLATE("Block 0x%Lx"), block);
1734 	else
1735 		snprintf(buffer, sizeof(buffer), B_TRANSLATE("Block %Ld (0x%Lx)"), block, block);
1736 
1737 	BMessage *message;
1738 	item = new BMenuItem(buffer, message = new BMessage(kMsgPositionUpdate));
1739 	item->SetTarget(fHeaderView);
1740 	if (count < 12)
1741 		item->SetShortcut('0' + count - 2, B_COMMAND_KEY);
1742 	message->AddInt64("block", block);
1743 
1744 	fBookmarkMenu->AddItem(item, i);
1745 }
1746 
1747 
1748 void
1749 ProbeView::_RemoveTypeEditor()
1750 {
1751 	if (fTypeView == NULL)
1752 		return;
1753 
1754 	if (Parent() != NULL)
1755 		Parent()->RemoveChild(fTypeView);
1756 	else
1757 		Window()->RemoveChild(fTypeView);
1758 
1759 	delete fTypeView;
1760 	fTypeView = NULL;
1761 }
1762 
1763 
1764 void
1765 ProbeView::_SetTypeEditor(int32 index)
1766 {
1767 	if (index == -1) {
1768 		// remove type editor, show raw editor
1769 		if (IsHidden())
1770 			Show();
1771 
1772 		_RemoveTypeEditor();
1773 	} else {
1774 		// hide raw editor, create and show type editor
1775 		if (!IsHidden())
1776 			Hide();
1777 
1778 		_RemoveTypeEditor();
1779 
1780 		fTypeView = new TypeView(Frame(), "type shell", index, fEditor,
1781 			B_FOLLOW_ALL);
1782 
1783 		if (Parent() != NULL)
1784 			Parent()->AddChild(fTypeView);
1785 		else
1786 			Window()->AddChild(fTypeView);
1787 	}
1788 }
1789 
1790 
1791 void
1792 ProbeView::_CheckClipboard()
1793 {
1794 	if (!be_clipboard->Lock())
1795 		return;
1796 
1797 	bool hasData = false;
1798 	BMessage *clip;
1799 	if ((clip = be_clipboard->Data()) != NULL) {
1800 		const void *data;
1801 		ssize_t size;
1802 		if (clip->FindData(B_FILE_MIME_TYPE, B_MIME_TYPE, &data, &size) == B_OK
1803 			|| clip->FindData("text/plain", B_MIME_TYPE, &data, &size) == B_OK)
1804 			hasData = true;
1805 	}
1806 
1807 	be_clipboard->Unlock();
1808 
1809 	fPasteMenuItem->SetEnabled(hasData);
1810 }
1811 
1812 
1813 status_t
1814 ProbeView::_PageSetup()
1815 {
1816 	BPrintJob printJob(Window()->Title());
1817 	if (fPrintSettings != NULL)
1818 		printJob.SetSettings(new BMessage(*fPrintSettings));
1819 
1820 	status_t status = printJob.ConfigPage();
1821 	if (status == B_OK) {
1822 		// replace the print settings on success
1823 		delete fPrintSettings;
1824 		fPrintSettings = printJob.Settings();
1825 	}
1826 
1827 	return status;
1828 }
1829 
1830 
1831 void
1832 ProbeView::_Print()
1833 {
1834 	if (fPrintSettings == NULL && _PageSetup() != B_OK)
1835 		return;
1836 
1837 	BPrintJob printJob(Window()->Title());
1838 	printJob.SetSettings(new BMessage(*fPrintSettings));
1839 
1840 	if (printJob.ConfigJob() == B_OK) {
1841 		BRect rect = printJob.PrintableRect();
1842 
1843 		float width, height;
1844 		fDataView->GetPreferredSize(&width, &height);
1845 
1846 		printJob.BeginJob();
1847 
1848 		fDataView->SetScale(rect.Width() / width);
1849 		printJob.DrawView(fDataView, rect, rect.LeftTop());
1850 		fDataView->SetScale(1.0);
1851 		printJob.SpoolPage();
1852 
1853 		printJob.CommitJob();
1854 	}
1855 }
1856 
1857 
1858 status_t
1859 ProbeView::_Save()
1860 {
1861 	status_t status = fEditor.Save();
1862 	if (status == B_OK)
1863 		return B_OK;
1864 
1865 	char buffer[1024];
1866 	snprintf(buffer, sizeof(buffer),
1867 		B_TRANSLATE("Writing to the file failed:\n"
1868 		"%s\n\n"
1869 		"All changes will be lost when you quit."),
1870 		strerror(status));
1871 
1872 	BAlert* alert = new BAlert(B_TRANSLATE("DiskProbe request"),
1873 		buffer, B_TRANSLATE("OK"), NULL, NULL,
1874 		B_WIDTH_AS_USUAL, B_WARNING_ALERT);
1875 	alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
1876 	alert->Go(NULL);
1877 
1878 	return status;
1879 }
1880 
1881 
1882 bool
1883 ProbeView::QuitRequested()
1884 {
1885 	fEditorLooper->QuitFind();
1886 
1887 	if (!fEditor.IsModified())
1888 		return true;
1889 
1890 	BAlert* alert = new BAlert(B_TRANSLATE("DiskProbe request"),
1891 		B_TRANSLATE("Save changes before closing?"), B_TRANSLATE("Cancel"),
1892 		B_TRANSLATE("Don't save"), B_TRANSLATE("Save"), B_WIDTH_AS_USUAL,
1893 		B_OFFSET_SPACING, B_WARNING_ALERT);
1894 	alert->SetShortcut(0, B_ESCAPE);
1895 	alert->SetShortcut(1, 'd');
1896 	alert->SetShortcut(2, 's');
1897 	int32 chosen = alert->Go();
1898 
1899 	if (chosen == 0)
1900 		return false;
1901 	if (chosen == 1)
1902 		return true;
1903 
1904 	return _Save() == B_OK;
1905 }
1906 
1907 
1908 void
1909 ProbeView::MessageReceived(BMessage *message)
1910 {
1911 	switch (message->what) {
1912 		case B_SAVE_REQUESTED:
1913 			_Save();
1914 			break;
1915 
1916 		case B_OBSERVER_NOTICE_CHANGE: {
1917 			int32 what;
1918 			if (message->FindInt32(B_OBSERVE_WHAT_CHANGE, &what) != B_OK)
1919 				break;
1920 
1921 			switch (what) {
1922 				case kDataViewSelection:
1923 				{
1924 					int64 start, end;
1925 					if (message->FindInt64("start", &start) == B_OK
1926 						&& message->FindInt64("end", &end) == B_OK)
1927 						_UpdateSelectionMenuItems(start, end);
1928 					break;
1929 				}
1930 				case kDataViewPreferredSize:
1931 					UpdateSizeLimits();
1932 					break;
1933 			}
1934 			break;
1935 		}
1936 
1937 		case kMsgBaseType:
1938 		{
1939 			int32 type;
1940 			if (message->FindInt32("base_type", &type) != B_OK)
1941 				break;
1942 
1943 			fHeaderView->SetBase((base_type)type);
1944 			fDataView->SetBase((base_type)type);
1945 
1946 			// The selection menu items depend on the base type as well
1947 			int32 start, end;
1948 			fDataView->GetSelection(start, end);
1949 			_UpdateSelectionMenuItems(start, end);
1950 
1951 			_UpdateBookmarkMenuItems();
1952 
1953 			// update the application's settings
1954 			BMessage update(*message);
1955 			update.what = kMsgSettingsChanged;
1956 			be_app_messenger.SendMessage(&update);
1957 			break;
1958 		}
1959 
1960 		case kMsgFontSize:
1961 		{
1962 			float size;
1963 			if (message->FindFloat("font_size", &size) != B_OK)
1964 				break;
1965 
1966 			fDataView->SetFontSize(size);
1967 
1968 			// update the application's settings
1969 			BMessage update(*message);
1970 			update.what = kMsgSettingsChanged;
1971 			be_app_messenger.SendMessage(&update);
1972 			break;
1973 		}
1974 
1975 		case kMsgBlockSize:
1976 		{
1977 			int32 blockSize;
1978 			if (message->FindInt32("block_size", &blockSize) != B_OK)
1979 				break;
1980 
1981 			BAutolock locker(fEditor);
1982 
1983 			if (fEditor.SetViewSize(blockSize) == B_OK
1984 				&& fEditor.SetBlockSize(blockSize) == B_OK)
1985 				fHeaderView->SetTo(fEditor.ViewOffset(), blockSize);
1986 			break;
1987 		}
1988 
1989 		case kMsgViewAs:
1990 		{
1991 			int32 index;
1992 			if (message->FindInt32("editor index", &index) != B_OK)
1993 				index = -1;
1994 
1995 			_SetTypeEditor(index);
1996 			break;
1997 		}
1998 
1999 		case kMsgAddBookmark:
2000 			_AddBookmark(fHeaderView->Position());
2001 			break;
2002 
2003 		case kMsgPrint:
2004 			_Print();
2005 			break;
2006 
2007 		case kMsgPageSetup:
2008 			_PageSetup();
2009 			break;
2010 
2011 		case kMsgOpenFindWindow:
2012 		{
2013 			fEditorLooper->QuitFind();
2014 
2015 			// set this view as the current find panel's target
2016 			BMessage find(*fFindAgainMenuItem->Message());
2017 			find.what = kMsgOpenFindWindow;
2018 			find.AddMessenger("target", this);
2019 			be_app_messenger.SendMessage(&find);
2020 			break;
2021 		}
2022 
2023 		case kMsgFind:
2024 		{
2025 			const uint8 *data;
2026 			ssize_t size;
2027 			if (message->FindData("data", B_RAW_TYPE, (const void **)&data, &size) != B_OK) {
2028 				// search again for last pattern
2029 				BMessage *itemMessage = fFindAgainMenuItem->Message();
2030 				if (itemMessage == NULL
2031 					|| itemMessage->FindData("data", B_RAW_TYPE, (const void **)&data, &size) != B_OK) {
2032 					// this shouldn't ever happen, but well...
2033 					beep();
2034 					break;
2035 				}
2036 			} else {
2037 				// remember the search pattern
2038 				fFindAgainMenuItem->SetMessage(new BMessage(*message));
2039 				fFindAgainMenuItem->SetEnabled(true);
2040 			}
2041 
2042 			int32 start, end;
2043 			fDataView->GetSelection(start, end);
2044 
2045 			BMessage find(*message);
2046 			find.AddInt64("start", fHeaderView->Position() + start + 1);
2047 			find.AddMessenger("progress_monitor", BMessenger(fHeaderView));
2048 			fEditorLooper->PostMessage(&find);
2049 			break;
2050 		}
2051 
2052 		case kMsgStopFind:
2053 			fEditorLooper->QuitFind();
2054 			break;
2055 
2056 		case B_NODE_MONITOR:
2057 		{
2058 			switch (message->FindInt32("opcode")) {
2059 				case B_STAT_CHANGED:
2060 					fEditor.ForceUpdate();
2061 					break;
2062 				case B_ATTR_CHANGED:
2063 				{
2064 					const char *name;
2065 					if (message->FindString("attr", &name) != B_OK)
2066 						break;
2067 
2068 					if (fEditor.IsAttribute()) {
2069 						if (!strcmp(name, fEditor.Attribute()))
2070 							fEditor.ForceUpdate();
2071 					} else {
2072 						BMenuBar *bar = Window()->KeyMenuBar();
2073 						if (bar != NULL) {
2074 							BMenuItem *item = bar->FindItem("Attributes");
2075 							if (item != NULL && item->Submenu() != NULL)
2076 								_UpdateAttributesMenu(item->Submenu());
2077 						}
2078 					}
2079 
2080 					// There might be a new icon
2081 					if (!strcmp(name, "BEOS:TYPE")
2082 						|| !strcmp(name, "BEOS:M:STD_ICON")
2083 						|| !strcmp(name, "BEOS:L:STD_ICON")
2084 						|| !strcmp(name, "BEOS:ICON"))
2085 						fHeaderView->UpdateIcon();
2086 					break;
2087 				}
2088 			}
2089 			break;
2090 		}
2091 
2092 		case B_CLIPBOARD_CHANGED:
2093 			_CheckClipboard();
2094 			break;
2095 
2096 		case kMsgDataEditorStateChange:
2097 		{
2098 			bool enabled;
2099 			if (message->FindBool("can_undo", &enabled) == B_OK)
2100 				fUndoMenuItem->SetEnabled(enabled);
2101 
2102 			if (message->FindBool("can_redo", &enabled) == B_OK)
2103 				fRedoMenuItem->SetEnabled(enabled);
2104 
2105 			if (message->FindBool("modified", &enabled) == B_OK)
2106 				fSaveMenuItem->SetEnabled(enabled);
2107 			break;
2108 		}
2109 
2110 		default:
2111 			BView::MessageReceived(message);
2112 	}
2113 }
2114 
2115