Assorted whitespace cleanup and typo fixes.
[haiku.git] / src / preferences / filetypes / ApplicationTypeWindow.cpp
blobf4e1c8f8aaf06ca7bccbb0de8787cdfb317f069d
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 kMsgTypeSelected = 'tpsl';
58 const uint32 kMsgAddType = 'adtp';
59 const uint32 kMsgTypeAdded = 'tpad';
60 const uint32 kMsgRemoveType = 'rmtp';
61 const uint32 kMsgTypeRemoved = 'tprm';
64 //! TextView that filters the tab key to be able to tab-navigate while editing
65 class TabFilteringTextView : public BTextView {
66 public:
67 TabFilteringTextView(const char* name);
68 virtual ~TabFilteringTextView();
70 virtual void KeyDown(const char* bytes, int32 count);
71 virtual void TargetedByScrollView(BScrollView* scroller);
72 virtual void MakeFocus(bool focused = true);
74 private:
75 BScrollView* fScrollView;
79 class SupportedTypeItem : public BStringItem {
80 public:
81 SupportedTypeItem(const char* type);
82 virtual ~SupportedTypeItem();
84 const char* Type() const { return fType.String(); }
85 ::Icon& Icon() { return fIcon; }
87 void SetIcon(::Icon* icon);
88 void SetIcon(entry_ref& ref, const char* type);
90 static int Compare(const void* _a, const void* _b);
92 private:
93 BString fType;
94 ::Icon fIcon;
98 class SupportedTypeListView : public DropTargetListView {
99 public:
100 SupportedTypeListView(const char* name,
101 list_view_type
102 type = B_SINGLE_SELECTION_LIST,
103 uint32 flags = B_WILL_DRAW
104 | B_FRAME_EVENTS | B_NAVIGABLE);
105 virtual ~SupportedTypeListView();
107 virtual void MessageReceived(BMessage* message);
108 virtual bool AcceptsDrag(const BMessage* message);
112 // #pragma mark -
115 TabFilteringTextView::TabFilteringTextView(const char* name)
117 BTextView(name, B_WILL_DRAW | B_PULSE_NEEDED | B_NAVIGABLE),
118 fScrollView(NULL)
123 TabFilteringTextView::~TabFilteringTextView()
128 void
129 TabFilteringTextView::KeyDown(const char* bytes, int32 count)
131 if (bytes[0] == B_TAB)
132 BView::KeyDown(bytes, count);
133 else
134 BTextView::KeyDown(bytes, count);
138 void
139 TabFilteringTextView::TargetedByScrollView(BScrollView* scroller)
141 fScrollView = scroller;
145 void
146 TabFilteringTextView::MakeFocus(bool focused)
148 BTextView::MakeFocus(focused);
150 if (fScrollView)
151 fScrollView->SetBorderHighlighted(focused);
155 // #pragma mark -
158 SupportedTypeItem::SupportedTypeItem(const char* type)
159 : BStringItem(type),
160 fType(type)
162 BMimeType mimeType(type);
164 char description[B_MIME_TYPE_LENGTH];
165 if (mimeType.GetShortDescription(description) == B_OK && description[0])
166 SetText(description);
170 SupportedTypeItem::~SupportedTypeItem()
175 void
176 SupportedTypeItem::SetIcon(::Icon* icon)
178 if (icon != NULL)
179 fIcon = *icon;
180 else
181 fIcon.Unset();
185 void
186 SupportedTypeItem::SetIcon(entry_ref& ref, const char* type)
188 fIcon.SetTo(ref, type);
192 /*static*/
194 SupportedTypeItem::Compare(const void* _a, const void* _b)
196 const SupportedTypeItem* a = *(const SupportedTypeItem**)_a;
197 const SupportedTypeItem* b = *(const SupportedTypeItem**)_b;
199 int compare = strcasecmp(a->Text(), b->Text());
200 if (compare != 0)
201 return compare;
203 return strcasecmp(a->Type(), b->Type());
207 // #pragma mark -
210 SupportedTypeListView::SupportedTypeListView(const char* name,
211 list_view_type type, uint32 flags)
213 DropTargetListView(name, type, flags)
218 SupportedTypeListView::~SupportedTypeListView()
223 void
224 SupportedTypeListView::MessageReceived(BMessage* message)
226 if (message->WasDropped() && AcceptsDrag(message)) {
227 // Add unique types
228 entry_ref ref;
229 for (int32 index = 0; message->FindRef("refs", index++, &ref) == B_OK; ) {
230 BNode node(&ref);
231 BNodeInfo info(&node);
232 if (node.InitCheck() != B_OK || info.InitCheck() != B_OK)
233 continue;
235 // TODO: we could identify the file in case it doesn't have a type...
236 char type[B_MIME_TYPE_LENGTH];
237 if (info.GetType(type) != B_OK)
238 continue;
240 // check if that type is already in our list
241 bool found = false;
242 for (int32 i = CountItems(); i-- > 0;) {
243 SupportedTypeItem* item = (SupportedTypeItem*)ItemAt(i);
244 if (!strcmp(item->Text(), type)) {
245 found = true;
246 break;
250 if (!found) {
251 // add type
252 AddItem(new SupportedTypeItem(type));
256 SortItems(&SupportedTypeItem::Compare);
257 } else
258 DropTargetListView::MessageReceived(message);
262 bool
263 SupportedTypeListView::AcceptsDrag(const BMessage* message)
265 type_code type;
266 return message->GetInfo("refs", &type) == B_OK && type == B_REF_TYPE;
270 // #pragma mark -
273 ApplicationTypeWindow::ApplicationTypeWindow(BPoint position,
274 const BEntry& entry)
276 BWindow(BRect(0.0f, 0.0f, 250.0f, 340.0f).OffsetBySelf(position),
277 B_TRANSLATE("Application type"), B_TITLED_WINDOW,
278 B_NOT_ZOOMABLE | B_ASYNCHRONOUS_CONTROLS |
279 B_FRAME_EVENTS | B_AUTO_UPDATE_SIZE_LIMITS),
280 fChangedProperties(0)
282 float padding = be_control_look->DefaultItemSpacing();
283 BAlignment labelAlignment = be_control_look->DefaultLabelAlignment();
285 BMenuBar* menuBar = new BMenuBar((char*)NULL);
286 menuBar->SetExplicitAlignment(BAlignment(B_ALIGN_LEFT, B_ALIGN_TOP));
288 BMenu* menu = new BMenu(B_TRANSLATE("File"));
289 fSaveMenuItem = new BMenuItem(B_TRANSLATE("Save"),
290 new BMessage(kMsgSave), 'S');
291 fSaveMenuItem->SetEnabled(false);
292 menu->AddItem(fSaveMenuItem);
293 BMenuItem* item;
294 menu->AddItem(item = new BMenuItem(
295 B_TRANSLATE("Save into resource file" B_UTF8_ELLIPSIS), NULL));
296 item->SetEnabled(false);
298 menu->AddSeparatorItem();
299 menu->AddItem(new BMenuItem(B_TRANSLATE("Close"),
300 new BMessage(B_QUIT_REQUESTED), 'W', B_COMMAND_KEY));
301 menuBar->AddItem(menu);
303 // Signature
305 fSignatureControl = new BTextControl(B_TRANSLATE("Signature:"), NULL,
306 new BMessage(kMsgSignatureChanged));
307 fSignatureControl->SetModificationMessage(
308 new BMessage(kMsgSignatureChanged));
310 // filter out invalid characters that can't be part of a MIME type name
311 BTextView* textView = fSignatureControl->TextView();
312 textView->SetMaxBytes(B_MIME_TYPE_LENGTH);
313 const char* disallowedCharacters = "<>@,;:\"()[]?= ";
314 for (int32 i = 0; disallowedCharacters[i]; i++) {
315 textView->DisallowChar(disallowedCharacters[i]);
318 // "Application Flags" group
320 BBox* flagsBox = new BBox("flagsBox");
322 fFlagsCheckBox = new BCheckBox("flags", B_TRANSLATE("Application flags"),
323 new BMessage(kMsgToggleAppFlags));
324 fFlagsCheckBox->SetValue(B_CONTROL_ON);
326 fSingleLaunchButton = new BRadioButton("single",
327 B_TRANSLATE("Single launch"), new BMessage(kMsgAppFlagsChanged));
329 fMultipleLaunchButton = new BRadioButton("multiple",
330 B_TRANSLATE("Multiple launch"), new BMessage(kMsgAppFlagsChanged));
332 fExclusiveLaunchButton = new BRadioButton("exclusive",
333 B_TRANSLATE("Exclusive launch"), new BMessage(kMsgAppFlagsChanged));
335 fArgsOnlyCheckBox = new BCheckBox("args only", B_TRANSLATE("Args only"),
336 new BMessage(kMsgAppFlagsChanged));
338 fBackgroundAppCheckBox = new BCheckBox("background",
339 B_TRANSLATE("Background app"), new BMessage(kMsgAppFlagsChanged));
341 BLayoutBuilder::Grid<>(flagsBox, 0, 0)
342 .SetInsets(padding, padding * 2, padding, padding)
343 .Add(fSingleLaunchButton, 0, 0).Add(fArgsOnlyCheckBox, 1, 0)
344 .Add(fMultipleLaunchButton, 0, 1).Add(fBackgroundAppCheckBox, 1, 1)
345 .Add(fExclusiveLaunchButton, 0, 2);
346 flagsBox->SetLabel(fFlagsCheckBox);
348 // "Icon" group
350 BBox* iconBox = new BBox("IconBox");
351 iconBox->SetLabel(B_TRANSLATE("Icon"));
352 fIconView = new IconView("icon");
353 fIconView->SetModificationMessage(new BMessage(kMsgIconChanged));
354 BLayoutBuilder::Group<>(iconBox, B_HORIZONTAL)
355 .SetInsets(padding, padding * 2, padding, padding)
356 .Add(fIconView);
358 // "Supported Types" group
360 BBox* typeBox = new BBox("typesBox");
361 typeBox->SetLabel(B_TRANSLATE("Supported types"));
363 fTypeListView = new SupportedTypeListView("Suppported Types",
364 B_SINGLE_SELECTION_LIST);
365 fTypeListView->SetSelectionMessage(new BMessage(kMsgTypeSelected));
367 BScrollView* scrollView = new BScrollView("type scrollview", fTypeListView,
368 B_FRAME_EVENTS | B_WILL_DRAW, false, true);
370 fAddTypeButton = new BButton("add type",
371 B_TRANSLATE("Add" B_UTF8_ELLIPSIS), new BMessage(kMsgAddType));
373 fRemoveTypeButton = new BButton("remove type", B_TRANSLATE("Remove"),
374 new BMessage(kMsgRemoveType));
376 fTypeIconView = new IconView("type icon");
377 BGroupView* iconHolder = new BGroupView(B_HORIZONTAL);
378 iconHolder->AddChild(fTypeIconView);
379 fTypeIconView->SetModificationMessage(new BMessage(kMsgTypeIconsChanged));
381 BLayoutBuilder::Grid<>(typeBox, padding, padding)
382 .SetInsets(padding, padding * 2, padding, padding)
383 .Add(scrollView, 0, 0, 1, 4)
384 .Add(fAddTypeButton, 1, 0, 1, 2)
385 .Add(fRemoveTypeButton, 1, 2, 1, 2)
386 .Add(iconHolder, 2, 1, 1, 2)
387 .SetColumnWeight(0, 3)
388 .SetColumnWeight(1, 2)
389 .SetColumnWeight(2, 1);
391 iconHolder->SetExplicitAlignment(
392 BAlignment(B_ALIGN_CENTER, B_ALIGN_MIDDLE));
394 // "Version Info" group
396 BBox* versionBox = new BBox("versionBox");
397 versionBox->SetLabel(B_TRANSLATE("Version info"));
399 fMajorVersionControl = new BTextControl(B_TRANSLATE("Version:"),
400 NULL, NULL);
401 _MakeNumberTextControl(fMajorVersionControl);
403 fMiddleVersionControl = new BTextControl(".", NULL, NULL);
404 _MakeNumberTextControl(fMiddleVersionControl);
406 fMinorVersionControl = new BTextControl(".", NULL, NULL);
407 _MakeNumberTextControl(fMinorVersionControl);
409 fVarietyMenu = new BPopUpMenu("variety", true, true);
410 fVarietyMenu->AddItem(new BMenuItem(B_TRANSLATE("Development"), NULL));
411 fVarietyMenu->AddItem(new BMenuItem(B_TRANSLATE("Alpha"), NULL));
412 fVarietyMenu->AddItem(new BMenuItem(B_TRANSLATE("Beta"), NULL));
413 fVarietyMenu->AddItem(new BMenuItem(B_TRANSLATE("Gamma"), NULL));
414 item = new BMenuItem(B_TRANSLATE("Golden master"), NULL);
415 fVarietyMenu->AddItem(item);
416 item->SetMarked(true);
417 fVarietyMenu->AddItem(new BMenuItem(B_TRANSLATE("Final"), NULL));
419 BMenuField* varietyField = new BMenuField("", fVarietyMenu);
420 fInternalVersionControl = new BTextControl("/", NULL, NULL);
421 fShortDescriptionControl =
422 new BTextControl(B_TRANSLATE("Short description:"), NULL, NULL);
424 // TODO: workaround for a GCC 4.1.0 bug? Or is that really what the standard says?
425 version_info versionInfo;
426 fShortDescriptionControl->TextView()->SetMaxBytes(
427 sizeof(versionInfo.short_info));
429 BStringView* longLabel = new BStringView(NULL,
430 B_TRANSLATE("Long description:"));
431 longLabel->SetExplicitAlignment(labelAlignment);
432 fLongDescriptionView = new TabFilteringTextView("long desc");
433 fLongDescriptionView->SetMaxBytes(sizeof(versionInfo.long_info));
435 scrollView = new BScrollView("desc scrollview", fLongDescriptionView,
436 B_FRAME_EVENTS | B_WILL_DRAW, false, true);
438 // TODO: remove workaround (bug #5678)
439 BSize minScrollSize = scrollView->ScrollBar(B_VERTICAL)->MinSize();
440 minScrollSize.width += fLongDescriptionView->MinSize().width;
441 scrollView->SetExplicitMinSize(minScrollSize);
443 // Manually set a minimum size for the version text controls
444 // TODO: the same does not work when applied to the layout items
445 float width = be_plain_font->StringWidth("99") + 16;
446 fMajorVersionControl->TextView()->SetExplicitMinSize(
447 BSize(width, fMajorVersionControl->MinSize().height));
448 fMiddleVersionControl->TextView()->SetExplicitMinSize(
449 BSize(width, fMiddleVersionControl->MinSize().height));
450 fMinorVersionControl->TextView()->SetExplicitMinSize(
451 BSize(width, fMinorVersionControl->MinSize().height));
452 fInternalVersionControl->TextView()->SetExplicitMinSize(
453 BSize(width, fInternalVersionControl->MinSize().height));
455 BLayoutBuilder::Grid<>(versionBox, padding / 2, padding / 2)
456 .SetInsets(padding, padding * 2, padding, padding)
457 .Add(fMajorVersionControl->CreateLabelLayoutItem(), 0, 0)
458 .Add(fMajorVersionControl->CreateTextViewLayoutItem(), 1, 0)
459 .Add(fMiddleVersionControl, 2, 0, 2)
460 .Add(fMinorVersionControl, 4, 0, 2)
461 .Add(varietyField, 6, 0, 3)
462 .Add(fInternalVersionControl, 9, 0, 2)
463 .Add(fShortDescriptionControl->CreateLabelLayoutItem(), 0, 1)
464 .Add(fShortDescriptionControl->CreateTextViewLayoutItem(), 1, 1, 10)
465 .Add(longLabel, 0, 2)
466 .Add(scrollView, 1, 2, 10, 3)
467 .SetRowWeight(3, 3);
469 // put it all together
470 BLayoutBuilder::Group<>(this, B_VERTICAL, 0)
471 .SetInsets(0, 0, 0, 0)
472 .Add(menuBar)
473 .AddGroup(B_VERTICAL, padding)
474 .SetInsets(padding, padding, padding, padding)
475 .Add(fSignatureControl)
476 .AddGroup(B_HORIZONTAL, padding)
477 .Add(flagsBox, 3)
478 .Add(iconBox, 1)
479 .End()
480 .Add(typeBox)
481 .Add(versionBox);
483 SetKeyMenuBar(menuBar);
485 fSignatureControl->MakeFocus(true);
486 BMimeType::StartWatching(this);
487 _SetTo(entry);
491 ApplicationTypeWindow::~ApplicationTypeWindow()
493 BMimeType::StopWatching(this);
497 BString
498 ApplicationTypeWindow::_Title(const BEntry& entry)
500 char name[B_FILE_NAME_LENGTH];
501 if (entry.GetName(name) != B_OK)
502 strcpy(name, "\"-\"");
504 BString title = B_TRANSLATE("%1 application type");
505 title.ReplaceFirst("%1", name);
506 return title;
510 void
511 ApplicationTypeWindow::_SetTo(const BEntry& entry)
513 SetTitle(_Title(entry).String());
514 fEntry = entry;
516 // Retrieve Info
518 BFile file(&entry, B_READ_ONLY);
519 if (file.InitCheck() != B_OK)
520 return;
522 BAppFileInfo info(&file);
523 if (info.InitCheck() != B_OK)
524 return;
526 char signature[B_MIME_TYPE_LENGTH];
527 if (info.GetSignature(signature) != B_OK)
528 signature[0] = '\0';
530 bool gotFlags = false;
531 uint32 flags;
532 if (info.GetAppFlags(&flags) == B_OK)
533 gotFlags = true;
534 else
535 flags = B_MULTIPLE_LAUNCH;
537 version_info versionInfo;
538 if (info.GetVersionInfo(&versionInfo, B_APP_VERSION_KIND) != B_OK)
539 memset(&versionInfo, 0, sizeof(version_info));
541 // Set Controls
543 fSignatureControl->SetModificationMessage(NULL);
544 fSignatureControl->SetText(signature);
545 fSignatureControl->SetModificationMessage(
546 new BMessage(kMsgSignatureChanged));
548 // flags
550 switch (flags & (B_SINGLE_LAUNCH | B_MULTIPLE_LAUNCH | B_EXCLUSIVE_LAUNCH)) {
551 case B_SINGLE_LAUNCH:
552 fSingleLaunchButton->SetValue(B_CONTROL_ON);
553 break;
555 case B_EXCLUSIVE_LAUNCH:
556 fExclusiveLaunchButton->SetValue(B_CONTROL_ON);
557 break;
559 case B_MULTIPLE_LAUNCH:
560 default:
561 fMultipleLaunchButton->SetValue(B_CONTROL_ON);
562 break;
565 fArgsOnlyCheckBox->SetValue((flags & B_ARGV_ONLY) != 0);
566 fBackgroundAppCheckBox->SetValue((flags & B_BACKGROUND_APP) != 0);
567 fFlagsCheckBox->SetValue(gotFlags);
569 _UpdateAppFlagsEnabled();
571 // icon
573 entry_ref ref;
574 if (entry.GetRef(&ref) == B_OK)
575 fIcon.SetTo(ref);
576 else
577 fIcon.Unset();
579 fIconView->SetModificationMessage(NULL);
580 fIconView->SetTo(&fIcon);
581 fIconView->SetModificationMessage(new BMessage(kMsgIconChanged));
583 // supported types
585 BMessage supportedTypes;
586 info.GetSupportedTypes(&supportedTypes);
588 for (int32 i = fTypeListView->CountItems(); i-- > 0;) {
589 BListItem* item = fTypeListView->RemoveItem(i);
590 delete item;
593 const char* type;
594 for (int32 i = 0; supportedTypes.FindString("types", i, &type) == B_OK; i++) {
595 SupportedTypeItem* item = new SupportedTypeItem(type);
597 entry_ref ref;
598 if (fEntry.GetRef(&ref) == B_OK)
599 item->SetIcon(ref, type);
601 fTypeListView->AddItem(item);
603 fTypeListView->SortItems(&SupportedTypeItem::Compare);
604 fTypeIconView->SetModificationMessage(NULL);
605 fTypeIconView->SetTo(NULL);
606 fTypeIconView->SetModificationMessage(new BMessage(kMsgTypeIconsChanged));
607 fTypeIconView->SetEnabled(false);
608 fRemoveTypeButton->SetEnabled(false);
610 // version info
612 char text[256];
613 snprintf(text, sizeof(text), "%" B_PRId32, versionInfo.major);
614 fMajorVersionControl->SetText(text);
615 snprintf(text, sizeof(text), "%" B_PRId32, versionInfo.middle);
616 fMiddleVersionControl->SetText(text);
617 snprintf(text, sizeof(text), "%" B_PRId32, versionInfo.minor);
618 fMinorVersionControl->SetText(text);
620 if (versionInfo.variety >= (uint32)fVarietyMenu->CountItems())
621 versionInfo.variety = 0;
622 BMenuItem* item = fVarietyMenu->ItemAt(versionInfo.variety);
623 if (item != NULL)
624 item->SetMarked(true);
626 snprintf(text, sizeof(text), "%" B_PRId32, versionInfo.internal);
627 fInternalVersionControl->SetText(text);
629 fShortDescriptionControl->SetText(versionInfo.short_info);
630 fLongDescriptionView->SetText(versionInfo.long_info);
632 // store original data
634 fOriginalInfo.signature = signature;
635 fOriginalInfo.gotFlags = gotFlags;
636 fOriginalInfo.flags = gotFlags ? flags : 0;
637 fOriginalInfo.versionInfo = versionInfo;
638 fOriginalInfo.supportedTypes = _SupportedTypes();
639 // The list view has the types sorted possibly differently
640 // to the supportedTypes message, so don't use that here, but
641 // get the sorted message instead.
642 fOriginalInfo.iconChanged = false;
643 fOriginalInfo.typeIconsChanged = false;
645 fChangedProperties = 0;
646 _CheckSaveMenuItem(0);
650 void
651 ApplicationTypeWindow::_UpdateAppFlagsEnabled()
653 bool enabled = fFlagsCheckBox->Value() != B_CONTROL_OFF;
655 fSingleLaunchButton->SetEnabled(enabled);
656 fMultipleLaunchButton->SetEnabled(enabled);
657 fExclusiveLaunchButton->SetEnabled(enabled);
658 fArgsOnlyCheckBox->SetEnabled(enabled);
659 fBackgroundAppCheckBox->SetEnabled(enabled);
663 void
664 ApplicationTypeWindow::_MakeNumberTextControl(BTextControl* control)
666 // filter out invalid characters that can't be part of a MIME type name
667 BTextView* textView = control->TextView();
668 textView->SetMaxBytes(10);
670 for (int32 i = 0; i < 256; i++) {
671 if (!isdigit(i))
672 textView->DisallowChar(i);
677 void
678 ApplicationTypeWindow::_Save()
680 BFile file;
681 status_t status = file.SetTo(&fEntry, B_READ_WRITE);
682 if (status != B_OK)
683 return;
685 BAppFileInfo info(&file);
686 status = info.InitCheck();
687 if (status != B_OK)
688 return;
690 // Retrieve Info
692 uint32 flags = 0;
693 bool gotFlags = _Flags(flags);
694 BMessage supportedTypes = _SupportedTypes();
695 version_info versionInfo = _VersionInfo();
697 // Save
699 status = info.SetSignature(fSignatureControl->Text());
700 if (status == B_OK) {
701 if (gotFlags)
702 status = info.SetAppFlags(flags);
703 else
704 status = info.RemoveAppFlags();
706 if (status == B_OK)
707 status = info.SetVersionInfo(&versionInfo, B_APP_VERSION_KIND);
708 if (status == B_OK)
709 fIcon.CopyTo(info, NULL, true);
711 // supported types and their icons
712 if (status == B_OK)
713 status = info.SetSupportedTypes(&supportedTypes);
715 for (int32 i = 0; i < fTypeListView->CountItems(); i++) {
716 SupportedTypeItem* item = dynamic_cast<SupportedTypeItem*>(
717 fTypeListView->ItemAt(i));
719 if (item != NULL)
720 item->Icon().CopyTo(info, item->Type(), true);
723 // reset the saved info
724 fOriginalInfo.signature = fSignatureControl->Text();
725 fOriginalInfo.gotFlags = gotFlags;
726 fOriginalInfo.flags = flags;
727 fOriginalInfo.versionInfo = versionInfo;
728 fOriginalInfo.supportedTypes = supportedTypes;
729 fOriginalInfo.iconChanged = false;
730 fOriginalInfo.typeIconsChanged = false;
732 fChangedProperties = 0;
733 _CheckSaveMenuItem(0);
737 void
738 ApplicationTypeWindow::_CheckSaveMenuItem(uint32 flags)
740 fChangedProperties = _NeedsSaving(flags);
741 fSaveMenuItem->SetEnabled(fChangedProperties != 0);
745 bool
746 operator!=(const version_info& a, const version_info& b)
748 return a.major != b.major || a.middle != b.middle || a.minor != b.minor
749 || a.variety != b.variety || a.internal != b.internal
750 || strcmp(a.short_info, b.short_info) != 0
751 || strcmp(a.long_info, b.long_info) != 0;
755 uint32
756 ApplicationTypeWindow::_NeedsSaving(uint32 _flags) const
758 uint32 flags = fChangedProperties;
759 if (_flags & CHECK_SIGNATUR) {
760 if (fOriginalInfo.signature != fSignatureControl->Text())
761 flags |= CHECK_SIGNATUR;
762 else
763 flags &= ~CHECK_SIGNATUR;
766 if (_flags & CHECK_FLAGS) {
767 uint32 appFlags = 0;
768 bool gotFlags = _Flags(appFlags);
769 if (fOriginalInfo.gotFlags != gotFlags
770 || fOriginalInfo.flags != appFlags) {
771 flags |= CHECK_FLAGS;
772 } else
773 flags &= ~CHECK_FLAGS;
776 if (_flags & CHECK_VERSION) {
777 if (fOriginalInfo.versionInfo != _VersionInfo())
778 flags |= CHECK_VERSION;
779 else
780 flags &= ~CHECK_VERSION;
783 if (_flags & CHECK_ICON) {
784 if (fOriginalInfo.iconChanged)
785 flags |= CHECK_ICON;
786 else
787 flags &= ~CHECK_ICON;
790 if (_flags & CHECK_TYPES) {
791 if (!fOriginalInfo.supportedTypes.HasSameData(_SupportedTypes()))
792 flags |= CHECK_TYPES;
793 else
794 flags &= ~CHECK_TYPES;
797 if (_flags & CHECK_TYPE_ICONS) {
798 if (fOriginalInfo.typeIconsChanged)
799 flags |= CHECK_TYPE_ICONS;
800 else
801 flags &= ~CHECK_TYPE_ICONS;
804 return flags;
808 // #pragma mark -
811 bool
812 ApplicationTypeWindow::_Flags(uint32& flags) const
814 flags = 0;
815 if (fFlagsCheckBox->Value() != B_CONTROL_OFF) {
816 if (fSingleLaunchButton->Value() != B_CONTROL_OFF)
817 flags |= B_SINGLE_LAUNCH;
818 else if (fMultipleLaunchButton->Value() != B_CONTROL_OFF)
819 flags |= B_MULTIPLE_LAUNCH;
820 else if (fExclusiveLaunchButton->Value() != B_CONTROL_OFF)
821 flags |= B_EXCLUSIVE_LAUNCH;
823 if (fArgsOnlyCheckBox->Value() != B_CONTROL_OFF)
824 flags |= B_ARGV_ONLY;
825 if (fBackgroundAppCheckBox->Value() != B_CONTROL_OFF)
826 flags |= B_BACKGROUND_APP;
827 return true;
829 return false;
833 BMessage
834 ApplicationTypeWindow::_SupportedTypes() const
836 BMessage supportedTypes;
837 for (int32 i = 0; i < fTypeListView->CountItems(); i++) {
838 SupportedTypeItem* item = dynamic_cast<SupportedTypeItem*>(
839 fTypeListView->ItemAt(i));
841 if (item != NULL)
842 supportedTypes.AddString("types", item->Type());
844 return supportedTypes;
848 version_info
849 ApplicationTypeWindow::_VersionInfo() const
851 version_info versionInfo;
852 versionInfo.major = atol(fMajorVersionControl->Text());
853 versionInfo.middle = atol(fMiddleVersionControl->Text());
854 versionInfo.minor = atol(fMinorVersionControl->Text());
855 versionInfo.variety = fVarietyMenu->IndexOf(fVarietyMenu->FindMarked());
856 versionInfo.internal = atol(fInternalVersionControl->Text());
857 strlcpy(versionInfo.short_info, fShortDescriptionControl->Text(),
858 sizeof(versionInfo.short_info));
859 strlcpy(versionInfo.long_info, fLongDescriptionView->Text(),
860 sizeof(versionInfo.long_info));
861 return versionInfo;
865 // #pragma mark -
868 void
869 ApplicationTypeWindow::FrameResized(float width, float height)
871 // This works around a flaw of BTextView
872 fLongDescriptionView->SetTextRect(fLongDescriptionView->Bounds());
876 void
877 ApplicationTypeWindow::MessageReceived(BMessage* message)
879 switch (message->what) {
880 case kMsgToggleAppFlags:
881 _UpdateAppFlagsEnabled();
882 _CheckSaveMenuItem(CHECK_FLAGS);
883 break;
885 case kMsgSignatureChanged:
886 _CheckSaveMenuItem(CHECK_SIGNATUR);
887 break;
889 case kMsgAppFlagsChanged:
890 _CheckSaveMenuItem(CHECK_FLAGS);
891 break;
893 case kMsgIconChanged:
894 fOriginalInfo.iconChanged = true;
895 _CheckSaveMenuItem(CHECK_ICON);
896 break;
898 case kMsgTypeIconsChanged:
899 fOriginalInfo.typeIconsChanged = true;
900 _CheckSaveMenuItem(CHECK_TYPE_ICONS);
901 break;
903 case kMsgSave:
904 _Save();
905 break;
907 case kMsgTypeSelected:
909 int32 index;
910 if (message->FindInt32("index", &index) == B_OK) {
911 SupportedTypeItem* item
912 = (SupportedTypeItem*)fTypeListView->ItemAt(index);
914 fTypeIconView->SetModificationMessage(NULL);
915 fTypeIconView->SetTo(item != NULL ? &item->Icon() : NULL);
916 fTypeIconView->SetModificationMessage(
917 new BMessage(kMsgTypeIconsChanged));
918 fTypeIconView->SetEnabled(item != NULL);
919 fRemoveTypeButton->SetEnabled(item != NULL);
921 _CheckSaveMenuItem(CHECK_TYPES);
923 break;
926 case kMsgAddType:
928 BWindow* window = new TypeListWindow(NULL,
929 kMsgTypeAdded, this);
930 window->Show();
931 break;
934 case kMsgTypeAdded:
936 const char* type;
937 if (message->FindString("type", &type) != B_OK)
938 break;
940 // check if this type already exists
942 SupportedTypeItem* newItem = new SupportedTypeItem(type);
943 int32 insertAt = 0;
945 for (int32 i = fTypeListView->CountItems(); i-- > 0;) {
946 SupportedTypeItem* item = dynamic_cast<SupportedTypeItem*>(
947 fTypeListView->ItemAt(i));
948 if (item == NULL)
949 continue;
951 int compare = strcasecmp(item->Type(), type);
952 if (!compare) {
953 // type does already exist, select it and bail out
954 delete newItem;
955 newItem = NULL;
956 fTypeListView->Select(i);
957 break;
959 if (compare < 0)
960 insertAt = i + 1;
963 if (newItem == NULL)
964 break;
966 fTypeListView->AddItem(newItem, insertAt);
967 fTypeListView->Select(insertAt);
969 _CheckSaveMenuItem(CHECK_TYPES);
970 break;
973 case kMsgRemoveType:
975 int32 index = fTypeListView->CurrentSelection();
976 if (index < 0)
977 break;
979 delete fTypeListView->RemoveItem(index);
980 fTypeIconView->SetModificationMessage(NULL);
981 fTypeIconView->SetTo(NULL);
982 fTypeIconView->SetModificationMessage(
983 new BMessage(kMsgTypeIconsChanged));
984 fTypeIconView->SetEnabled(false);
985 fRemoveTypeButton->SetEnabled(false);
987 _CheckSaveMenuItem(CHECK_TYPES);
988 break;
991 case B_SIMPLE_DATA:
993 entry_ref ref;
994 if (message->FindRef("refs", &ref) != B_OK)
995 break;
997 // TODO: add to supported types
998 break;
1001 case B_META_MIME_CHANGED:
1002 const char* type;
1003 int32 which;
1004 if (message->FindString("be:type", &type) != B_OK
1005 || message->FindInt32("be:which", &which) != B_OK)
1006 break;
1008 // TODO: update supported types names
1009 // if (which == B_MIME_TYPE_DELETED)
1011 // _CheckSaveMenuItem(...);
1012 break;
1014 default:
1015 BWindow::MessageReceived(message);
1020 bool
1021 ApplicationTypeWindow::QuitRequested()
1023 if (_NeedsSaving(CHECK_ALL) != 0) {
1024 BAlert* alert = new BAlert(B_TRANSLATE("Save request"),
1025 B_TRANSLATE("Save changes before closing?"),
1026 B_TRANSLATE("Cancel"), B_TRANSLATE("Don't save"),
1027 B_TRANSLATE("Save"), B_WIDTH_AS_USUAL, B_OFFSET_SPACING,
1028 B_WARNING_ALERT);
1029 alert->SetShortcut(0, B_ESCAPE);
1030 alert->SetShortcut(1, 'd');
1031 alert->SetShortcut(2, 's');
1033 int32 choice = alert->Go();
1034 switch (choice) {
1035 case 0:
1036 return false;
1037 case 1:
1038 break;
1039 case 2:
1040 _Save();
1041 break;
1045 be_app->PostMessage(kMsgTypeWindowClosed);
1046 return true;