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