xref: /haiku/src/apps/diskprobe/TypeEditors.cpp (revision 3961af9fe94e645328eb4b510e5daea351b1ceae)
1 /*
2  * Copyright 2004-2008, Axel Dörfler, axeld@pinc-software.de. All rights reserved.
3  * Distributed under the terms of the MIT License.
4  */
5 
6 
7 #include "TypeEditors.h"
8 #include "DataEditor.h"
9 
10 #include <stdio.h>
11 #include <stdlib.h>
12 
13 #include <Alert.h>
14 #include <Autolock.h>
15 #include <Bitmap.h>
16 #include <Catalog.h>
17 #include <IconUtils.h>
18 #include <LayoutBuilder.h>
19 #include <Locale.h>
20 #include <MenuField.h>
21 #include <MenuItem.h>
22 #include <Mime.h>
23 #include <PopUpMenu.h>
24 #include <ScrollBar.h>
25 #include <ScrollView.h>
26 #include <Slider.h>
27 #include <String.h>
28 #include <StringView.h>
29 #include <TextControl.h>
30 #include <TextView.h>
31 #include <TranslationUtils.h>
32 #include <TranslatorFormats.h>
33 
34 
35 #undef B_TRANSLATION_CONTEXT
36 #define B_TRANSLATION_CONTEXT "TypeEditors"
37 
38 static const uint32 kMsgValueChanged = 'vlch';
39 static const uint32 kMsgScaleChanged = 'scch';
40 static const uint32 kMimeTypeItem = 'miti';
41 
42 
43 class StringEditor : public TypeEditorView {
44 	public:
45 		StringEditor(DataEditor& editor);
46 
47 		virtual void AttachedToWindow();
48 		virtual void DetachedFromWindow();
49 		virtual void MessageReceived(BMessage* message);
50 
51 		virtual void CommitChanges();
52 
53 	private:
54 		void _UpdateText();
55 
56 		BTextView*		fTextView;
57 		BString			fPreviousText;
58 };
59 
60 
61 class MimeTypeEditor : public TypeEditorView {
62 	public:
63 		MimeTypeEditor(BRect rect, DataEditor& editor);
64 
65 		virtual void AttachedToWindow();
66 		virtual void DetachedFromWindow();
67 		virtual void MessageReceived(BMessage* message);
68 
69 		virtual void CommitChanges();
70 		virtual bool TypeMatches();
71 
72 	private:
73 		void _UpdateText();
74 
75 		BTextControl*	fTextControl;
76 		BString			fPreviousText;
77 };
78 
79 
80 class NumberEditor : public TypeEditorView {
81 	public:
82 		NumberEditor(BRect rect, DataEditor& editor);
83 
84 		virtual void AttachedToWindow();
85 		virtual void DetachedFromWindow();
86 		virtual void MessageReceived(BMessage* message);
87 
88 		virtual void CommitChanges();
89 		virtual bool TypeMatches();
90 
91 	private:
92 		void _UpdateText();
93 		const char* _TypeLabel();
94 		status_t _Format(char* buffer);
95 		size_t _Size();
96 
97 		BTextControl*	fTextControl;
98 		BString			fPreviousText;
99 };
100 
101 
102 class BooleanEditor : public TypeEditorView {
103 	public:
104 		BooleanEditor(BRect rect, DataEditor& editor);
105 
106 		virtual void AttachedToWindow();
107 		virtual void DetachedFromWindow();
108 		virtual void MessageReceived(BMessage* message);
109 
110 		virtual void CommitChanges();
111 		virtual bool TypeMatches();
112 
113 	private:
114 		void _UpdateMenuField();
115 
116 		BMenuItem*		fFalseMenuItem;
117 		BMenuItem*		fTrueMenuItem;
118 };
119 
120 
121 class ImageView : public TypeEditorView {
122 	public:
123 		ImageView(DataEditor &editor);
124 		virtual ~ImageView();
125 
126 		virtual void AttachedToWindow();
127 		virtual void DetachedFromWindow();
128 		virtual void MessageReceived(BMessage *message);
129 		virtual void Draw(BRect updateRect);
130 
131 	private:
132 		void _UpdateImage();
133 
134 		BBitmap*		fBitmap;
135 		BStringView*	fDescriptionView;
136 		BSlider*		fScaleSlider;
137 };
138 
139 
140 class MessageView : public TypeEditorView {
141 	public:
142 		MessageView(BRect rect, DataEditor& editor);
143 		virtual ~MessageView();
144 
145 		virtual void AttachedToWindow();
146 		virtual void DetachedFromWindow();
147 		virtual void MessageReceived(BMessage* message);
148 
149 		void SetTo(BMessage& message);
150 
151 	private:
152 		BString _TypeToString(type_code type);
153 		void _UpdateMessage();
154 
155 		BTextView*		fTextView;
156 };
157 
158 
159 //	#pragma mark - TypeEditorView
160 
161 
TypeEditorView(BRect rect,const char * name,uint32 resizingMode,uint32 flags,DataEditor & editor)162 TypeEditorView::TypeEditorView(BRect rect, const char *name,
163 		uint32 resizingMode, uint32 flags, DataEditor& editor)
164 	: BView(rect, name, resizingMode, flags),
165 	fEditor(editor)
166 {
167 }
168 
169 
TypeEditorView(const char * name,uint32 flags,DataEditor & editor)170 TypeEditorView::TypeEditorView(const char *name, uint32 flags,
171 		DataEditor& editor)
172 	: BView(name, flags),
173 	fEditor(editor)
174 {
175 }
176 
177 
~TypeEditorView()178 TypeEditorView::~TypeEditorView()
179 {
180 }
181 
182 
183 void
CommitChanges()184 TypeEditorView::CommitChanges()
185 {
186 	// the default just does nothing here
187 }
188 
189 
190 bool
TypeMatches()191 TypeEditorView::TypeMatches()
192 {
193 	// the default is to accept anything that easily fits into memory
194 
195 	system_info info;
196 	get_system_info(&info);
197 
198 	return uint64(fEditor.FileSize()) / B_PAGE_SIZE < info.max_pages / 2;
199 }
200 
201 
202 //	#pragma mark - StringEditor
203 
204 
StringEditor(DataEditor & editor)205 StringEditor::StringEditor(DataEditor& editor)
206 	: TypeEditorView(B_TRANSLATE("String editor"), 0, editor)
207 {
208 	SetViewUIColor(B_PANEL_BACKGROUND_COLOR);
209 
210 	BStringView *stringView = new BStringView(B_EMPTY_STRING,
211 		B_TRANSLATE("Contents:"));
212 
213 	fTextView = new BTextView(B_EMPTY_STRING, B_WILL_DRAW);
214 	BScrollView* scrollView = new BScrollView("scroller", fTextView, 0, true, true);
215 
216 	BLayoutBuilder::Group<>(this, B_VERTICAL)
217 		.Add(stringView)
218 		.Add(scrollView)
219 	.End();
220 }
221 
222 
223 void
_UpdateText()224 StringEditor::_UpdateText()
225 {
226 	BAutolock locker(fEditor);
227 
228 	size_t viewSize = fEditor.ViewSize();
229 	// that may need some more memory...
230 	if ((off_t)viewSize < fEditor.FileSize())
231 		fEditor.SetViewSize(fEditor.FileSize());
232 
233 	const char *buffer;
234 	if (fEditor.GetViewBuffer((const uint8 **)&buffer) == B_OK) {
235 		fTextView->SetText(buffer);
236 		fPreviousText.SetTo(buffer);
237 	}
238 
239 	// restore old view size
240 	fEditor.SetViewSize(viewSize);
241 }
242 
243 
244 void
CommitChanges()245 StringEditor::CommitChanges()
246 {
247 	if (fPreviousText != fTextView->Text()) {
248 		fEditor.Replace(0, (const uint8 *)fTextView->Text(),
249 			fTextView->TextLength() + 1);
250 	}
251 }
252 
253 
254 void
AttachedToWindow()255 StringEditor::AttachedToWindow()
256 {
257 	fEditor.StartWatching(this);
258 
259 	_UpdateText();
260 }
261 
262 
263 void
DetachedFromWindow()264 StringEditor::DetachedFromWindow()
265 {
266 	fEditor.StopWatching(this);
267 
268 	CommitChanges();
269 }
270 
271 
272 void
MessageReceived(BMessage * message)273 StringEditor::MessageReceived(BMessage *message)
274 {
275 	BView::MessageReceived(message);
276 }
277 
278 
279 //	#pragma mark - MimeTypeEditor
280 
281 
MimeTypeEditor(BRect rect,DataEditor & editor)282 MimeTypeEditor::MimeTypeEditor(BRect rect, DataEditor& editor)
283 	: TypeEditorView(rect, B_TRANSLATE("MIME type editor"), B_FOLLOW_LEFT_RIGHT, 0, editor)
284 {
285 	SetViewUIColor(B_PANEL_BACKGROUND_COLOR);
286 
287 	fTextControl = new BTextControl(rect.InsetByCopy(5, 5), B_EMPTY_STRING,
288 		B_TRANSLATE("MIME type:"), NULL, new BMessage(kMsgValueChanged), B_FOLLOW_ALL);
289 	fTextControl->SetDivider(StringWidth(fTextControl->Label()) + 8);
290 
291 	float width, height;
292 	fTextControl->GetPreferredSize(&width, &height);
293 	fTextControl->ResizeTo(rect.Width() - 10, height);
294 
295 	ResizeTo(rect.Width(), height + 10);
296 
297 	AddChild(fTextControl);
298 }
299 
300 
301 void
_UpdateText()302 MimeTypeEditor::_UpdateText()
303 {
304 	BAutolock locker(fEditor);
305 
306 	const char* mimeType;
307 	if (fEditor.GetViewBuffer((const uint8 **)&mimeType) == B_OK) {
308 		fTextControl->SetText(mimeType);
309 		fPreviousText.SetTo(mimeType);
310 	}
311 }
312 
313 
314 void
CommitChanges()315 MimeTypeEditor::CommitChanges()
316 {
317 	if (fPreviousText != fTextControl->Text()) {
318 		fEditor.Replace(0, (const uint8*)fTextControl->Text(),
319 			strlen(fTextControl->Text()) + 1);
320 	}
321 }
322 
323 
324 bool
TypeMatches()325 MimeTypeEditor::TypeMatches()
326 {
327 	// TODO: check contents?
328 	return fEditor.FileSize() <= B_MIME_TYPE_LENGTH;
329 }
330 
331 
332 void
AttachedToWindow()333 MimeTypeEditor::AttachedToWindow()
334 {
335 	fTextControl->SetTarget(this);
336 	fEditor.StartWatching(this);
337 
338 	_UpdateText();
339 }
340 
341 
342 void
DetachedFromWindow()343 MimeTypeEditor::DetachedFromWindow()
344 {
345 	fEditor.StopWatching(this);
346 
347 	CommitChanges();
348 }
349 
350 
351 void
MessageReceived(BMessage * message)352 MimeTypeEditor::MessageReceived(BMessage *message)
353 {
354 	switch (message->what) {
355 		case kMsgValueChanged:
356 			fEditor.Replace(0, (const uint8 *)fTextControl->Text(),
357 				strlen(fTextControl->Text()) + 1);
358 			break;
359 
360 		case kMsgDataEditorUpdate:
361 			_UpdateText();
362 			break;
363 
364 		default:
365 			BView::MessageReceived(message);
366 	}
367 }
368 
369 
370 //	#pragma mark - NumberEditor
371 
372 
NumberEditor(BRect rect,DataEditor & editor)373 NumberEditor::NumberEditor(BRect rect, DataEditor &editor)
374 	: TypeEditorView(rect, B_TRANSLATE("Number editor"), B_FOLLOW_LEFT_RIGHT, 0, editor)
375 {
376 	SetViewUIColor(B_PANEL_BACKGROUND_COLOR);
377 
378 	fTextControl = new BTextControl(rect.InsetByCopy(5, 5), B_EMPTY_STRING,
379 		_TypeLabel(), NULL, new BMessage(kMsgValueChanged), B_FOLLOW_ALL);
380 	fTextControl->SetDivider(StringWidth(fTextControl->Label()) + 8);
381 	fTextControl->SetAlignment(B_ALIGN_RIGHT, B_ALIGN_RIGHT);
382 	ResizeTo(rect.Width(), 30);
383 
384 	AddChild(fTextControl);
385 }
386 
387 
388 void
_UpdateText()389 NumberEditor::_UpdateText()
390 {
391 	if (fEditor.Lock()) {
392 		const char* number;
393 		if (fEditor.GetViewBuffer((const uint8**)&number) == B_OK) {
394 			char buffer[64];
395 			char format[16];
396 			switch (fEditor.Type()) {
397 				case B_FLOAT_TYPE:
398 				{
399 					float value = *(float*)number;
400 					snprintf(buffer, sizeof(buffer), "%g", value);
401 					break;
402 				}
403 				case B_DOUBLE_TYPE:
404 				{
405 					double value = *(double *)number;
406 					snprintf(buffer, sizeof(buffer), "%g", value);
407 					break;
408 				}
409 				case B_SSIZE_T_TYPE:
410 				case B_INT8_TYPE:
411 				case B_INT16_TYPE:
412 				case B_INT32_TYPE:
413 				case B_INT64_TYPE:
414 				case B_OFF_T_TYPE:
415 				{
416 					_Format(format);
417 					switch (_Size()) {
418 						case 1:
419 						{
420 							int8 value = *(int8 *)number;
421 							snprintf(buffer, sizeof(buffer), format, value);
422 							break;
423 						}
424 						case 2:
425 						{
426 							int16 value = *(int16 *)number;
427 							snprintf(buffer, sizeof(buffer), format, value);
428 							break;
429 						}
430 						case 4:
431 						{
432 							int32 value = *(int32 *)number;
433 							snprintf(buffer, sizeof(buffer), format, value);
434 							break;
435 						}
436 						case 8:
437 						{
438 							int64 value = *(int64 *)number;
439 							snprintf(buffer, sizeof(buffer), format, value);
440 							break;
441 						}
442 					}
443 					break;
444 				}
445 
446 				default:
447 				{
448 					_Format(format);
449 					switch (_Size()) {
450 						case 1:
451 						{
452 							uint8 value = *(uint8 *)number;
453 							snprintf(buffer, sizeof(buffer), format, value);
454 							break;
455 						}
456 						case 2:
457 						{
458 							uint16 value = *(uint16 *)number;
459 							snprintf(buffer, sizeof(buffer), format, value);
460 							break;
461 						}
462 						case 4:
463 						{
464 							uint32 value = *(uint32 *)number;
465 							snprintf(buffer, sizeof(buffer), format, value);
466 							break;
467 						}
468 						case 8:
469 						{
470 							uint64 value = *(uint64 *)number;
471 							snprintf(buffer, sizeof(buffer), format, value);
472 							break;
473 						}
474 					}
475 					break;
476 				}
477 			}
478 			fTextControl->SetText(buffer);
479 			fPreviousText.SetTo(buffer);
480 		}
481 
482 		fEditor.Unlock();
483 	}
484 }
485 
486 
487 bool
TypeMatches()488 NumberEditor::TypeMatches()
489 {
490 	return fEditor.FileSize() >= (off_t)_Size();
491 		// we only look at as many bytes we need to
492 }
493 
494 
495 void
CommitChanges()496 NumberEditor::CommitChanges()
497 {
498 	if (fPreviousText == fTextControl->Text())
499 		return;
500 
501 	const char *number = fTextControl->Text();
502 	uint8 buffer[8];
503 
504 	switch (fEditor.Type()) {
505 		case B_FLOAT_TYPE:
506 		{
507 			float value = strtod(number, NULL);
508 			*(float *)buffer = value;
509 			break;
510 		}
511 		case B_DOUBLE_TYPE:
512 		{
513 			double value = strtod(number, NULL);
514 			*(double *)buffer = value;
515 			break;
516 		}
517 		case B_INT8_TYPE:
518 		{
519 			int64 value = strtoll(number, NULL, 0);
520 			if (value > CHAR_MAX)
521 				value = CHAR_MAX;
522 			else if (value < CHAR_MIN)
523 				value = CHAR_MIN;
524 			*(int8 *)buffer = (int8)value;
525 			break;
526 		}
527 		case B_UINT8_TYPE:
528 		{
529 			int64 value = strtoull(number, NULL, 0);
530 			if (value > UCHAR_MAX)
531 				value = UCHAR_MAX;
532 			*(uint8 *)buffer = (uint8)value;
533 			break;
534 		}
535 		case B_INT16_TYPE:
536 		{
537 			int64 value = strtoll(number, NULL, 0);
538 			if (value > SHRT_MAX)
539 				value = SHRT_MAX;
540 			else if (value < SHRT_MIN)
541 				value = SHRT_MIN;
542 			*(int16 *)buffer = (int16)value;
543 			break;
544 		}
545 		case B_UINT16_TYPE:
546 		{
547 			int64 value = strtoull(number, NULL, 0);
548 			if (value > USHRT_MAX)
549 				value = USHRT_MAX;
550 			*(uint16 *)buffer = (uint16)value;
551 			break;
552 		}
553 		case B_INT32_TYPE:
554 		case B_SSIZE_T_TYPE:
555 		{
556 			int64 value = strtoll(number, NULL, 0);
557 			if (value > LONG_MAX)
558 				value = LONG_MAX;
559 			else if (value < LONG_MIN)
560 				value = LONG_MIN;
561 			*(int32 *)buffer = (int32)value;
562 			break;
563 		}
564 		case B_UINT32_TYPE:
565 		case B_SIZE_T_TYPE:
566 		case B_POINTER_TYPE:
567 		{
568 			uint64 value = strtoull(number, NULL, 0);
569 			if (value > ULONG_MAX)
570 				value = ULONG_MAX;
571 			*(uint32 *)buffer = (uint32)value;
572 			break;
573 		}
574 		case B_INT64_TYPE:
575 		case B_OFF_T_TYPE:
576 		{
577 			int64 value = strtoll(number, NULL, 0);
578 			*(int64 *)buffer = value;
579 			break;
580 		}
581 		case B_UINT64_TYPE:
582 		{
583 			uint64 value = strtoull(number, NULL, 0);
584 			*(uint64 *)buffer = value;
585 			break;
586 		}
587 		default:
588 			return;
589 	}
590 
591 	fEditor.Replace(0, buffer, _Size());
592 	fPreviousText.SetTo((char *)buffer);
593 }
594 
595 
596 const char*
_TypeLabel()597 NumberEditor::_TypeLabel()
598 {
599 	switch (fEditor.Type()) {
600 		case B_INT8_TYPE:
601 			return B_TRANSLATE("8 bit signed value:");
602 		case B_UINT8_TYPE:
603 			return B_TRANSLATE("8 bit unsigned value:");
604 		case B_INT16_TYPE:
605 			return B_TRANSLATE("16 bit signed value:");
606 		case B_UINT16_TYPE:
607 			return B_TRANSLATE("16 bit unsigned value:");
608 		case B_INT32_TYPE:
609 			return B_TRANSLATE("32 bit signed value:");
610 		case B_UINT32_TYPE:
611 			return B_TRANSLATE("32 bit unsigned value:");
612 		case B_INT64_TYPE:
613 			return B_TRANSLATE("64 bit signed value:");
614 		case B_UINT64_TYPE:
615 			return B_TRANSLATE("64 bit unsigned value:");
616 		case B_FLOAT_TYPE:
617 			return B_TRANSLATE("Floating-point value:");
618 		case B_DOUBLE_TYPE:
619 			return B_TRANSLATE("Double precision floating-point value:");
620 		case B_SSIZE_T_TYPE:
621 			return B_TRANSLATE("32 bit size or status:");
622 		case B_SIZE_T_TYPE:
623 			return B_TRANSLATE("32 bit unsigned size:");
624 		case B_OFF_T_TYPE:
625 			return B_TRANSLATE("64 bit signed offset:");
626 		case B_POINTER_TYPE:
627 			return B_TRANSLATE("32 bit unsigned pointer:");
628 		default:
629 			return B_TRANSLATE("Number:");
630 	}
631 }
632 
633 
634 size_t
_Size()635 NumberEditor::_Size()
636 {
637 	switch (fEditor.Type()) {
638 		case B_INT8_TYPE:
639 		case B_UINT8_TYPE:
640 			return 1;
641 		case B_INT16_TYPE:
642 		case B_UINT16_TYPE:
643 			return 2;
644 		case B_SSIZE_T_TYPE:
645 		case B_INT32_TYPE:
646 		case B_SIZE_T_TYPE:
647 		case B_POINTER_TYPE:
648 		case B_UINT32_TYPE:
649 			return 4;
650 		case B_INT64_TYPE:
651 		case B_OFF_T_TYPE:
652 		case B_UINT64_TYPE:
653 			return 8;
654 		case B_FLOAT_TYPE:
655 			return 4;
656 		case B_DOUBLE_TYPE:
657 			return 8;
658 
659 		default:
660 			return 0;
661 	}
662 }
663 
664 
665 status_t
_Format(char * buffer)666 NumberEditor::_Format(char *buffer)
667 {
668 	switch (fEditor.Type()) {
669 		case B_INT8_TYPE:
670 			strcpy(buffer, "%hd");
671 			return B_OK;
672 		case B_UINT8_TYPE:
673 			strcpy(buffer, "%hu");
674 			return B_OK;
675 		case B_INT16_TYPE:
676 			strcpy(buffer, "%hd");
677 			return B_OK;
678 		case B_UINT16_TYPE:
679 			strcpy(buffer, "%hu");
680 			return B_OK;
681 		case B_SSIZE_T_TYPE:
682 		case B_INT32_TYPE:
683 			strcpy(buffer, "%ld");
684 			return B_OK;
685 		case B_SIZE_T_TYPE:
686 		case B_POINTER_TYPE:
687 		case B_UINT32_TYPE:
688 			strcpy(buffer, "%lu");
689 			return B_OK;
690 		case B_INT64_TYPE:
691 		case B_OFF_T_TYPE:
692 			strcpy(buffer, "%lld");
693 			return B_OK;
694 		case B_UINT64_TYPE:
695 			strcpy(buffer, "%Lu");
696 			return B_OK;
697 		case B_FLOAT_TYPE:
698 			strcpy(buffer, "%g");
699 			return B_OK;
700 		case B_DOUBLE_TYPE:
701 			strcpy(buffer, "%lg");
702 			return B_OK;
703 
704 		default:
705 			return B_ERROR;
706 	}
707 }
708 
709 
710 void
AttachedToWindow()711 NumberEditor::AttachedToWindow()
712 {
713 	fTextControl->SetTarget(this);
714 	fEditor.StartWatching(this);
715 
716 	_UpdateText();
717 }
718 
719 
720 void
DetachedFromWindow()721 NumberEditor::DetachedFromWindow()
722 {
723 	fEditor.StopWatching(this);
724 
725 	CommitChanges();
726 }
727 
728 
729 void
MessageReceived(BMessage * message)730 NumberEditor::MessageReceived(BMessage *message)
731 {
732 	switch (message->what) {
733 		case kMsgValueChanged:
734 			CommitChanges();
735 			break;
736 		case kMsgDataEditorUpdate:
737 			_UpdateText();
738 			break;
739 
740 		default:
741 			BView::MessageReceived(message);
742 	}
743 }
744 
745 
746 //	#pragma mark - BooleanEditor
747 
748 
BooleanEditor(BRect rect,DataEditor & editor)749 BooleanEditor::BooleanEditor(BRect rect, DataEditor &editor)
750 	: TypeEditorView(rect, B_TRANSLATE("Boolean editor"), B_FOLLOW_NONE, 0, editor)
751 {
752 	SetViewUIColor(B_PANEL_BACKGROUND_COLOR);
753 
754 	BPopUpMenu *menu = new BPopUpMenu("bool");
755 	BMessage *message;
756 	menu->AddItem(fFalseMenuItem = new BMenuItem("false",
757 		new BMessage(kMsgValueChanged)));
758 	menu->AddItem(fTrueMenuItem = new BMenuItem("true",
759 		message = new BMessage(kMsgValueChanged)));
760 	message->AddInt8("value", 1);
761 
762 	BMenuField *menuField = new BMenuField(rect.InsetByCopy(5, 5),
763 		B_EMPTY_STRING, B_TRANSLATE("Boolean value:"), menu, B_FOLLOW_LEFT_RIGHT);
764 	menuField->SetDivider(StringWidth(menuField->Label()) + 8);
765 	menuField->ResizeToPreferred();
766 	ResizeTo(menuField->Bounds().Width() + 10,
767 		menuField->Bounds().Height() + 10);
768 
769 	_UpdateMenuField();
770 
771 	AddChild(menuField);
772 }
773 
774 
775 bool
TypeMatches()776 BooleanEditor::TypeMatches()
777 {
778 	// we accept everything: we just look at the first byte, anyway
779 	return true;
780 }
781 
782 
783 void
_UpdateMenuField()784 BooleanEditor::_UpdateMenuField()
785 {
786 	if (fEditor.FileSize() != 1)
787 		return;
788 
789 	if (fEditor.Lock()) {
790 		const char *buffer;
791 		if (fEditor.GetViewBuffer((const uint8 **)&buffer) == B_OK)
792 			(buffer[0] != 0 ? fTrueMenuItem : fFalseMenuItem)->SetMarked(true);
793 
794 		fEditor.Unlock();
795 	}
796 }
797 
798 
799 void
CommitChanges()800 BooleanEditor::CommitChanges()
801 {
802 	// we're commiting the changes as they happen
803 }
804 
805 
806 void
AttachedToWindow()807 BooleanEditor::AttachedToWindow()
808 {
809 	fTrueMenuItem->SetTarget(this);
810 	fFalseMenuItem->SetTarget(this);
811 
812 	fEditor.StartWatching(this);
813 }
814 
815 
816 void
DetachedFromWindow()817 BooleanEditor::DetachedFromWindow()
818 {
819 	fEditor.StopWatching(this);
820 }
821 
822 
823 void
MessageReceived(BMessage * message)824 BooleanEditor::MessageReceived(BMessage *message)
825 {
826 	switch (message->what) {
827 		case kMsgValueChanged:
828 		{
829 			uint8 boolean = message->FindInt8("value");
830 			fEditor.Replace(0, (const uint8 *)&boolean, 1);
831 			break;
832 		}
833 		case kMsgDataEditorUpdate:
834 			_UpdateMenuField();
835 			break;
836 
837 		default:
838 			BView::MessageReceived(message);
839 	}
840 }
841 
842 
843 //	#pragma mark - ImageView
844 
845 
ImageView(DataEditor & editor)846 ImageView::ImageView(DataEditor &editor)
847 	: TypeEditorView(B_TRANSLATE_COMMENT("Image view", "Image means here a "
848 		"picture file, not a disk image."),
849 		B_WILL_DRAW | B_FULL_UPDATE_ON_RESIZE, editor),
850 	fBitmap(NULL),
851 	fScaleSlider(NULL)
852 {
853 	if (editor.Type() == B_MINI_ICON_TYPE
854 		|| editor.Type() == B_LARGE_ICON_TYPE
855 		|| editor.Type() == B_VECTOR_ICON_TYPE) {
856 		SetName(B_TRANSLATE("Icon view"));
857 	}
858 
859 
860 	fDescriptionView = new BStringView("",
861 		B_TRANSLATE_COMMENT("Could not read image", "Image means "
862 		"here a picture file, not a disk image."));
863 	fDescriptionView->SetAlignment(B_ALIGN_CENTER);
864 
865 	if (editor.Type() == B_VECTOR_ICON_TYPE) {
866 		// vector icon
867 		fScaleSlider = new BSlider("", NULL,
868 			new BMessage(kMsgScaleChanged), 2, 32, B_HORIZONTAL);
869 		fScaleSlider->SetModificationMessage(new BMessage(kMsgScaleChanged));
870 		fScaleSlider->ResizeToPreferred();
871 		fScaleSlider->SetValue(8);
872 		fScaleSlider->SetHashMarks(B_HASH_MARKS_BOTH);
873 		fScaleSlider->SetHashMarkCount(15);
874 
875 		BLayoutBuilder::Group<>(this, B_HORIZONTAL)
876 			.SetInsets(B_USE_WINDOW_SPACING, 256, B_USE_WINDOW_SPACING,
877 				B_H_SCROLL_BAR_HEIGHT) // Leave space for the icon
878 			.AddGlue()
879 			.AddGroup(B_VERTICAL)
880 				.Add(fDescriptionView)
881 				.Add(fScaleSlider)
882 			.End()
883 			.AddGlue();
884 	} else {
885 		BLayoutBuilder::Group<>(this, B_HORIZONTAL)
886 			.SetInsets(B_USE_WINDOW_SPACING, 256, B_USE_WINDOW_SPACING,
887 				B_USE_WINDOW_SPACING) // Leave space for the icon
888 			.AddGlue()
889 			.Add(fDescriptionView)
890 			.AddGlue();
891 	}
892 }
893 
894 
~ImageView()895 ImageView::~ImageView()
896 {
897 	delete fBitmap;
898 }
899 
900 
901 void
AttachedToWindow()902 ImageView::AttachedToWindow()
903 {
904 	SetViewUIColor(B_PANEL_BACKGROUND_COLOR);
905 
906 	fEditor.StartWatching(this);
907 	if (fScaleSlider != NULL)
908 		fScaleSlider->SetTarget(this);
909 
910 	_UpdateImage();
911 }
912 
913 
914 void
DetachedFromWindow()915 ImageView::DetachedFromWindow()
916 {
917 	fEditor.StopWatching(this);
918 }
919 
920 
921 void
MessageReceived(BMessage * message)922 ImageView::MessageReceived(BMessage *message)
923 {
924 	switch (message->what) {
925 		case kMsgDataEditorUpdate:
926 			_UpdateImage();
927 			break;
928 
929 		case kMsgScaleChanged:
930 			_UpdateImage();
931 			break;
932 
933 		default:
934 			BView::MessageReceived(message);
935 	}
936 }
937 
938 
939 void
Draw(BRect updateRect)940 ImageView::Draw(BRect updateRect)
941 {
942 	if (fBitmap != NULL) {
943 		SetDrawingMode(B_OP_ALPHA);
944 		DrawBitmap(fBitmap, BPoint((Bounds().Width() - fBitmap->Bounds().Width()) / 2, 0));
945 		SetDrawingMode(B_OP_COPY);
946 	}
947 }
948 
949 
950 void
_UpdateImage()951 ImageView::_UpdateImage()
952 {
953 	// ToDo: add scroller if necessary?!
954 
955 	BAutolock locker(fEditor);
956 
957 	// we need all the data...
958 
959 	size_t viewSize = fEditor.ViewSize();
960 	// that may need some more memory...
961 	if ((off_t)viewSize < fEditor.FileSize())
962 		fEditor.SetViewSize(fEditor.FileSize());
963 
964 	const char *data;
965 	if (fEditor.GetViewBuffer((const uint8 **)&data) != B_OK) {
966 		fEditor.SetViewSize(viewSize);
967 		return;
968 	}
969 
970 	if (fBitmap != NULL && (fEditor.Type() == B_MINI_ICON_TYPE
971 			|| fEditor.Type() == B_LARGE_ICON_TYPE)) {
972 		// optimize icon update...
973 		fBitmap->SetBits(data, fEditor.FileSize(), 0, B_CMAP8);
974 		fEditor.SetViewSize(viewSize);
975 		return;
976 	}
977 	if (fBitmap != NULL && fEditor.Type() == B_VECTOR_ICON_TYPE
978 		&& fScaleSlider->Value() * 8 - 1 == fBitmap->Bounds().Width()) {
979 		if (BIconUtils::GetVectorIcon((const uint8 *)data,
980 				(size_t)fEditor.FileSize(), fBitmap) == B_OK) {
981 			fEditor.SetViewSize(viewSize);
982 			return;
983 		}
984 	}
985 
986 	delete fBitmap;
987 	fBitmap = NULL;
988 
989 	switch (fEditor.Type()) {
990 		case B_MINI_ICON_TYPE:
991 			fBitmap = new BBitmap(BRect(0, 0, 15, 15), B_CMAP8);
992 			if (fBitmap->InitCheck() == B_OK)
993 				fBitmap->SetBits(data, fEditor.FileSize(), 0, B_CMAP8);
994 			break;
995 		case B_LARGE_ICON_TYPE:
996 			fBitmap = new BBitmap(BRect(0, 0, 31, 31), B_CMAP8);
997 			if (fBitmap->InitCheck() == B_OK)
998 				fBitmap->SetBits(data, fEditor.FileSize(), 0, B_CMAP8);
999 			break;
1000 		case B_VECTOR_ICON_TYPE:
1001 			fBitmap = new BBitmap(BRect(0, 0, fScaleSlider->Value() * 8 - 1,
1002 				fScaleSlider->Value() * 8 - 1), B_RGB32);
1003 			if (fBitmap->InitCheck() != B_OK
1004 				|| BIconUtils::GetVectorIcon((const uint8 *)data,
1005 					(size_t)fEditor.FileSize(), fBitmap) != B_OK) {
1006 				delete fBitmap;
1007 				fBitmap = NULL;
1008 			}
1009 			break;
1010 		case B_PNG_FORMAT:
1011 		{
1012 			BMemoryIO stream(data, fEditor.FileSize());
1013 			fBitmap = BTranslationUtils::GetBitmap(&stream);
1014 			break;
1015 		}
1016 		case B_MESSAGE_TYPE:
1017 		{
1018 			BMessage message;
1019 			// ToDo: this could be problematic if the data is not large
1020 			//		enough to contain the message...
1021 			if (message.Unflatten(data) == B_OK)
1022 				fBitmap = new BBitmap(&message);
1023 			break;
1024 		}
1025 	}
1026 
1027 	// Update the bitmap description. If no image can be displayed,
1028 	// we will show our "unsupported" message
1029 
1030 	if (fBitmap != NULL && fBitmap->InitCheck() != B_OK) {
1031 		delete fBitmap;
1032 		fBitmap = NULL;
1033 	}
1034 
1035 	if (fBitmap != NULL) {
1036 		char buffer[256];
1037 		const char *type = B_TRANSLATE("Unknown type");
1038 		switch (fEditor.Type()) {
1039 			case B_MINI_ICON_TYPE:
1040 			case B_LARGE_ICON_TYPE:
1041 			case B_VECTOR_ICON_TYPE:
1042 				type = B_TRANSLATE("Icon");
1043 				break;
1044 			case B_PNG_FORMAT:
1045 				type = B_TRANSLATE("PNG format");
1046 				break;
1047 			case B_MESSAGE_TYPE:
1048 				type = B_TRANSLATE("Flattened bitmap");
1049 				break;
1050 			default:
1051 				break;
1052 		}
1053 		const char *colorSpace;
1054 		switch (fBitmap->ColorSpace()) {
1055 			case B_GRAY1:
1056 			case B_GRAY8:
1057 				colorSpace = B_TRANSLATE("Grayscale");
1058 				break;
1059 			case B_CMAP8:
1060 				colorSpace = B_TRANSLATE("8 bit palette");
1061 				break;
1062 			case B_RGB32:
1063 			case B_RGBA32:
1064 			case B_RGB32_BIG:
1065 			case B_RGBA32_BIG:
1066 				colorSpace = B_TRANSLATE("32 bit");
1067 				break;
1068 			case B_RGB15:
1069 			case B_RGBA15:
1070 			case B_RGB15_BIG:
1071 			case B_RGBA15_BIG:
1072 				colorSpace = B_TRANSLATE("15 bit");
1073 				break;
1074 			case B_RGB16:
1075 			case B_RGB16_BIG:
1076 				colorSpace = B_TRANSLATE("16 bit");
1077 				break;
1078 			default:
1079 				colorSpace = B_TRANSLATE("Unknown format");
1080 				break;
1081 		}
1082 		snprintf(buffer, sizeof(buffer), B_TRANSLATE_COMMENT("%s, %g × %g, %s",
1083 			"The '×' is the Unicode multiplication sign U+00D7"), type,
1084 			fBitmap->Bounds().Width() + 1, fBitmap->Bounds().Height() + 1,
1085 			colorSpace);
1086 		fDescriptionView->SetText(buffer);
1087 	} else
1088 		fDescriptionView->SetText(B_TRANSLATE_COMMENT("Could not read image",
1089 			"Image means here a picture file, not a disk image."));
1090 
1091 	if (fBitmap != NULL) {
1092 		if (fScaleSlider != NULL) {
1093 			if (fScaleSlider->IsHidden())
1094 				fScaleSlider->Show();
1095 		}
1096 	} else if (fScaleSlider != NULL && !fScaleSlider->IsHidden())
1097 		fScaleSlider->Hide();
1098 
1099 	Invalidate();
1100 
1101 	// restore old view size
1102 	fEditor.SetViewSize(viewSize);
1103 }
1104 
1105 
1106 //	#pragma mark - MessageView
1107 
1108 
MessageView(BRect rect,DataEditor & editor)1109 MessageView::MessageView(BRect rect, DataEditor &editor)
1110 	: TypeEditorView(rect, B_TRANSLATE("Message View"), B_FOLLOW_ALL, 0, editor)
1111 {
1112 	SetViewUIColor(B_PANEL_BACKGROUND_COLOR);
1113 
1114 	rect = Bounds().InsetByCopy(10, 10);
1115 	rect.right -= B_V_SCROLL_BAR_WIDTH;
1116 	rect.bottom -= B_H_SCROLL_BAR_HEIGHT;
1117 
1118 	fTextView = new BTextView(rect, B_EMPTY_STRING,
1119 		rect.OffsetToCopy(B_ORIGIN).InsetByCopy(5, 5),
1120 		B_FOLLOW_ALL, B_WILL_DRAW);
1121 	fTextView->SetViewUIColor(ViewUIColor());
1122 	fTextView->SetLowUIColor(ViewUIColor());
1123 
1124 	BScrollView *scrollView = new BScrollView("scroller", fTextView,
1125 		B_FOLLOW_ALL, B_WILL_DRAW, true, true);
1126 	AddChild(scrollView);
1127 }
1128 
1129 
~MessageView()1130 MessageView::~MessageView()
1131 {
1132 }
1133 
1134 
1135 BString
_TypeToString(type_code type)1136 MessageView::_TypeToString(type_code type)
1137 {
1138 	// TODO: move this to a utility function (it's a copy from something
1139 	// similar in ProbeView.cpp)
1140 	char text[32];
1141 	for (int32 i = 0; i < 4; i++) {
1142 		text[i] = type >> (24 - 8 * i);
1143 		if (text[i] < ' ' || text[i] == 0x7f) {
1144 			snprintf(text, sizeof(text), "0x%04" B_PRIx32, type);
1145 			break;
1146 		} else if (i == 3)
1147 			text[4] = '\0';
1148 	}
1149 
1150 	return BString(text);
1151 }
1152 
1153 
1154 void
SetTo(BMessage & message)1155 MessageView::SetTo(BMessage& message)
1156 {
1157 	// TODO: when we have a real column list/tree view, redo this using
1158 	// it with nice editors as well.
1159 
1160 	fTextView->SetText("");
1161 
1162 	char text[512];
1163 	snprintf(text, sizeof(text), B_TRANSLATE_COMMENT("what: '%.4s'\n\n",
1164 		"'What' is a message specifier that defines the type of the message."),
1165 		(char*)&message.what);
1166 	fTextView->Insert(text);
1167 
1168 	type_code type;
1169 	int32 count;
1170 	char* name;
1171 	for (int32 i = 0; message.GetInfo(B_ANY_TYPE, i, &name, &type, &count)
1172 			== B_OK; i++) {
1173 		snprintf(text, sizeof(text), "%s\t", _TypeToString(type).String());
1174 		fTextView->Insert(text);
1175 
1176 		text_run_array array;
1177 		array.count = 1;
1178 		array.runs[0].offset = 0;
1179 		array.runs[0].font.SetFace(B_BOLD_FACE);
1180 		array.runs[0].color = (rgb_color){0, 0, 0, 255};
1181 
1182 		fTextView->Insert(name, &array);
1183 
1184 		array.runs[0].font = be_plain_font;
1185 		fTextView->Insert("\n", &array);
1186 
1187 		for (int32 j = 0; j < count; j++) {
1188 			const char* data;
1189 			ssize_t size;
1190 			if (message.FindData(name, type, j, (const void**)&data, &size)
1191 					!= B_OK)
1192 				continue;
1193 
1194 			text[0] = '\0';
1195 
1196 			switch (type) {
1197 				case B_INT64_TYPE:
1198 					snprintf(text, sizeof(text), "%" B_PRId64, *(int64*)data);
1199 					break;
1200 				case B_UINT64_TYPE:
1201 					snprintf(text, sizeof(text), "%" B_PRIu64, *(uint64*)data);
1202 					break;
1203 				case B_INT32_TYPE:
1204 					snprintf(text, sizeof(text), "%" B_PRId32, *(int32*)data);
1205 					break;
1206 				case B_UINT32_TYPE:
1207 					snprintf(text, sizeof(text), "%" B_PRIu32, *(uint32*)data);
1208 					break;
1209 				case B_BOOL_TYPE:
1210 					if (*(uint8*)data)
1211 						strcpy(text, "true");
1212 					else
1213 						strcpy(text, "false");
1214 					break;
1215 				case B_STRING_TYPE:
1216 				case B_MIME_STRING_TYPE:
1217 				{
1218 					snprintf(text, sizeof(text), "%s", data);
1219 					break;
1220 				}
1221 			}
1222 
1223 			if (text[0]) {
1224 				fTextView->Insert("\t\t");
1225 				if (count > 1) {
1226 					char index[16];
1227 					snprintf(index, sizeof(index), "%" B_PRId32 ".\t", j);
1228 					fTextView->Insert(index);
1229 				}
1230 				fTextView->Insert(text);
1231 				fTextView->Insert("\n");
1232 			}
1233 		}
1234 	}
1235 }
1236 
1237 
1238 void
_UpdateMessage()1239 MessageView::_UpdateMessage()
1240 {
1241 	BAutolock locker(fEditor);
1242 
1243 	size_t viewSize = fEditor.ViewSize();
1244 	// that may need some more memory...
1245 	if ((off_t)viewSize < fEditor.FileSize())
1246 		fEditor.SetViewSize(fEditor.FileSize());
1247 
1248 	const char *buffer;
1249 	if (fEditor.GetViewBuffer((const uint8 **)&buffer) == B_OK) {
1250 		BMessage message;
1251 		message.Unflatten(buffer);
1252 		SetTo(message);
1253 	}
1254 
1255 	// restore old view size
1256 	fEditor.SetViewSize(viewSize);
1257 }
1258 
1259 
1260 void
AttachedToWindow()1261 MessageView::AttachedToWindow()
1262 {
1263 	fEditor.StartWatching(this);
1264 
1265 	_UpdateMessage();
1266 }
1267 
1268 
1269 void
DetachedFromWindow()1270 MessageView::DetachedFromWindow()
1271 {
1272 	fEditor.StopWatching(this);
1273 }
1274 
1275 
1276 void
MessageReceived(BMessage * message)1277 MessageView::MessageReceived(BMessage* message)
1278 {
1279 	BView::MessageReceived(message);
1280 }
1281 
1282 
1283 //	#pragma mark -
1284 
1285 
1286 TypeEditorView*
GetTypeEditorFor(BRect rect,DataEditor & editor)1287 GetTypeEditorFor(BRect rect, DataEditor& editor)
1288 {
1289 	switch (editor.Type()) {
1290 		case B_STRING_TYPE:
1291 			return new StringEditor(editor);
1292 		case B_MIME_STRING_TYPE:
1293 			return new MimeTypeEditor(rect, editor);
1294 		case B_BOOL_TYPE:
1295 			return new BooleanEditor(rect, editor);
1296 		case B_INT8_TYPE:
1297 		case B_UINT8_TYPE:
1298 		case B_INT16_TYPE:
1299 		case B_UINT16_TYPE:
1300 		case B_INT32_TYPE:
1301 		case B_UINT32_TYPE:
1302 		case B_INT64_TYPE:
1303 		case B_UINT64_TYPE:
1304 		case B_FLOAT_TYPE:
1305 		case B_DOUBLE_TYPE:
1306 		case B_SSIZE_T_TYPE:
1307 		case B_SIZE_T_TYPE:
1308 		case B_OFF_T_TYPE:
1309 		case B_POINTER_TYPE:
1310 			return new NumberEditor(rect, editor);
1311 		case B_MESSAGE_TYPE:
1312 			// TODO: check for archived bitmaps!!!
1313 			return new MessageView(rect, editor);
1314 		case B_MINI_ICON_TYPE:
1315 		case B_LARGE_ICON_TYPE:
1316 		case B_PNG_FORMAT:
1317 		case B_VECTOR_ICON_TYPE:
1318 			return new ImageView(editor);
1319 	}
1320 
1321 	return NULL;
1322 }
1323 
1324 
1325 status_t
GetNthTypeEditor(int32 index,const char ** _name)1326 GetNthTypeEditor(int32 index, const char** _name)
1327 {
1328 	static const char* kEditors[] = {
1329 		B_TRANSLATE_COMMENT("Text", "This is the type of editor"),
1330 		B_TRANSLATE_COMMENT("Number", "This is the type of editor"),
1331 		B_TRANSLATE_COMMENT("Boolean", "This is the type of editor"),
1332 		B_TRANSLATE_COMMENT("Message", "This is the type of view"),
1333 		B_TRANSLATE_COMMENT("Image", "This is the type of view")
1334 	};
1335 
1336 	if (index < 0 || index >= int32(sizeof(kEditors) / sizeof(kEditors[0])))
1337 		return B_BAD_VALUE;
1338 
1339 	*_name = kEditors[index];
1340 	return B_OK;
1341 }
1342 
1343 
1344 TypeEditorView*
GetTypeEditorAt(int32 index,BRect rect,DataEditor & editor)1345 GetTypeEditorAt(int32 index, BRect rect, DataEditor& editor)
1346 {
1347 	TypeEditorView* view = NULL;
1348 
1349 	switch (index) {
1350 		case 0:
1351 			view = new StringEditor(editor);
1352 			break;
1353 		case 1:
1354 			view = new NumberEditor(rect, editor);
1355 			break;
1356 		case 2:
1357 			view = new BooleanEditor(rect, editor);
1358 			break;
1359 		case 3:
1360 			view = new MessageView(rect, editor);
1361 			break;
1362 		case 4:
1363 			view = new ImageView(editor);
1364 			break;
1365 
1366 		default:
1367 			return NULL;
1368 	}
1369 
1370 	if (view == NULL)
1371 		return NULL;
1372 
1373 	if (!view->TypeMatches()) {
1374 		delete view;
1375 		return NULL;
1376 	}
1377 
1378 	return view;
1379 }
1380 
1381