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