xref: /haiku/src/apps/diskprobe/ProbeView.cpp (revision 323b65468e5836bb27a5e373b14027d902349437)
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 	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 			(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))->Go(NULL);
1112 		} else
1113 			beep();
1114 	}
1115 }
1116 
1117 
1118 void
1119 EditorLooper::QuitFind()
1120 {
1121 	fQuitFind = true;
1122 		// this will cleanly stop the find process
1123 }
1124 
1125 
1126 //	#pragma mark - TypeView
1127 
1128 
1129 TypeView::TypeView(BRect rect, const char* name, int32 index,
1130 		DataEditor& editor, int32 resizingMode)
1131 	: BView(rect, name, resizingMode, B_FRAME_EVENTS)
1132 {
1133 	SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR));
1134 
1135 	fTypeEditorView = GetTypeEditorAt(index, Frame(), editor);
1136 	if (fTypeEditorView == NULL) {
1137 		AddChild(new BStringView(Bounds(), B_TRANSLATE("Type editor"),
1138 			B_TRANSLATE("Type editor not supported"), B_FOLLOW_NONE));
1139 	} else
1140 		AddChild(fTypeEditorView);
1141 
1142 	if ((fTypeEditorView->ResizingMode() & (B_FOLLOW_RIGHT | B_FOLLOW_BOTTOM))
1143 			!= 0) {
1144 		BRect rect = Bounds();
1145 
1146 		BRect frame = fTypeEditorView->Frame();
1147 		rect.left = frame.left;
1148 		rect.top = frame.top;
1149 		if ((fTypeEditorView->ResizingMode() & B_FOLLOW_RIGHT) == 0)
1150 			rect.right = frame.right;
1151 		if ((fTypeEditorView->ResizingMode() & B_FOLLOW_BOTTOM) == 0)
1152 			rect.bottom = frame.bottom;
1153 
1154 		fTypeEditorView->ResizeTo(rect.Width(), rect.Height());
1155 	}
1156 }
1157 
1158 
1159 TypeView::~TypeView()
1160 {
1161 }
1162 
1163 
1164 void
1165 TypeView::FrameResized(float width, float height)
1166 {
1167 	BRect rect = Bounds();
1168 
1169 	BPoint point = fTypeEditorView->Frame().LeftTop();
1170 	if ((fTypeEditorView->ResizingMode() & B_FOLLOW_RIGHT) == 0)
1171 		point.x = (rect.Width() - fTypeEditorView->Bounds().Width()) / 2;
1172 	if ((fTypeEditorView->ResizingMode() & B_FOLLOW_BOTTOM) == 0)
1173 		point.y = (rect.Height() - fTypeEditorView->Bounds().Height()) / 2;
1174 
1175 	fTypeEditorView->MoveTo(point);
1176 }
1177 
1178 
1179 //	#pragma mark - ProbeView
1180 
1181 
1182 ProbeView::ProbeView(BRect rect, entry_ref *ref, const char *attribute,
1183 		const BMessage *settings)
1184 	: BView(rect, "probeView", B_FOLLOW_ALL, B_WILL_DRAW),
1185 	fPrintSettings(NULL),
1186 	fTypeView(NULL),
1187 	fLastSearch(NULL)
1188 {
1189 	fEditor.SetTo(*ref, attribute);
1190 
1191 	int32 baseType = kHexBase;
1192 	float fontSize = 12.0f;
1193 	if (settings != NULL) {
1194 		settings->FindInt32("base_type", &baseType);
1195 		settings->FindFloat("font_size", &fontSize);
1196 	}
1197 
1198 	rect = Bounds();
1199 	fHeaderView = new HeaderView(rect, &fEditor.Ref(), fEditor);
1200 	fHeaderView->ResizeToPreferred();
1201 	fHeaderView->SetBase((base_type)baseType);
1202 	AddChild(fHeaderView);
1203 
1204 	rect = fHeaderView->Frame();
1205 	rect.top = rect.bottom + 3;
1206 	rect.bottom = Bounds().bottom - B_H_SCROLL_BAR_HEIGHT;
1207 	rect.right -= B_V_SCROLL_BAR_WIDTH;
1208 	fDataView = new DataView(rect, fEditor);
1209 	fDataView->SetBase((base_type)baseType);
1210 	fDataView->SetFontSize(fontSize);
1211 
1212 	fScrollView = new BScrollView("scroller", fDataView, B_FOLLOW_ALL,
1213 		B_WILL_DRAW, true, true);
1214 	AddChild(fScrollView);
1215 
1216 	fDataView->UpdateScroller();
1217 }
1218 
1219 
1220 ProbeView::~ProbeView()
1221 {
1222 }
1223 
1224 
1225 void
1226 ProbeView::UpdateSizeLimits()
1227 {
1228 	if (Window() == NULL)
1229 		return;
1230 
1231 	if (!fDataView->FontSizeFitsBounds()) {
1232 		float width, height;
1233 		fDataView->GetPreferredSize(&width, &height);
1234 
1235 		BRect frame = Window()->ConvertFromScreen(ConvertToScreen(
1236 			fHeaderView->Frame()));
1237 
1238 		Window()->SetSizeLimits(250, width + B_V_SCROLL_BAR_WIDTH,
1239 			200, height + frame.bottom + 4 + B_H_SCROLL_BAR_HEIGHT);
1240 	} else
1241 		Window()->SetSizeLimits(250, 32768, 200, 32768);
1242 
1243 #ifdef HAIKU_TARGET_PLATFORM_BEOS
1244 	// In Haiku and Dano, the window is resized automatically
1245 	BRect bounds = Window()->Bounds();
1246 	float minWidth, maxWidth, minHeight, maxHeight;
1247 	Window()->GetSizeLimits(&minWidth, &maxWidth, &minHeight, &maxHeight);
1248 	if (maxWidth < bounds.Width() || maxHeight < bounds.Height()) {
1249 		Window()->ResizeTo(MIN(maxWidth, bounds.Width()), MIN(maxHeight,
1250 			bounds.Height()));
1251 	}
1252 #endif
1253 }
1254 
1255 
1256 void
1257 ProbeView::DetachedFromWindow()
1258 {
1259 	fEditorLooper->QuitFind();
1260 
1261 	if (fEditorLooper->Lock())
1262 		fEditorLooper->Quit();
1263 	fEditorLooper = NULL;
1264 
1265 	fEditor.StopWatching(this);
1266 	fDataView->StopWatching(fHeaderView, kDataViewCursorPosition);
1267 	fDataView->StopWatching(this, kDataViewSelection);
1268 	fDataView->StopWatching(this, kDataViewPreferredSize);
1269 	be_clipboard->StopWatching(this);
1270 }
1271 
1272 
1273 void
1274 ProbeView::_UpdateAttributesMenu(BMenu *menu)
1275 {
1276 	// remove old contents
1277 
1278 	for (int32 i = menu->CountItems(); i-- > 0;) {
1279 		delete menu->RemoveItem(i);
1280 	}
1281 
1282 	// add new items (sorted)
1283 
1284 	BNode node(&fEditor.AttributeRef());
1285 	if (node.InitCheck() == B_OK) {
1286 		char attribute[B_ATTR_NAME_LENGTH];
1287 		node.RewindAttrs();
1288 
1289 		while (node.GetNextAttrName(attribute) == B_OK) {
1290 			attr_info info;
1291 			if (node.GetAttrInfo(attribute, &info) != B_OK)
1292 				continue;
1293 
1294 			char type[16];
1295 			type[0] = '[';
1296 			get_type_string(type + 1, sizeof(type) - 2, info.type);
1297 			strcat(type, "]");
1298 
1299 			// find where to insert
1300 			int32 i;
1301 			for (i = 0; i < menu->CountItems(); i++) {
1302 				if (strcasecmp(menu->ItemAt(i)->Label(), attribute) > 0)
1303 					break;
1304 			}
1305 
1306 			BMessage *message = new BMessage(B_REFS_RECEIVED);
1307 			message->AddRef("refs", &fEditor.AttributeRef());
1308 			message->AddString("attributes", attribute);
1309 
1310 			menu->AddItem(new TypeMenuItem(attribute, type, message), i);
1311 		}
1312 	}
1313 
1314 	if (menu->CountItems() == 0) {
1315 		// if there are no attributes, add an item to the menu
1316 		// that says so
1317 		BMenuItem *item = new BMenuItem(B_TRANSLATE_COMMENT("none",
1318 			"No attributes"), NULL);
1319 		item->SetEnabled(false);
1320 		menu->AddItem(item);
1321 	}
1322 
1323 	menu->SetTargetForItems(be_app);
1324 }
1325 
1326 
1327 void
1328 ProbeView::AddSaveMenuItems(BMenu* menu, int32 index)
1329 {
1330 	menu->AddItem(fSaveMenuItem = new BMenuItem(B_TRANSLATE("Save"),
1331 		new BMessage(B_SAVE_REQUESTED), 'S'), index);
1332 	fSaveMenuItem->SetTarget(this);
1333 	fSaveMenuItem->SetEnabled(false);
1334 	//menu->AddItem(new BMenuItem("Save As" B_UTF8_ELLIPSIS, NULL), index);
1335 }
1336 
1337 
1338 void
1339 ProbeView::AddPrintMenuItems(BMenu* menu, int32 index)
1340 {
1341 	BMenuItem *item;
1342 	menu->AddItem(item = new BMenuItem(B_TRANSLATE("Page setup" B_UTF8_ELLIPSIS),
1343 		new BMessage(kMsgPageSetup)), index++);
1344 	item->SetTarget(this);
1345 	menu->AddItem(item = new BMenuItem(B_TRANSLATE("Print" B_UTF8_ELLIPSIS),
1346 		new BMessage(kMsgPrint), 'P'), index++);
1347 	item->SetTarget(this);
1348 }
1349 
1350 
1351 void
1352 ProbeView::AddViewAsMenuItems()
1353 {
1354 #if 0
1355 	BMenuBar* bar = Window()->KeyMenuBar();
1356 	if (bar == NULL)
1357 		return;
1358 
1359 	BMenuItem* item = bar->FindItem(B_TRANSLATE("View"));
1360 	BMenu* menu = NULL;
1361 	if (item != NULL)
1362 		menu = item->Submenu();
1363 	else
1364 		menu = bar->SubmenuAt(bar->CountItems() - 1);
1365 
1366 	if (menu == NULL)
1367 		return;
1368 
1369 	menu->AddSeparatorItem();
1370 
1371 	BMenu* subMenu = new BMenu(B_TRANSLATE("View As"));
1372 	subMenu->SetRadioMode(true);
1373 
1374 	BMessage* message = new BMessage(kMsgViewAs);
1375 	subMenu->AddItem(item = new BMenuItem(B_TRANSLATE("Raw"), message));
1376 	item->SetMarked(true);
1377 
1378 	const char* name;
1379 	for (int32 i = 0; GetNthTypeEditor(i, &name) == B_OK; i++) {
1380 		message = new BMessage(kMsgViewAs);
1381 		message->AddInt32("editor index", i);
1382 		subMenu->AddItem(new BMenuItem(name, message));
1383 	}
1384 
1385 	subMenu->SetTargetForItems(this);
1386 	menu->AddItem(new BMenuItem(subMenu));
1387 #endif
1388 }
1389 
1390 
1391 void
1392 ProbeView::AttachedToWindow()
1393 {
1394 	fEditorLooper = new EditorLooper(fEditor.Ref().name, fEditor,
1395 		BMessenger(fDataView));
1396 	fEditorLooper->Run();
1397 
1398 	fEditor.StartWatching(this);
1399 	fDataView->StartWatching(fHeaderView, kDataViewCursorPosition);
1400 	fDataView->StartWatching(this, kDataViewSelection);
1401 	fDataView->StartWatching(this, kDataViewPreferredSize);
1402 	be_clipboard->StartWatching(this);
1403 
1404 	// Add menu to window
1405 
1406 	BMenuBar *bar = Window()->KeyMenuBar();
1407 	if (bar == NULL) {
1408 		// there is none? Well, but we really want to have one
1409 		bar = new BMenuBar(BRect(0, 0, 0, 0), NULL);
1410 		Window()->AddChild(bar);
1411 
1412 		MoveBy(0, bar->Bounds().Height());
1413 		ResizeBy(0, -bar->Bounds().Height());
1414 
1415 		BMenu *menu = new BMenu(fEditor.IsAttribute()
1416 			? B_TRANSLATE("Attribute") : fEditor.IsDevice() ? B_TRANSLATE("Device") : B_TRANSLATE("File"));
1417 		AddSaveMenuItems(menu, 0);
1418 		menu->AddSeparatorItem();
1419 		AddPrintMenuItems(menu, menu->CountItems());
1420 		menu->AddSeparatorItem();
1421 
1422 		menu->AddItem(new BMenuItem(B_TRANSLATE("Close"), new BMessage(B_CLOSE_REQUESTED),
1423 			'W'));
1424 		bar->AddItem(menu);
1425 	}
1426 
1427 	// "Edit" menu
1428 
1429 	BMenu *menu = new BMenu(B_TRANSLATE("Edit"));
1430 	BMenuItem *item;
1431 	menu->AddItem(fUndoMenuItem = new BMenuItem(B_TRANSLATE("Undo"), new BMessage(B_UNDO),
1432 		'Z'));
1433 	fUndoMenuItem->SetEnabled(fEditor.CanUndo());
1434 	fUndoMenuItem->SetTarget(fDataView);
1435 	menu->AddItem(fRedoMenuItem = new BMenuItem(B_TRANSLATE("Redo"), new BMessage(B_REDO),
1436 		'Z', B_SHIFT_KEY));
1437 	fRedoMenuItem->SetEnabled(fEditor.CanRedo());
1438 	fRedoMenuItem->SetTarget(fDataView);
1439 	menu->AddSeparatorItem();
1440 	menu->AddItem(item = new BMenuItem(B_TRANSLATE("Copy"), new BMessage(B_COPY), 'C'));
1441 	item->SetTarget(NULL, Window());
1442 	menu->AddItem(fPasteMenuItem = new BMenuItem(B_TRANSLATE("Paste"), new BMessage(B_PASTE),
1443 		'V'));
1444 	fPasteMenuItem->SetTarget(NULL, Window());
1445 	_CheckClipboard();
1446 	menu->AddItem(item = new BMenuItem(B_TRANSLATE("Select all"), new BMessage(B_SELECT_ALL),
1447 		'A'));
1448 	item->SetTarget(NULL, Window());
1449 	menu->AddSeparatorItem();
1450 	menu->AddItem(item = new BMenuItem(B_TRANSLATE("Find" B_UTF8_ELLIPSIS),
1451 		new BMessage(kMsgOpenFindWindow), 'F'));
1452 	item->SetTarget(this);
1453 	menu->AddItem(fFindAgainMenuItem = new BMenuItem(B_TRANSLATE("Find again"),
1454 		new BMessage(kMsgFind), 'G'));
1455 	fFindAgainMenuItem->SetEnabled(false);
1456 	fFindAgainMenuItem->SetTarget(this);
1457 	bar->AddItem(menu);
1458 
1459 	// "Block" menu
1460 
1461 	menu = new BMenu(B_TRANSLATE("Block"));
1462 	BMessage *message = new BMessage(kMsgPositionUpdate);
1463 	message->AddInt32("delta", 1);
1464 	menu->AddItem(item = new BMenuItem(B_TRANSLATE("Next"), message, B_RIGHT_ARROW));
1465 	item->SetTarget(fHeaderView);
1466 	message = new BMessage(kMsgPositionUpdate);
1467 	message->AddInt32("delta", -1);
1468 	menu->AddItem(item = new BMenuItem(B_TRANSLATE("Previous"), message, B_LEFT_ARROW));
1469 	item->SetTarget(fHeaderView);
1470 	menu->AddItem(item = new BMenuItem(B_TRANSLATE("Back"), new BMessage(kMsgLastPosition),
1471 		'J'));
1472 	item->SetTarget(fHeaderView);
1473 
1474 	BMenu *subMenu = new BMenu(B_TRANSLATE("Selection"));
1475 	message = new BMessage(kMsgPositionUpdate);
1476 	message->AddInt64("block", 0);
1477 	subMenu->AddItem(fNativeMenuItem = new BMenuItem("", message, 'K'));
1478 	fNativeMenuItem->SetTarget(fHeaderView);
1479 	message = new BMessage(*message);
1480 	subMenu->AddItem(fSwappedMenuItem = new BMenuItem("", message, 'L'));
1481 	fSwappedMenuItem->SetTarget(fHeaderView);
1482 	menu->AddItem(new BMenuItem(subMenu));
1483 	_UpdateSelectionMenuItems(0, 0);
1484 	menu->AddSeparatorItem();
1485 
1486 	fBookmarkMenu = new BMenu(B_TRANSLATE("Bookmarks"));
1487 	fBookmarkMenu->AddItem(item = new BMenuItem(B_TRANSLATE("Add"),
1488 		new BMessage(kMsgAddBookmark), 'B'));
1489 	item->SetTarget(this);
1490 	menu->AddItem(new BMenuItem(fBookmarkMenu));
1491 	bar->AddItem(menu);
1492 
1493 	// "Attributes" menu (it's only visible if the underlying
1494 	// file system actually supports attributes)
1495 
1496 	BDirectory directory;
1497 	BVolume volume;
1498 	if (directory.SetTo(&fEditor.AttributeRef()) == B_OK
1499 		&& directory.IsRootDirectory())
1500 		directory.GetVolume(&volume);
1501 	else
1502 		fEditor.File().GetVolume(&volume);
1503 
1504 	if (!fEditor.IsAttribute() && volume.InitCheck() == B_OK
1505 		&& (volume.KnowsMime() || volume.KnowsAttr())) {
1506 		bar->AddItem(menu = new BMenu(B_TRANSLATE("Attributes")));
1507 		_UpdateAttributesMenu(menu);
1508 	}
1509 
1510 	// "View" menu
1511 
1512 	menu = new BMenu(B_TRANSLATE_COMMENT("View",
1513 		"This is the last menubar item 'File Edit Block View'"));
1514 
1515 	// Number Base (hex/decimal)
1516 
1517 	subMenu = new BMenu(B_TRANSLATE_COMMENT("Base", "A menu item, the number "
1518 		"that is basis for a system of calculation. The base 10 system is a "
1519 		"decimal system. This is in the same menu window than 'Font size' "
1520 		"and 'BlockSize'"));
1521 	message = new BMessage(kMsgBaseType);
1522 	message->AddInt32("base_type", kDecimalBase);
1523 	subMenu->AddItem(item = new BMenuItem(B_TRANSLATE_COMMENT("Decimal",
1524 		"A menu item, as short as possible, noun is recommended if it is "
1525 		"shorter than adjective."), message, 'D'));
1526 	item->SetTarget(this);
1527 	if (fHeaderView->Base() == kDecimalBase)
1528 		item->SetMarked(true);
1529 
1530 	message = new BMessage(kMsgBaseType);
1531 	message->AddInt32("base_type", kHexBase);
1532 	subMenu->AddItem(item = new BMenuItem(B_TRANSLATE_COMMENT("Hex",
1533 		"A menu item, as short as possible, noun is recommended if it is "
1534 		"shorter than adjective."), message, 'H'));
1535 	item->SetTarget(this);
1536 	if (fHeaderView->Base() == kHexBase)
1537 		item->SetMarked(true);
1538 
1539 	subMenu->SetRadioMode(true);
1540 	menu->AddItem(new BMenuItem(subMenu));
1541 
1542 	// Block Size
1543 
1544 	subMenu = new BMenu(B_TRANSLATE_COMMENT("BlockSize", "A menu item, a "
1545 		"shortened form from 'block size'. This is in the same menu window"
1546 		"than 'Base' and 'Font size'"));
1547 	subMenu->SetRadioMode(true);
1548 	const uint32 blockSizes[] = {512, 1024, 2048};
1549 	for (uint32 i = 0; i < sizeof(blockSizes) / sizeof(blockSizes[0]); i++) {
1550 		char buffer[32];
1551 		snprintf(buffer, sizeof(buffer), "%ld%s", blockSizes[i],
1552 			fEditor.IsDevice() && fEditor.BlockSize() == blockSizes[i]
1553 			? B_TRANSLATE(" (native)") : "");
1554 		subMenu->AddItem(item = new BMenuItem(buffer,
1555 			message = new BMessage(kMsgBlockSize)));
1556 		message->AddInt32("block_size", blockSizes[i]);
1557 		if (fEditor.BlockSize() == blockSizes[i])
1558 			item->SetMarked(true);
1559 	}
1560 	if (subMenu->FindMarked() == NULL) {
1561 		// if the device has some weird block size, we'll add it here, too
1562 		char buffer[32];
1563 		snprintf(buffer, sizeof(buffer), B_TRANSLATE("%ld (native)"), fEditor.BlockSize());
1564 		subMenu->AddItem(item = new BMenuItem(buffer,
1565 			message = new BMessage(kMsgBlockSize)));
1566 		message->AddInt32("block_size", fEditor.BlockSize());
1567 		item->SetMarked(true);
1568 	}
1569 	subMenu->SetTargetForItems(this);
1570 	menu->AddItem(new BMenuItem(subMenu));
1571 	menu->AddSeparatorItem();
1572 
1573 	// Font Size
1574 
1575 	subMenu = new BMenu(B_TRANSLATE("Font size"));
1576 	subMenu->SetRadioMode(true);
1577 	const int32 fontSizes[] = {9, 10, 11, 12, 13, 14, 18, 24, 36, 48};
1578 	int32 fontSize = int32(fDataView->FontSize() + 0.5);
1579 	if (fDataView->FontSizeFitsBounds())
1580 		fontSize = 0;
1581 	for (uint32 i = 0; i < sizeof(fontSizes) / sizeof(fontSizes[0]); i++) {
1582 		char buffer[16];
1583 		snprintf(buffer, sizeof(buffer), "%ld", fontSizes[i]);
1584 		subMenu->AddItem(item = new BMenuItem(buffer,
1585 			message = new BMessage(kMsgFontSize)));
1586 		message->AddFloat("font_size", fontSizes[i]);
1587 		if (fontSizes[i] == fontSize)
1588 			item->SetMarked(true);
1589 	}
1590 	subMenu->AddSeparatorItem();
1591 	subMenu->AddItem(item = new BMenuItem(B_TRANSLATE_COMMENT("Fit",
1592 		"Size of fonts, fits to available room"),
1593 		message = new BMessage(kMsgFontSize)));
1594 	message->AddFloat("font_size", 0.0f);
1595 	if (fontSize == 0)
1596 		item->SetMarked(true);
1597 
1598 	subMenu->SetTargetForItems(this);
1599 	menu->AddItem(new BMenuItem(subMenu));
1600 
1601 	bar->AddItem(menu);
1602 }
1603 
1604 
1605 void
1606 ProbeView::AllAttached()
1607 {
1608 	fHeaderView->SetTarget(fEditorLooper);
1609 }
1610 
1611 
1612 void
1613 ProbeView::WindowActivated(bool active)
1614 {
1615 	if (!active)
1616 		return;
1617 
1618 	fDataView->MakeFocus(true);
1619 
1620 	// set this view as the current find panel's target
1621 	BMessage target(kMsgFindTarget);
1622 	target.AddMessenger("target", this);
1623 	be_app_messenger.SendMessage(&target);
1624 }
1625 
1626 
1627 void
1628 ProbeView::_UpdateSelectionMenuItems(int64 start, int64 end)
1629 {
1630 	int64 position = 0;
1631 	const uint8 *data = fDataView->DataAt(start);
1632 	if (data == NULL) {
1633 		fNativeMenuItem->SetEnabled(false);
1634 		fSwappedMenuItem->SetEnabled(false);
1635 		return;
1636 	}
1637 
1638 	// retrieve native endian position
1639 
1640 	int size;
1641 	if (end < start + 8)
1642 		size = end + 1 - start;
1643 	else
1644 		size = 8;
1645 
1646 	int64 bigEndianPosition = 0;
1647 	memcpy(&bigEndianPosition, data, size);
1648 
1649 	position = B_BENDIAN_TO_HOST_INT64(bigEndianPosition) >> (8 * (8 - size));
1650 
1651 	// update menu items
1652 
1653 	char buffer[128];
1654 	if (fDataView->Base() == kHexBase)
1655 		snprintf(buffer, sizeof(buffer), B_TRANSLATE("Native: 0x%0*Lx"), size * 2, position);
1656 	else
1657 		snprintf(buffer, sizeof(buffer), B_TRANSLATE("Native: %Ld (0x%0*Lx)"), position, size * 2, position);
1658 
1659 	fNativeMenuItem->SetLabel(buffer);
1660 	fNativeMenuItem->SetEnabled(position >= 0 && (position * fEditor.BlockSize()) < fEditor.FileSize());
1661 	fNativeMenuItem->Message()->ReplaceInt64("block", position);
1662 
1663 	position = B_SWAP_INT64(position) >> (8 * (8 - size));
1664 	if (fDataView->Base() == kHexBase)
1665 		snprintf(buffer, sizeof(buffer), B_TRANSLATE("Swapped: 0x%0*Lx"), size * 2, position);
1666 	else
1667 		snprintf(buffer, sizeof(buffer), B_TRANSLATE("Swapped: %Ld (0x%0*Lx)"), position, size * 2, position);
1668 
1669 	fSwappedMenuItem->SetLabel(buffer);
1670 	fSwappedMenuItem->SetEnabled(position >= 0 && (position * fEditor.BlockSize()) < fEditor.FileSize());
1671 	fSwappedMenuItem->Message()->ReplaceInt64("block", position);
1672 }
1673 
1674 
1675 void
1676 ProbeView::_UpdateBookmarkMenuItems()
1677 {
1678 	for (int32 i = 2; i < fBookmarkMenu->CountItems(); i++) {
1679 		BMenuItem *item = fBookmarkMenu->ItemAt(i);
1680 		if (item == NULL)
1681 			break;
1682 
1683 		BMessage *message = item->Message();
1684 		if (message == NULL)
1685 			break;
1686 
1687 		off_t block = message->FindInt64("block");
1688 
1689 		char buffer[128];
1690 		if (fDataView->Base() == kHexBase)
1691 			snprintf(buffer, sizeof(buffer), B_TRANSLATE("Block 0x%Lx"), block);
1692 		else
1693 			snprintf(buffer, sizeof(buffer), B_TRANSLATE("Block %Ld (0x%Lx)"), block, block);
1694 
1695 		item->SetLabel(buffer);
1696 	}
1697 }
1698 
1699 
1700 void
1701 ProbeView::_AddBookmark(off_t position)
1702 {
1703 	int32 count = fBookmarkMenu->CountItems();
1704 
1705 	if (count == 1) {
1706 		fBookmarkMenu->AddSeparatorItem();
1707 		count++;
1708 	}
1709 
1710 	// insert current position as bookmark
1711 
1712 	off_t block = position / fEditor.BlockSize();
1713 
1714 	off_t bookmark = -1;
1715 	BMenuItem *item;
1716 	int32 i;
1717 	for (i = 2; (item = fBookmarkMenu->ItemAt(i)) != NULL; i++) {
1718 		BMessage *message = item->Message();
1719 		if (message != NULL && message->FindInt64("block", &bookmark) == B_OK) {
1720 			if (block <= bookmark)
1721 				break;
1722 		}
1723 	}
1724 
1725 	// the bookmark already exists
1726 	if (block == bookmark)
1727 		return;
1728 
1729 	char buffer[128];
1730 	if (fDataView->Base() == kHexBase)
1731 		snprintf(buffer, sizeof(buffer), B_TRANSLATE("Block 0x%Lx"), block);
1732 	else
1733 		snprintf(buffer, sizeof(buffer), B_TRANSLATE("Block %Ld (0x%Lx)"), block, block);
1734 
1735 	BMessage *message;
1736 	item = new BMenuItem(buffer, message = new BMessage(kMsgPositionUpdate));
1737 	item->SetTarget(fHeaderView);
1738 	if (count < 12)
1739 		item->SetShortcut('0' + count - 2, B_COMMAND_KEY);
1740 	message->AddInt64("block", block);
1741 
1742 	fBookmarkMenu->AddItem(item, i);
1743 }
1744 
1745 
1746 void
1747 ProbeView::_RemoveTypeEditor()
1748 {
1749 	if (fTypeView == NULL)
1750 		return;
1751 
1752 	if (Parent() != NULL)
1753 		Parent()->RemoveChild(fTypeView);
1754 	else
1755 		Window()->RemoveChild(fTypeView);
1756 
1757 	delete fTypeView;
1758 	fTypeView = NULL;
1759 }
1760 
1761 
1762 void
1763 ProbeView::_SetTypeEditor(int32 index)
1764 {
1765 	if (index == -1) {
1766 		// remove type editor, show raw editor
1767 		if (IsHidden())
1768 			Show();
1769 
1770 		_RemoveTypeEditor();
1771 	} else {
1772 		// hide raw editor, create and show type editor
1773 		if (!IsHidden())
1774 			Hide();
1775 
1776 		_RemoveTypeEditor();
1777 
1778 		fTypeView = new TypeView(Frame(), "type shell", index, fEditor,
1779 			B_FOLLOW_ALL);
1780 
1781 		if (Parent() != NULL)
1782 			Parent()->AddChild(fTypeView);
1783 		else
1784 			Window()->AddChild(fTypeView);
1785 	}
1786 }
1787 
1788 
1789 void
1790 ProbeView::_CheckClipboard()
1791 {
1792 	if (!be_clipboard->Lock())
1793 		return;
1794 
1795 	bool hasData = false;
1796 	BMessage *clip;
1797 	if ((clip = be_clipboard->Data()) != NULL) {
1798 		const void *data;
1799 		ssize_t size;
1800 		if (clip->FindData(B_FILE_MIME_TYPE, B_MIME_TYPE, &data, &size) == B_OK
1801 			|| clip->FindData("text/plain", B_MIME_TYPE, &data, &size) == B_OK)
1802 			hasData = true;
1803 	}
1804 
1805 	be_clipboard->Unlock();
1806 
1807 	fPasteMenuItem->SetEnabled(hasData);
1808 }
1809 
1810 
1811 status_t
1812 ProbeView::_PageSetup()
1813 {
1814 	BPrintJob printJob(Window()->Title());
1815 	if (fPrintSettings != NULL)
1816 		printJob.SetSettings(new BMessage(*fPrintSettings));
1817 
1818 	status_t status = printJob.ConfigPage();
1819 	if (status == B_OK) {
1820 		// replace the print settings on success
1821 		delete fPrintSettings;
1822 		fPrintSettings = printJob.Settings();
1823 	}
1824 
1825 	return status;
1826 }
1827 
1828 
1829 void
1830 ProbeView::_Print()
1831 {
1832 	if (fPrintSettings == NULL && _PageSetup() != B_OK)
1833 		return;
1834 
1835 	BPrintJob printJob(Window()->Title());
1836 	printJob.SetSettings(new BMessage(*fPrintSettings));
1837 
1838 	if (printJob.ConfigJob() == B_OK) {
1839 		BRect rect = printJob.PrintableRect();
1840 
1841 		float width, height;
1842 		fDataView->GetPreferredSize(&width, &height);
1843 
1844 		printJob.BeginJob();
1845 
1846 		fDataView->SetScale(rect.Width() / width);
1847 		printJob.DrawView(fDataView, rect, rect.LeftTop());
1848 		fDataView->SetScale(1.0);
1849 		printJob.SpoolPage();
1850 
1851 		printJob.CommitJob();
1852 	}
1853 }
1854 
1855 
1856 status_t
1857 ProbeView::_Save()
1858 {
1859 	status_t status = fEditor.Save();
1860 	if (status == B_OK)
1861 		return B_OK;
1862 
1863 	char buffer[1024];
1864 	snprintf(buffer, sizeof(buffer),
1865 		B_TRANSLATE("Writing to the file failed:\n"
1866 		"%s\n\n"
1867 		"All changes will be lost when you quit."),
1868 		strerror(status));
1869 
1870 	(new BAlert(B_TRANSLATE("DiskProbe request"),
1871 		buffer, B_TRANSLATE("OK"), NULL, NULL,
1872 		B_WIDTH_AS_USUAL, B_WARNING_ALERT))->Go(NULL);
1873 
1874 	return status;
1875 }
1876 
1877 
1878 bool
1879 ProbeView::QuitRequested()
1880 {
1881 	fEditorLooper->QuitFind();
1882 
1883 	if (!fEditor.IsModified())
1884 		return true;
1885 
1886 	int32 chosen = (new BAlert(B_TRANSLATE("DiskProbe request"),
1887 		B_TRANSLATE("Save changes before closing?"), B_TRANSLATE("Don't save"), B_TRANSLATE("Cancel"),
1888 		B_TRANSLATE("Save"),	B_WIDTH_AS_USUAL, B_WARNING_ALERT))->Go();
1889 
1890 	if (chosen == 0)
1891 		return true;
1892 	if (chosen == 1)
1893 		return false;
1894 
1895 	return _Save() == B_OK;
1896 }
1897 
1898 
1899 void
1900 ProbeView::MessageReceived(BMessage *message)
1901 {
1902 	switch (message->what) {
1903 		case B_SAVE_REQUESTED:
1904 			_Save();
1905 			break;
1906 
1907 		case B_OBSERVER_NOTICE_CHANGE: {
1908 			int32 what;
1909 			if (message->FindInt32(B_OBSERVE_WHAT_CHANGE, &what) != B_OK)
1910 				break;
1911 
1912 			switch (what) {
1913 				case kDataViewSelection:
1914 				{
1915 					int64 start, end;
1916 					if (message->FindInt64("start", &start) == B_OK
1917 						&& message->FindInt64("end", &end) == B_OK)
1918 						_UpdateSelectionMenuItems(start, end);
1919 					break;
1920 				}
1921 				case kDataViewPreferredSize:
1922 					UpdateSizeLimits();
1923 					break;
1924 			}
1925 			break;
1926 		}
1927 
1928 		case kMsgBaseType:
1929 		{
1930 			int32 type;
1931 			if (message->FindInt32("base_type", &type) != B_OK)
1932 				break;
1933 
1934 			fHeaderView->SetBase((base_type)type);
1935 			fDataView->SetBase((base_type)type);
1936 
1937 			// The selection menu items depend on the base type as well
1938 			int32 start, end;
1939 			fDataView->GetSelection(start, end);
1940 			_UpdateSelectionMenuItems(start, end);
1941 
1942 			_UpdateBookmarkMenuItems();
1943 
1944 			// update the application's settings
1945 			BMessage update(*message);
1946 			update.what = kMsgSettingsChanged;
1947 			be_app_messenger.SendMessage(&update);
1948 			break;
1949 		}
1950 
1951 		case kMsgFontSize:
1952 		{
1953 			float size;
1954 			if (message->FindFloat("font_size", &size) != B_OK)
1955 				break;
1956 
1957 			fDataView->SetFontSize(size);
1958 
1959 			// update the application's settings
1960 			BMessage update(*message);
1961 			update.what = kMsgSettingsChanged;
1962 			be_app_messenger.SendMessage(&update);
1963 			break;
1964 		}
1965 
1966 		case kMsgBlockSize:
1967 		{
1968 			int32 blockSize;
1969 			if (message->FindInt32("block_size", &blockSize) != B_OK)
1970 				break;
1971 
1972 			BAutolock locker(fEditor);
1973 
1974 			if (fEditor.SetViewSize(blockSize) == B_OK
1975 				&& fEditor.SetBlockSize(blockSize) == B_OK)
1976 				fHeaderView->SetTo(fEditor.ViewOffset(), blockSize);
1977 			break;
1978 		}
1979 
1980 		case kMsgViewAs:
1981 		{
1982 			int32 index;
1983 			if (message->FindInt32("editor index", &index) != B_OK)
1984 				index = -1;
1985 
1986 			_SetTypeEditor(index);
1987 			break;
1988 		}
1989 
1990 		case kMsgAddBookmark:
1991 			_AddBookmark(fHeaderView->Position());
1992 			break;
1993 
1994 		case kMsgPrint:
1995 			_Print();
1996 			break;
1997 
1998 		case kMsgPageSetup:
1999 			_PageSetup();
2000 			break;
2001 
2002 		case kMsgOpenFindWindow:
2003 		{
2004 			fEditorLooper->QuitFind();
2005 
2006 			// set this view as the current find panel's target
2007 			BMessage find(*fFindAgainMenuItem->Message());
2008 			find.what = kMsgOpenFindWindow;
2009 			find.AddMessenger("target", this);
2010 			be_app_messenger.SendMessage(&find);
2011 			break;
2012 		}
2013 
2014 		case kMsgFind:
2015 		{
2016 			const uint8 *data;
2017 			ssize_t size;
2018 			if (message->FindData("data", B_RAW_TYPE, (const void **)&data, &size) != B_OK) {
2019 				// search again for last pattern
2020 				BMessage *itemMessage = fFindAgainMenuItem->Message();
2021 				if (itemMessage == NULL
2022 					|| itemMessage->FindData("data", B_RAW_TYPE, (const void **)&data, &size) != B_OK) {
2023 					// this shouldn't ever happen, but well...
2024 					beep();
2025 					break;
2026 				}
2027 			} else {
2028 				// remember the search pattern
2029 				fFindAgainMenuItem->SetMessage(new BMessage(*message));
2030 				fFindAgainMenuItem->SetEnabled(true);
2031 			}
2032 
2033 			int32 start, end;
2034 			fDataView->GetSelection(start, end);
2035 
2036 			BMessage find(*message);
2037 			find.AddInt64("start", fHeaderView->Position() + start + 1);
2038 			find.AddMessenger("progress_monitor", BMessenger(fHeaderView));
2039 			fEditorLooper->PostMessage(&find);
2040 			break;
2041 		}
2042 
2043 		case kMsgStopFind:
2044 			fEditorLooper->QuitFind();
2045 			break;
2046 
2047 		case B_NODE_MONITOR:
2048 		{
2049 			switch (message->FindInt32("opcode")) {
2050 				case B_STAT_CHANGED:
2051 					fEditor.ForceUpdate();
2052 					break;
2053 				case B_ATTR_CHANGED:
2054 				{
2055 					const char *name;
2056 					if (message->FindString("attr", &name) != B_OK)
2057 						break;
2058 
2059 					if (fEditor.IsAttribute()) {
2060 						if (!strcmp(name, fEditor.Attribute()))
2061 							fEditor.ForceUpdate();
2062 					} else {
2063 						BMenuBar *bar = Window()->KeyMenuBar();
2064 						if (bar != NULL) {
2065 							BMenuItem *item = bar->FindItem("Attributes");
2066 							if (item != NULL && item->Submenu() != NULL)
2067 								_UpdateAttributesMenu(item->Submenu());
2068 						}
2069 					}
2070 
2071 					// There might be a new icon
2072 					if (!strcmp(name, "BEOS:TYPE")
2073 						|| !strcmp(name, "BEOS:M:STD_ICON")
2074 						|| !strcmp(name, "BEOS:L:STD_ICON")
2075 						|| !strcmp(name, "BEOS:ICON"))
2076 						fHeaderView->UpdateIcon();
2077 					break;
2078 				}
2079 			}
2080 			break;
2081 		}
2082 
2083 		case B_CLIPBOARD_CHANGED:
2084 			_CheckClipboard();
2085 			break;
2086 
2087 		case kMsgDataEditorStateChange:
2088 		{
2089 			bool enabled;
2090 			if (message->FindBool("can_undo", &enabled) == B_OK)
2091 				fUndoMenuItem->SetEnabled(enabled);
2092 
2093 			if (message->FindBool("can_redo", &enabled) == B_OK)
2094 				fRedoMenuItem->SetEnabled(enabled);
2095 
2096 			if (message->FindBool("modified", &enabled) == B_OK)
2097 				fSaveMenuItem->SetEnabled(enabled);
2098 			break;
2099 		}
2100 
2101 		default:
2102 			BView::MessageReceived(message);
2103 	}
2104 }
2105 
2106