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