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