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
38static const uint32 kMsgValueChanged = 'vlch';
39static const uint32 kMsgScaleChanged = 'scch';
40static const uint32 kMimeTypeItem = 'miti';
41
42
43class 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
61class 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
80class 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
102class 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
121class 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
140class 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
162TypeEditorView::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
170TypeEditorView::TypeEditorView(const char *name, uint32 flags,
171		DataEditor& editor)
172	: BView(name, flags),
173	fEditor(editor)
174{
175}
176
177
178TypeEditorView::~TypeEditorView()
179{
180}
181
182
183void
184TypeEditorView::CommitChanges()
185{
186	// the default just does nothing here
187}
188
189
190bool
191TypeEditorView::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
205StringEditor::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
223void
224StringEditor::_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
244void
245StringEditor::CommitChanges()
246{
247	if (fPreviousText != fTextView->Text()) {
248		fEditor.Replace(0, (const uint8 *)fTextView->Text(),
249			fTextView->TextLength() + 1);
250	}
251}
252
253
254void
255StringEditor::AttachedToWindow()
256{
257	fEditor.StartWatching(this);
258
259	_UpdateText();
260}
261
262
263void
264StringEditor::DetachedFromWindow()
265{
266	fEditor.StopWatching(this);
267
268	CommitChanges();
269}
270
271
272void
273StringEditor::MessageReceived(BMessage *message)
274{
275	BView::MessageReceived(message);
276}
277
278
279//	#pragma mark - MimeTypeEditor
280
281
282MimeTypeEditor::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
301void
302MimeTypeEditor::_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
314void
315MimeTypeEditor::CommitChanges()
316{
317	if (fPreviousText != fTextControl->Text()) {
318		fEditor.Replace(0, (const uint8*)fTextControl->Text(),
319			strlen(fTextControl->Text()) + 1);
320	}
321}
322
323
324bool
325MimeTypeEditor::TypeMatches()
326{
327	// TODO: check contents?
328	return fEditor.FileSize() <= B_MIME_TYPE_LENGTH;
329}
330
331
332void
333MimeTypeEditor::AttachedToWindow()
334{
335	fTextControl->SetTarget(this);
336	fEditor.StartWatching(this);
337
338	_UpdateText();
339}
340
341
342void
343MimeTypeEditor::DetachedFromWindow()
344{
345	fEditor.StopWatching(this);
346
347	CommitChanges();
348}
349
350
351void
352MimeTypeEditor::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
373NumberEditor::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
388void
389NumberEditor::_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
487bool
488NumberEditor::TypeMatches()
489{
490	return fEditor.FileSize() >= (off_t)_Size();
491		// we only look at as many bytes we need to
492}
493
494
495void
496NumberEditor::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
596const char*
597NumberEditor::_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
634size_t
635NumberEditor::_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
665status_t
666NumberEditor::_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
710void
711NumberEditor::AttachedToWindow()
712{
713	fTextControl->SetTarget(this);
714	fEditor.StartWatching(this);
715
716	_UpdateText();
717}
718
719
720void
721NumberEditor::DetachedFromWindow()
722{
723	fEditor.StopWatching(this);
724
725	CommitChanges();
726}
727
728
729void
730NumberEditor::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
749BooleanEditor::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
775bool
776BooleanEditor::TypeMatches()
777{
778	// we accept everything: we just look at the first byte, anyway
779	return true;
780}
781
782
783void
784BooleanEditor::_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
799void
800BooleanEditor::CommitChanges()
801{
802	// we're commiting the changes as they happen
803}
804
805
806void
807BooleanEditor::AttachedToWindow()
808{
809	fTrueMenuItem->SetTarget(this);
810	fFalseMenuItem->SetTarget(this);
811
812	fEditor.StartWatching(this);
813}
814
815
816void
817BooleanEditor::DetachedFromWindow()
818{
819	fEditor.StopWatching(this);
820}
821
822
823void
824BooleanEditor::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
846ImageView::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
895ImageView::~ImageView()
896{
897	delete fBitmap;
898}
899
900
901void
902ImageView::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
914void
915ImageView::DetachedFromWindow()
916{
917	fEditor.StopWatching(this);
918}
919
920
921void
922ImageView::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
939void
940ImageView::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
950void
951ImageView::_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
1109MessageView::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
1130MessageView::~MessageView()
1131{
1132}
1133
1134
1135BString
1136MessageView::_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
1154void
1155MessageView::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
1238void
1239MessageView::_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
1260void
1261MessageView::AttachedToWindow()
1262{
1263	fEditor.StartWatching(this);
1264
1265	_UpdateMessage();
1266}
1267
1268
1269void
1270MessageView::DetachedFromWindow()
1271{
1272	fEditor.StopWatching(this);
1273}
1274
1275
1276void
1277MessageView::MessageReceived(BMessage* message)
1278{
1279	BView::MessageReceived(message);
1280}
1281
1282
1283//	#pragma mark -
1284
1285
1286TypeEditorView*
1287GetTypeEditorFor(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
1325status_t
1326GetNthTypeEditor(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
1344TypeEditorView*
1345GetTypeEditorAt(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