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