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