vfs: check userland buffers before reading them.
[haiku.git] / src / preferences / filetypes / ApplicationTypeWindow.cpp
blobf92534cc53022df68c1298edc52ec89af53dd8b2
1 /*
2 * Copyright 2006-2010, Axel Dörfler, axeld@pinc-software.de.
3 * Distributed under the terms of the MIT License.
4 */
7 #include "ApplicationTypeWindow.h"
8 #include "DropTargetListView.h"
9 #include "FileTypes.h"
10 #include "IconView.h"
11 #include "PreferredAppMenu.h"
12 #include "StringView.h"
13 #include "TypeListWindow.h"
15 #include <Application.h>
16 #include <Bitmap.h>
17 #include <Box.h>
18 #include <Button.h>
19 #include <Catalog.h>
20 #include <CheckBox.h>
21 #include <ControlLook.h>
22 #include <File.h>
23 #include <GroupView.h>
24 #include <LayoutBuilder.h>
25 #include <ListView.h>
26 #include <Locale.h>
27 #include <MenuBar.h>
28 #include <MenuField.h>
29 #include <MenuItem.h>
30 #include <Mime.h>
31 #include <NodeInfo.h>
32 #include <PopUpMenu.h>
33 #include <RadioButton.h>
34 #include <Roster.h>
35 #include <ScrollView.h>
36 #include <StringView.h>
37 #include <TextControl.h>
39 #include <ctype.h>
40 #include <stdio.h>
41 #include <stdlib.h>
42 #include <strings.h>
45 #undef B_TRANSLATION_CONTEXT
46 #define B_TRANSLATION_CONTEXT "Application Type Window"
49 const uint32 kMsgSave = 'save';
50 const uint32 kMsgSignatureChanged = 'sgch';
51 const uint32 kMsgToggleAppFlags = 'tglf';
52 const uint32 kMsgAppFlagsChanged = 'afch';
54 const uint32 kMsgIconChanged = 'icch';
55 const uint32 kMsgTypeIconsChanged = 'tich';
57 const uint32 kMsgVersionInfoChanged = 'tvch';
59 const uint32 kMsgTypeSelected = 'tpsl';
60 const uint32 kMsgAddType = 'adtp';
61 const uint32 kMsgTypeAdded = 'tpad';
62 const uint32 kMsgRemoveType = 'rmtp';
63 const uint32 kMsgTypeRemoved = 'tprm';
66 //! TextView that filters the tab key to be able to tab-navigate while editing
67 class TabFilteringTextView : public BTextView {
68 public:
69 TabFilteringTextView(const char* name,
70 uint32 changedMessageWhat = 0);
71 virtual ~TabFilteringTextView();
73 virtual void InsertText(const char* text, int32 length,
74 int32 offset, const text_run_array* runs);
75 virtual void DeleteText(int32 fromOffset, int32 toOffset);
76 virtual void KeyDown(const char* bytes, int32 count);
77 virtual void TargetedByScrollView(BScrollView* scroller);
78 virtual void MakeFocus(bool focused = true);
80 private:
81 BScrollView* fScrollView;
82 uint32 fChangedMessageWhat;
86 class SupportedTypeItem : public BStringItem {
87 public:
88 SupportedTypeItem(const char* type);
89 virtual ~SupportedTypeItem();
91 const char* Type() const { return fType.String(); }
92 ::Icon& Icon() { return fIcon; }
94 void SetIcon(::Icon* icon);
95 void SetIcon(entry_ref& ref, const char* type);
97 static int Compare(const void* _a, const void* _b);
99 private:
100 BString fType;
101 ::Icon fIcon;
105 class SupportedTypeListView : public DropTargetListView {
106 public:
107 SupportedTypeListView(const char* name,
108 list_view_type
109 type = B_SINGLE_SELECTION_LIST,
110 uint32 flags = B_WILL_DRAW
111 | B_FRAME_EVENTS | B_NAVIGABLE);
112 virtual ~SupportedTypeListView();
114 virtual void MessageReceived(BMessage* message);
115 virtual bool AcceptsDrag(const BMessage* message);
119 // #pragma mark -
122 TabFilteringTextView::TabFilteringTextView(const char* name,
123 uint32 changedMessageWhat)
125 BTextView(name, B_WILL_DRAW | B_PULSE_NEEDED | B_NAVIGABLE),
126 fScrollView(NULL),
127 fChangedMessageWhat(changedMessageWhat)
132 TabFilteringTextView::~TabFilteringTextView()
137 void
138 TabFilteringTextView::InsertText(const char* text, int32 length, int32 offset,
139 const text_run_array* runs)
141 BTextView::InsertText(text, length, offset, runs);
142 if (fChangedMessageWhat != 0)
143 Window()->PostMessage(fChangedMessageWhat);
147 void
148 TabFilteringTextView::DeleteText(int32 fromOffset, int32 toOffset)
150 BTextView::DeleteText(fromOffset, toOffset);
151 if (fChangedMessageWhat != 0)
152 Window()->PostMessage(fChangedMessageWhat);
156 void
157 TabFilteringTextView::KeyDown(const char* bytes, int32 count)
159 if (bytes[0] == B_TAB)
160 BView::KeyDown(bytes, count);
161 else
162 BTextView::KeyDown(bytes, count);
166 void
167 TabFilteringTextView::TargetedByScrollView(BScrollView* scroller)
169 fScrollView = scroller;
173 void
174 TabFilteringTextView::MakeFocus(bool focused)
176 BTextView::MakeFocus(focused);
178 if (fScrollView)
179 fScrollView->SetBorderHighlighted(focused);
183 // #pragma mark -
186 SupportedTypeItem::SupportedTypeItem(const char* type)
187 : BStringItem(type),
188 fType(type)
190 BMimeType mimeType(type);
192 char description[B_MIME_TYPE_LENGTH];
193 if (mimeType.GetShortDescription(description) == B_OK && description[0])
194 SetText(description);
198 SupportedTypeItem::~SupportedTypeItem()
203 void
204 SupportedTypeItem::SetIcon(::Icon* icon)
206 if (icon != NULL)
207 fIcon = *icon;
208 else
209 fIcon.Unset();
213 void
214 SupportedTypeItem::SetIcon(entry_ref& ref, const char* type)
216 fIcon.SetTo(ref, type);
220 /*static*/
222 SupportedTypeItem::Compare(const void* _a, const void* _b)
224 const SupportedTypeItem* a = *(const SupportedTypeItem**)_a;
225 const SupportedTypeItem* b = *(const SupportedTypeItem**)_b;
227 int compare = strcasecmp(a->Text(), b->Text());
228 if (compare != 0)
229 return compare;
231 return strcasecmp(a->Type(), b->Type());
235 // #pragma mark -
238 SupportedTypeListView::SupportedTypeListView(const char* name,
239 list_view_type type, uint32 flags)
241 DropTargetListView(name, type, flags)
246 SupportedTypeListView::~SupportedTypeListView()
251 void
252 SupportedTypeListView::MessageReceived(BMessage* message)
254 if (message->WasDropped() && AcceptsDrag(message)) {
255 // Add unique types
256 entry_ref ref;
257 for (int32 index = 0; message->FindRef("refs", index++, &ref) == B_OK; ) {
258 BNode node(&ref);
259 BNodeInfo info(&node);
260 if (node.InitCheck() != B_OK || info.InitCheck() != B_OK)
261 continue;
263 // TODO: we could identify the file in case it doesn't have a type...
264 char type[B_MIME_TYPE_LENGTH];
265 if (info.GetType(type) != B_OK)
266 continue;
268 // check if that type is already in our list
269 bool found = false;
270 for (int32 i = CountItems(); i-- > 0;) {
271 SupportedTypeItem* item = (SupportedTypeItem*)ItemAt(i);
272 if (!strcmp(item->Text(), type)) {
273 found = true;
274 break;
278 if (!found) {
279 // add type
280 AddItem(new SupportedTypeItem(type));
284 SortItems(&SupportedTypeItem::Compare);
285 } else
286 DropTargetListView::MessageReceived(message);
290 bool
291 SupportedTypeListView::AcceptsDrag(const BMessage* message)
293 type_code type;
294 return message->GetInfo("refs", &type) == B_OK && type == B_REF_TYPE;
298 // #pragma mark -
301 ApplicationTypeWindow::ApplicationTypeWindow(BPoint position,
302 const BEntry& entry)
304 BWindow(BRect(0.0f, 0.0f, 250.0f, 340.0f).OffsetBySelf(position),
305 B_TRANSLATE("Application type"), B_TITLED_WINDOW,
306 B_NOT_ZOOMABLE | B_ASYNCHRONOUS_CONTROLS |
307 B_FRAME_EVENTS | B_AUTO_UPDATE_SIZE_LIMITS),
308 fChangedProperties(0)
310 float padding = be_control_look->DefaultItemSpacing();
311 BAlignment labelAlignment = be_control_look->DefaultLabelAlignment();
313 BMenuBar* menuBar = new BMenuBar((char*)NULL);
314 menuBar->SetExplicitAlignment(BAlignment(B_ALIGN_LEFT, B_ALIGN_TOP));
316 BMenu* menu = new BMenu(B_TRANSLATE("File"));
317 fSaveMenuItem = new BMenuItem(B_TRANSLATE("Save"),
318 new BMessage(kMsgSave), 'S');
319 fSaveMenuItem->SetEnabled(false);
320 menu->AddItem(fSaveMenuItem);
321 BMenuItem* item;
322 menu->AddItem(item = new BMenuItem(
323 B_TRANSLATE("Save into resource file" B_UTF8_ELLIPSIS), NULL));
324 item->SetEnabled(false);
326 menu->AddSeparatorItem();
327 menu->AddItem(new BMenuItem(B_TRANSLATE("Close"),
328 new BMessage(B_QUIT_REQUESTED), 'W', B_COMMAND_KEY));
329 menuBar->AddItem(menu);
331 // Signature
333 fSignatureControl = new BTextControl("signature",
334 B_TRANSLATE("Signature:"), new BMessage(kMsgSignatureChanged));
335 fSignatureControl->SetModificationMessage(
336 new BMessage(kMsgSignatureChanged));
338 // filter out invalid characters that can't be part of a MIME type name
339 BTextView* textView = fSignatureControl->TextView();
340 textView->SetMaxBytes(B_MIME_TYPE_LENGTH);
341 const char* disallowedCharacters = "<>@,;:\"()[]?= ";
342 for (int32 i = 0; disallowedCharacters[i]; i++) {
343 textView->DisallowChar(disallowedCharacters[i]);
346 // "Application Flags" group
348 BBox* flagsBox = new BBox("flagsBox");
350 fFlagsCheckBox = new BCheckBox("flags", B_TRANSLATE("Application flags"),
351 new BMessage(kMsgToggleAppFlags));
352 fFlagsCheckBox->SetValue(B_CONTROL_ON);
354 fSingleLaunchButton = new BRadioButton("single",
355 B_TRANSLATE("Single launch"), new BMessage(kMsgAppFlagsChanged));
357 fMultipleLaunchButton = new BRadioButton("multiple",
358 B_TRANSLATE("Multiple launch"), new BMessage(kMsgAppFlagsChanged));
360 fExclusiveLaunchButton = new BRadioButton("exclusive",
361 B_TRANSLATE("Exclusive launch"), new BMessage(kMsgAppFlagsChanged));
363 fArgsOnlyCheckBox = new BCheckBox("args only", B_TRANSLATE("Args only"),
364 new BMessage(kMsgAppFlagsChanged));
366 fBackgroundAppCheckBox = new BCheckBox("background",
367 B_TRANSLATE("Background app"), new BMessage(kMsgAppFlagsChanged));
369 BLayoutBuilder::Grid<>(flagsBox, 0, 0)
370 .SetInsets(padding, padding * 2, padding, padding)
371 .Add(fSingleLaunchButton, 0, 0).Add(fArgsOnlyCheckBox, 1, 0)
372 .Add(fMultipleLaunchButton, 0, 1).Add(fBackgroundAppCheckBox, 1, 1)
373 .Add(fExclusiveLaunchButton, 0, 2);
374 flagsBox->SetLabel(fFlagsCheckBox);
376 // "Icon" group
378 BBox* iconBox = new BBox("IconBox");
379 iconBox->SetLabel(B_TRANSLATE("Icon"));
380 fIconView = new IconView("icon");
381 fIconView->SetModificationMessage(new BMessage(kMsgIconChanged));
382 BLayoutBuilder::Group<>(iconBox, B_HORIZONTAL)
383 .SetInsets(padding, padding * 2, padding, padding)
384 .Add(fIconView);
386 // "Supported Types" group
388 BBox* typeBox = new BBox("typesBox");
389 typeBox->SetLabel(B_TRANSLATE("Supported types"));
391 fTypeListView = new SupportedTypeListView("Suppported Types",
392 B_SINGLE_SELECTION_LIST);
393 fTypeListView->SetSelectionMessage(new BMessage(kMsgTypeSelected));
395 BScrollView* scrollView = new BScrollView("type scrollview", fTypeListView,
396 B_FRAME_EVENTS | B_WILL_DRAW, false, true);
398 fAddTypeButton = new BButton("add type",
399 B_TRANSLATE("Add" B_UTF8_ELLIPSIS), new BMessage(kMsgAddType));
401 fRemoveTypeButton = new BButton("remove type", B_TRANSLATE("Remove"),
402 new BMessage(kMsgRemoveType));
404 fTypeIconView = new IconView("type icon");
405 BGroupView* iconHolder = new BGroupView(B_HORIZONTAL);
406 iconHolder->AddChild(fTypeIconView);
407 fTypeIconView->SetModificationMessage(new BMessage(kMsgTypeIconsChanged));
409 BLayoutBuilder::Grid<>(typeBox, padding, padding)
410 .SetInsets(padding, padding * 2, padding, padding)
411 .Add(scrollView, 0, 0, 1, 4)
412 .Add(fAddTypeButton, 1, 0, 1, 2)
413 .Add(fRemoveTypeButton, 1, 2, 1, 2)
414 .Add(iconHolder, 2, 1, 1, 2)
415 .SetColumnWeight(0, 3)
416 .SetColumnWeight(1, 2)
417 .SetColumnWeight(2, 1);
419 iconHolder->SetExplicitAlignment(
420 BAlignment(B_ALIGN_CENTER, B_ALIGN_MIDDLE));
422 // "Version Info" group
424 BBox* versionBox = new BBox("versionBox");
425 versionBox->SetLabel(B_TRANSLATE("Version info"));
427 fMajorVersionControl = new BTextControl("version major",
428 B_TRANSLATE("Version:"), NULL, new BMessage(kMsgVersionInfoChanged));
429 _MakeNumberTextControl(fMajorVersionControl);
431 fMiddleVersionControl = new BTextControl("version middle", ".", NULL,
432 new BMessage(kMsgVersionInfoChanged));
433 _MakeNumberTextControl(fMiddleVersionControl);
435 fMinorVersionControl = new BTextControl("version minor", ".", NULL,
436 new BMessage(kMsgVersionInfoChanged));
437 _MakeNumberTextControl(fMinorVersionControl);
439 fVarietyMenu = new BPopUpMenu("variety", true, true);
440 fVarietyMenu->AddItem(new BMenuItem(B_TRANSLATE("Development"),
441 new BMessage(kMsgVersionInfoChanged)));
442 fVarietyMenu->AddItem(new BMenuItem(B_TRANSLATE("Alpha"),
443 new BMessage(kMsgVersionInfoChanged)));
444 fVarietyMenu->AddItem(new BMenuItem(B_TRANSLATE("Beta"),
445 new BMessage(kMsgVersionInfoChanged)));
446 fVarietyMenu->AddItem(new BMenuItem(B_TRANSLATE("Gamma"),
447 new BMessage(kMsgVersionInfoChanged)));
448 item = new BMenuItem(B_TRANSLATE("Golden master"),
449 new BMessage(kMsgVersionInfoChanged));
450 fVarietyMenu->AddItem(item);
451 item->SetMarked(true);
452 fVarietyMenu->AddItem(new BMenuItem(B_TRANSLATE("Final"),
453 new BMessage(kMsgVersionInfoChanged)));
455 BMenuField* varietyField = new BMenuField("", fVarietyMenu);
456 fInternalVersionControl = new BTextControl("version internal", "/", NULL,
457 new BMessage(kMsgVersionInfoChanged));
458 fShortDescriptionControl = new BTextControl("short description",
459 B_TRANSLATE("Short description:"), NULL,
460 new BMessage(kMsgVersionInfoChanged));
462 // TODO: workaround for a GCC 4.1.0 bug? Or is that really what the standard says?
463 version_info versionInfo;
464 fShortDescriptionControl->TextView()->SetMaxBytes(
465 sizeof(versionInfo.short_info));
467 BStringView* longLabel = new BStringView(NULL,
468 B_TRANSLATE("Long description:"));
469 longLabel->SetExplicitAlignment(labelAlignment);
470 fLongDescriptionView = new TabFilteringTextView("long desc",
471 kMsgVersionInfoChanged);
472 fLongDescriptionView->SetMaxBytes(sizeof(versionInfo.long_info));
474 scrollView = new BScrollView("desc scrollview", fLongDescriptionView,
475 B_FRAME_EVENTS | B_WILL_DRAW, false, true);
477 // TODO: remove workaround (bug #5678)
478 BSize minScrollSize = scrollView->ScrollBar(B_VERTICAL)->MinSize();
479 minScrollSize.width += fLongDescriptionView->MinSize().width;
480 scrollView->SetExplicitMinSize(minScrollSize);
482 // Manually set a minimum size for the version text controls
483 // TODO: the same does not work when applied to the layout items
484 float width = be_plain_font->StringWidth("99") + 16;
485 fMajorVersionControl->TextView()->SetExplicitMinSize(
486 BSize(width, fMajorVersionControl->MinSize().height));
487 fMiddleVersionControl->TextView()->SetExplicitMinSize(
488 BSize(width, fMiddleVersionControl->MinSize().height));
489 fMinorVersionControl->TextView()->SetExplicitMinSize(
490 BSize(width, fMinorVersionControl->MinSize().height));
491 fInternalVersionControl->TextView()->SetExplicitMinSize(
492 BSize(width, fInternalVersionControl->MinSize().height));
494 BLayoutBuilder::Grid<>(versionBox, padding / 2, padding / 2)
495 .SetInsets(padding, padding * 2, padding, padding)
496 .Add(fMajorVersionControl->CreateLabelLayoutItem(), 0, 0)
497 .Add(fMajorVersionControl->CreateTextViewLayoutItem(), 1, 0)
498 .Add(fMiddleVersionControl, 2, 0, 2)
499 .Add(fMinorVersionControl, 4, 0, 2)
500 .Add(varietyField, 6, 0, 3)
501 .Add(fInternalVersionControl, 9, 0, 2)
502 .Add(fShortDescriptionControl->CreateLabelLayoutItem(), 0, 1)
503 .Add(fShortDescriptionControl->CreateTextViewLayoutItem(), 1, 1, 10)
504 .Add(longLabel, 0, 2)
505 .Add(scrollView, 1, 2, 10, 3)
506 .SetRowWeight(3, 3);
508 // put it all together
509 BLayoutBuilder::Group<>(this, B_VERTICAL, 0)
510 .SetInsets(0, 0, 0, 0)
511 .Add(menuBar)
512 .AddGroup(B_VERTICAL, padding)
513 .SetInsets(padding, padding, padding, padding)
514 .Add(fSignatureControl)
515 .AddGroup(B_HORIZONTAL, padding)
516 .Add(flagsBox, 3)
517 .Add(iconBox, 1)
518 .End()
519 .Add(typeBox)
520 .Add(versionBox);
522 SetKeyMenuBar(menuBar);
524 fSignatureControl->MakeFocus(true);
525 BMimeType::StartWatching(this);
526 _SetTo(entry);
530 ApplicationTypeWindow::~ApplicationTypeWindow()
532 BMimeType::StopWatching(this);
536 BString
537 ApplicationTypeWindow::_Title(const BEntry& entry)
539 char name[B_FILE_NAME_LENGTH];
540 if (entry.GetName(name) != B_OK)
541 strcpy(name, "\"-\"");
543 BString title = B_TRANSLATE("%1 application type");
544 title.ReplaceFirst("%1", name);
545 return title;
549 void
550 ApplicationTypeWindow::_SetTo(const BEntry& entry)
552 SetTitle(_Title(entry).String());
553 fEntry = entry;
555 // Retrieve Info
557 BFile file(&entry, B_READ_ONLY);
558 if (file.InitCheck() != B_OK)
559 return;
561 BAppFileInfo info(&file);
562 if (info.InitCheck() != B_OK)
563 return;
565 char signature[B_MIME_TYPE_LENGTH];
566 if (info.GetSignature(signature) != B_OK)
567 signature[0] = '\0';
569 bool gotFlags = false;
570 uint32 flags;
571 if (info.GetAppFlags(&flags) == B_OK)
572 gotFlags = true;
573 else
574 flags = B_MULTIPLE_LAUNCH;
576 version_info versionInfo;
577 if (info.GetVersionInfo(&versionInfo, B_APP_VERSION_KIND) != B_OK)
578 memset(&versionInfo, 0, sizeof(version_info));
580 // Set Controls
582 fSignatureControl->SetModificationMessage(NULL);
583 fSignatureControl->SetText(signature);
584 fSignatureControl->SetModificationMessage(
585 new BMessage(kMsgSignatureChanged));
587 // flags
589 switch (flags & (B_SINGLE_LAUNCH | B_MULTIPLE_LAUNCH | B_EXCLUSIVE_LAUNCH)) {
590 case B_SINGLE_LAUNCH:
591 fSingleLaunchButton->SetValue(B_CONTROL_ON);
592 break;
594 case B_EXCLUSIVE_LAUNCH:
595 fExclusiveLaunchButton->SetValue(B_CONTROL_ON);
596 break;
598 case B_MULTIPLE_LAUNCH:
599 default:
600 fMultipleLaunchButton->SetValue(B_CONTROL_ON);
601 break;
604 fArgsOnlyCheckBox->SetValue((flags & B_ARGV_ONLY) != 0);
605 fBackgroundAppCheckBox->SetValue((flags & B_BACKGROUND_APP) != 0);
606 fFlagsCheckBox->SetValue(gotFlags);
608 _UpdateAppFlagsEnabled();
610 // icon
612 entry_ref ref;
613 if (entry.GetRef(&ref) == B_OK)
614 fIcon.SetTo(ref);
615 else
616 fIcon.Unset();
618 fIconView->SetModificationMessage(NULL);
619 fIconView->SetTo(&fIcon);
620 fIconView->SetModificationMessage(new BMessage(kMsgIconChanged));
622 // supported types
624 BMessage supportedTypes;
625 info.GetSupportedTypes(&supportedTypes);
627 for (int32 i = fTypeListView->CountItems(); i-- > 0;) {
628 BListItem* item = fTypeListView->RemoveItem(i);
629 delete item;
632 const char* type;
633 for (int32 i = 0; supportedTypes.FindString("types", i, &type) == B_OK; i++) {
634 SupportedTypeItem* item = new SupportedTypeItem(type);
636 entry_ref ref;
637 if (fEntry.GetRef(&ref) == B_OK)
638 item->SetIcon(ref, type);
640 fTypeListView->AddItem(item);
642 fTypeListView->SortItems(&SupportedTypeItem::Compare);
643 fTypeIconView->SetModificationMessage(NULL);
644 fTypeIconView->SetTo(NULL);
645 fTypeIconView->SetModificationMessage(new BMessage(kMsgTypeIconsChanged));
646 fTypeIconView->SetEnabled(false);
647 fRemoveTypeButton->SetEnabled(false);
649 // version info
651 char text[256];
652 snprintf(text, sizeof(text), "%" B_PRId32, versionInfo.major);
653 fMajorVersionControl->SetText(text);
654 snprintf(text, sizeof(text), "%" B_PRId32, versionInfo.middle);
655 fMiddleVersionControl->SetText(text);
656 snprintf(text, sizeof(text), "%" B_PRId32, versionInfo.minor);
657 fMinorVersionControl->SetText(text);
659 if (versionInfo.variety >= (uint32)fVarietyMenu->CountItems())
660 versionInfo.variety = 0;
661 BMenuItem* item = fVarietyMenu->ItemAt(versionInfo.variety);
662 if (item != NULL)
663 item->SetMarked(true);
665 snprintf(text, sizeof(text), "%" B_PRId32, versionInfo.internal);
666 fInternalVersionControl->SetText(text);
668 fShortDescriptionControl->SetText(versionInfo.short_info);
669 fLongDescriptionView->SetText(versionInfo.long_info);
671 // store original data
673 fOriginalInfo.signature = signature;
674 fOriginalInfo.gotFlags = gotFlags;
675 fOriginalInfo.flags = gotFlags ? flags : 0;
676 fOriginalInfo.versionInfo = versionInfo;
677 fOriginalInfo.supportedTypes = _SupportedTypes();
678 // The list view has the types sorted possibly differently
679 // to the supportedTypes message, so don't use that here, but
680 // get the sorted message instead.
681 fOriginalInfo.iconChanged = false;
682 fOriginalInfo.typeIconsChanged = false;
684 fChangedProperties = 0;
685 _CheckSaveMenuItem(0);
689 void
690 ApplicationTypeWindow::_UpdateAppFlagsEnabled()
692 bool enabled = fFlagsCheckBox->Value() != B_CONTROL_OFF;
694 fSingleLaunchButton->SetEnabled(enabled);
695 fMultipleLaunchButton->SetEnabled(enabled);
696 fExclusiveLaunchButton->SetEnabled(enabled);
697 fArgsOnlyCheckBox->SetEnabled(enabled);
698 fBackgroundAppCheckBox->SetEnabled(enabled);
702 void
703 ApplicationTypeWindow::_MakeNumberTextControl(BTextControl* control)
705 // filter out invalid characters that can't be part of a MIME type name
706 BTextView* textView = control->TextView();
707 textView->SetMaxBytes(10);
709 for (int32 i = 0; i < 256; i++) {
710 if (!isdigit(i))
711 textView->DisallowChar(i);
716 void
717 ApplicationTypeWindow::_Save()
719 BFile file;
720 status_t status = file.SetTo(&fEntry, B_READ_WRITE);
721 if (status != B_OK)
722 return;
724 BAppFileInfo info(&file);
725 status = info.InitCheck();
726 if (status != B_OK)
727 return;
729 // Retrieve Info
731 uint32 flags = 0;
732 bool gotFlags = _Flags(flags);
733 BMessage supportedTypes = _SupportedTypes();
734 version_info versionInfo = _VersionInfo();
736 // Save
738 status = info.SetSignature(fSignatureControl->Text());
739 if (status == B_OK) {
740 if (gotFlags)
741 status = info.SetAppFlags(flags);
742 else
743 status = info.RemoveAppFlags();
745 if (status == B_OK)
746 status = info.SetVersionInfo(&versionInfo, B_APP_VERSION_KIND);
747 if (status == B_OK)
748 fIcon.CopyTo(info, NULL, true);
750 // supported types and their icons
751 if (status == B_OK)
752 status = info.SetSupportedTypes(&supportedTypes);
754 for (int32 i = 0; i < fTypeListView->CountItems(); i++) {
755 SupportedTypeItem* item = dynamic_cast<SupportedTypeItem*>(
756 fTypeListView->ItemAt(i));
758 if (item != NULL)
759 item->Icon().CopyTo(info, item->Type(), true);
762 // reset the saved info
763 fOriginalInfo.signature = fSignatureControl->Text();
764 fOriginalInfo.gotFlags = gotFlags;
765 fOriginalInfo.flags = flags;
766 fOriginalInfo.versionInfo = versionInfo;
767 fOriginalInfo.supportedTypes = supportedTypes;
768 fOriginalInfo.iconChanged = false;
769 fOriginalInfo.typeIconsChanged = false;
771 fChangedProperties = 0;
772 _CheckSaveMenuItem(0);
776 void
777 ApplicationTypeWindow::_CheckSaveMenuItem(uint32 flags)
779 fChangedProperties = _NeedsSaving(flags);
780 fSaveMenuItem->SetEnabled(fChangedProperties != 0);
784 bool
785 operator!=(const version_info& a, const version_info& b)
787 return a.major != b.major || a.middle != b.middle || a.minor != b.minor
788 || a.variety != b.variety || a.internal != b.internal
789 || strcmp(a.short_info, b.short_info) != 0
790 || strcmp(a.long_info, b.long_info) != 0;
794 uint32
795 ApplicationTypeWindow::_NeedsSaving(uint32 _flags) const
797 uint32 flags = fChangedProperties;
798 if (_flags & CHECK_SIGNATUR) {
799 if (fOriginalInfo.signature != fSignatureControl->Text())
800 flags |= CHECK_SIGNATUR;
801 else
802 flags &= ~CHECK_SIGNATUR;
805 if (_flags & CHECK_FLAGS) {
806 uint32 appFlags = 0;
807 bool gotFlags = _Flags(appFlags);
808 if (fOriginalInfo.gotFlags != gotFlags
809 || fOriginalInfo.flags != appFlags) {
810 flags |= CHECK_FLAGS;
811 } else
812 flags &= ~CHECK_FLAGS;
815 if (_flags & CHECK_VERSION) {
816 if (fOriginalInfo.versionInfo != _VersionInfo())
817 flags |= CHECK_VERSION;
818 else
819 flags &= ~CHECK_VERSION;
822 if (_flags & CHECK_ICON) {
823 if (fOriginalInfo.iconChanged)
824 flags |= CHECK_ICON;
825 else
826 flags &= ~CHECK_ICON;
829 if (_flags & CHECK_TYPES) {
830 if (!fOriginalInfo.supportedTypes.HasSameData(_SupportedTypes()))
831 flags |= CHECK_TYPES;
832 else
833 flags &= ~CHECK_TYPES;
836 if (_flags & CHECK_TYPE_ICONS) {
837 if (fOriginalInfo.typeIconsChanged)
838 flags |= CHECK_TYPE_ICONS;
839 else
840 flags &= ~CHECK_TYPE_ICONS;
843 return flags;
847 // #pragma mark -
850 bool
851 ApplicationTypeWindow::_Flags(uint32& flags) const
853 flags = 0;
854 if (fFlagsCheckBox->Value() != B_CONTROL_OFF) {
855 if (fSingleLaunchButton->Value() != B_CONTROL_OFF)
856 flags |= B_SINGLE_LAUNCH;
857 else if (fMultipleLaunchButton->Value() != B_CONTROL_OFF)
858 flags |= B_MULTIPLE_LAUNCH;
859 else if (fExclusiveLaunchButton->Value() != B_CONTROL_OFF)
860 flags |= B_EXCLUSIVE_LAUNCH;
862 if (fArgsOnlyCheckBox->Value() != B_CONTROL_OFF)
863 flags |= B_ARGV_ONLY;
864 if (fBackgroundAppCheckBox->Value() != B_CONTROL_OFF)
865 flags |= B_BACKGROUND_APP;
866 return true;
868 return false;
872 BMessage
873 ApplicationTypeWindow::_SupportedTypes() const
875 BMessage supportedTypes;
876 for (int32 i = 0; i < fTypeListView->CountItems(); i++) {
877 SupportedTypeItem* item = dynamic_cast<SupportedTypeItem*>(
878 fTypeListView->ItemAt(i));
880 if (item != NULL)
881 supportedTypes.AddString("types", item->Type());
883 return supportedTypes;
887 version_info
888 ApplicationTypeWindow::_VersionInfo() const
890 version_info versionInfo;
891 versionInfo.major = atol(fMajorVersionControl->Text());
892 versionInfo.middle = atol(fMiddleVersionControl->Text());
893 versionInfo.minor = atol(fMinorVersionControl->Text());
894 versionInfo.variety = fVarietyMenu->IndexOf(fVarietyMenu->FindMarked());
895 versionInfo.internal = atol(fInternalVersionControl->Text());
896 strlcpy(versionInfo.short_info, fShortDescriptionControl->Text(),
897 sizeof(versionInfo.short_info));
898 strlcpy(versionInfo.long_info, fLongDescriptionView->Text(),
899 sizeof(versionInfo.long_info));
900 return versionInfo;
904 // #pragma mark -
907 void
908 ApplicationTypeWindow::FrameResized(float width, float height)
910 // This works around a flaw of BTextView
911 fLongDescriptionView->SetTextRect(fLongDescriptionView->Bounds());
915 void
916 ApplicationTypeWindow::MessageReceived(BMessage* message)
918 switch (message->what) {
919 case kMsgToggleAppFlags:
920 _UpdateAppFlagsEnabled();
921 _CheckSaveMenuItem(CHECK_FLAGS);
922 break;
924 case kMsgSignatureChanged:
925 _CheckSaveMenuItem(CHECK_SIGNATUR);
926 break;
928 case kMsgAppFlagsChanged:
929 _CheckSaveMenuItem(CHECK_FLAGS);
930 break;
932 case kMsgIconChanged:
933 fOriginalInfo.iconChanged = true;
934 _CheckSaveMenuItem(CHECK_ICON);
935 break;
937 case kMsgTypeIconsChanged:
938 fOriginalInfo.typeIconsChanged = true;
939 _CheckSaveMenuItem(CHECK_TYPE_ICONS);
940 break;
942 case kMsgVersionInfoChanged:
943 _CheckSaveMenuItem(CHECK_VERSION);
944 break;
946 case kMsgSave:
947 _Save();
948 break;
950 case kMsgTypeSelected:
952 int32 index;
953 if (message->FindInt32("index", &index) == B_OK) {
954 SupportedTypeItem* item
955 = (SupportedTypeItem*)fTypeListView->ItemAt(index);
957 fTypeIconView->SetModificationMessage(NULL);
958 fTypeIconView->SetTo(item != NULL ? &item->Icon() : NULL);
959 fTypeIconView->SetModificationMessage(
960 new BMessage(kMsgTypeIconsChanged));
961 fTypeIconView->SetEnabled(item != NULL);
962 fRemoveTypeButton->SetEnabled(item != NULL);
964 _CheckSaveMenuItem(CHECK_TYPES);
966 break;
969 case kMsgAddType:
971 BWindow* window = new TypeListWindow(NULL,
972 kMsgTypeAdded, this);
973 window->Show();
974 break;
977 case kMsgTypeAdded:
979 const char* type;
980 if (message->FindString("type", &type) != B_OK)
981 break;
983 // check if this type already exists
985 SupportedTypeItem* newItem = new SupportedTypeItem(type);
986 int32 insertAt = 0;
988 for (int32 i = fTypeListView->CountItems(); i-- > 0;) {
989 SupportedTypeItem* item = dynamic_cast<SupportedTypeItem*>(
990 fTypeListView->ItemAt(i));
991 if (item == NULL)
992 continue;
994 int compare = strcasecmp(item->Type(), type);
995 if (!compare) {
996 // type does already exist, select it and bail out
997 delete newItem;
998 newItem = NULL;
999 fTypeListView->Select(i);
1000 break;
1002 if (compare < 0)
1003 insertAt = i + 1;
1006 if (newItem == NULL)
1007 break;
1009 fTypeListView->AddItem(newItem, insertAt);
1010 fTypeListView->Select(insertAt);
1012 _CheckSaveMenuItem(CHECK_TYPES);
1013 break;
1016 case kMsgRemoveType:
1018 int32 index = fTypeListView->CurrentSelection();
1019 if (index < 0)
1020 break;
1022 delete fTypeListView->RemoveItem(index);
1023 fTypeIconView->SetModificationMessage(NULL);
1024 fTypeIconView->SetTo(NULL);
1025 fTypeIconView->SetModificationMessage(
1026 new BMessage(kMsgTypeIconsChanged));
1027 fTypeIconView->SetEnabled(false);
1028 fRemoveTypeButton->SetEnabled(false);
1030 _CheckSaveMenuItem(CHECK_TYPES);
1031 break;
1034 case B_SIMPLE_DATA:
1036 entry_ref ref;
1037 if (message->FindRef("refs", &ref) != B_OK)
1038 break;
1040 // TODO: add to supported types
1041 break;
1044 case B_META_MIME_CHANGED:
1045 const char* type;
1046 int32 which;
1047 if (message->FindString("be:type", &type) != B_OK
1048 || message->FindInt32("be:which", &which) != B_OK)
1049 break;
1051 // TODO: update supported types names
1052 // if (which == B_MIME_TYPE_DELETED)
1054 // _CheckSaveMenuItem(...);
1055 break;
1057 default:
1058 BWindow::MessageReceived(message);
1063 bool
1064 ApplicationTypeWindow::QuitRequested()
1066 if (_NeedsSaving(CHECK_ALL) != 0) {
1067 BAlert* alert = new BAlert(B_TRANSLATE("Save request"),
1068 B_TRANSLATE("Save changes before closing?"),
1069 B_TRANSLATE("Cancel"), B_TRANSLATE("Don't save"),
1070 B_TRANSLATE("Save"), B_WIDTH_AS_USUAL, B_OFFSET_SPACING,
1071 B_WARNING_ALERT);
1072 alert->SetShortcut(0, B_ESCAPE);
1073 alert->SetShortcut(1, 'd');
1074 alert->SetShortcut(2, 's');
1076 int32 choice = alert->Go();
1077 switch (choice) {
1078 case 0:
1079 return false;
1080 case 1:
1081 break;
1082 case 2:
1083 _Save();
1084 break;
1088 be_app->PostMessage(kMsgTypeWindowClosed);
1089 return true;