* Copyright 2006, Axel Dörfler, axeld@pinc-software.de. All rights reserved.
* Distributed under the terms of the MIT License.
*/
#include "ApplicationTypeWindow.h"
#include "DropTargetListView.h"
#include "FileTypes.h"
#include "IconView.h"
#include "PreferredAppMenu.h"
#include "StringView.h"
#include "TypeListWindow.h"
#include <Application.h>
#include <Bitmap.h>
#include <Box.h>
#include <Button.h>
#include <CheckBox.h>
#include <File.h>
#include <ListView.h>
#include <MenuBar.h>
#include <MenuField.h>
#include <MenuItem.h>
#include <Mime.h>
#include <NodeInfo.h>
#include <PopUpMenu.h>
#include <RadioButton.h>
#include <Roster.h>
#include <ScrollView.h>
#include <TextControl.h>
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
const uint32 kMsgSave = 'save';
const uint32 kMsgSignatureChanged = 'sgch';
const uint32 kMsgToggleAppFlags = 'tglf';
const uint32 kMsgAppFlagsChanged = 'afch';
const uint32 kMsgIconChanged = 'icch';
const uint32 kMsgTypeIconsChanged = 'tich';
const uint32 kMsgTypeSelected = 'tpsl';
const uint32 kMsgAddType = 'adtp';
const uint32 kMsgTypeAdded = 'tpad';
const uint32 kMsgRemoveType = 'rmtp';
const uint32 kMsgTypeRemoved = 'tprm';
class TabFilteringTextView : public BTextView {
public:
TabFilteringTextView(BRect frame, const char* name, BRect textRect,
uint32 resizeMask, uint32 flags = B_WILL_DRAW | B_PULSE_NEEDED);
virtual ~TabFilteringTextView();
virtual void KeyDown(const char* bytes, int32 count);
};
TabFilteringTextView::TabFilteringTextView(BRect frame, const char* name,
BRect textRect, uint32 resizeMask, uint32 flags)
: BTextView(frame, name, textRect, resizeMask, flags)
{
}
TabFilteringTextView::~TabFilteringTextView()
{
}
void
TabFilteringTextView::KeyDown(const char* bytes, int32 count)
{
if (bytes[0] == B_TAB)
BView::KeyDown(bytes, count);
else
BTextView::KeyDown(bytes, count);
}
class SupportedTypeItem : public BStringItem {
public:
SupportedTypeItem(const char* type);
~SupportedTypeItem();
const char* Type() const { return fType.String(); }
::Icon& Icon() { return fIcon; }
void SetIcon(::Icon* icon);
void SetIcon(entry_ref& ref, const char* type);
static int Compare(const void* _a, const void* _b);
private:
BString fType;
::Icon fIcon;
};
class SupportedTypeListView : public DropTargetListView {
public:
SupportedTypeListView(BRect frame, const char* name,
list_view_type type = B_SINGLE_SELECTION_LIST,
uint32 resizeMask = B_FOLLOW_LEFT | B_FOLLOW_TOP,
uint32 flags = B_WILL_DRAW | B_FRAME_EVENTS | B_NAVIGABLE);
virtual ~SupportedTypeListView();
virtual void MessageReceived(BMessage* message);
virtual bool AcceptsDrag(const BMessage* message);
};
SupportedTypeItem::SupportedTypeItem(const char* type)
: BStringItem(type),
fType(type)
{
BMimeType mimeType(type);
char description[B_MIME_TYPE_LENGTH];
if (mimeType.GetShortDescription(description) == B_OK && description[0])
SetText(description);
}
SupportedTypeItem::~SupportedTypeItem()
{
}
void
SupportedTypeItem::SetIcon(::Icon* icon)
{
if (icon != NULL)
fIcon = *icon;
else
fIcon.Unset();
}
void
SupportedTypeItem::SetIcon(entry_ref& ref, const char* type)
{
fIcon.SetTo(ref, type);
}
int
SupportedTypeItem::Compare(const void* _a, const void* _b)
{
const SupportedTypeItem* a = *(const SupportedTypeItem**)_a;
const SupportedTypeItem* b = *(const SupportedTypeItem**)_b;
int compare = strcasecmp(a->Text(), b->Text());
if (compare != 0)
return compare;
return strcasecmp(a->Type(), b->Type());
}
SupportedTypeListView::SupportedTypeListView(BRect frame, const char* name,
list_view_type type, uint32 resizeMask, uint32 flags)
: DropTargetListView(frame, name, type, resizeMask, flags)
{
}
SupportedTypeListView::~SupportedTypeListView()
{
}
void
SupportedTypeListView::MessageReceived(BMessage* message)
{
if (message->WasDropped() && AcceptsDrag(message)) {
entry_ref ref;
for (int32 index = 0; message->FindRef("refs", index++, &ref) == B_OK; ) {
BNode node(&ref);
BNodeInfo info(&node);
if (node.InitCheck() != B_OK || info.InitCheck() != B_OK)
continue;
char type[B_MIME_TYPE_LENGTH];
if (info.GetType(type) != B_OK)
continue;
bool found = false;
for (int32 i = CountItems(); i-- > 0;) {
SupportedTypeItem* item = (SupportedTypeItem*)ItemAt(i);
if (!strcmp(item->Text(), type)) {
found = true;
break;
}
}
if (!found) {
AddItem(new SupportedTypeItem(type));
}
}
SortItems(&SupportedTypeItem::Compare);
} else
DropTargetListView::MessageReceived(message);
}
bool
SupportedTypeListView::AcceptsDrag(const BMessage* message)
{
type_code type;
return message->GetInfo("refs", &type) == B_OK && type == B_REF_TYPE;
}
ApplicationTypeWindow::ApplicationTypeWindow(BPoint position, const BEntry& entry)
: BWindow(BRect(0.0f, 0.0f, 250.0f, 340.0f).OffsetBySelf(position),
"Application Type", B_TITLED_WINDOW,
B_NOT_ZOOMABLE | B_ASYNCHRONOUS_CONTROLS),
fChangedProperties(0)
{
BMenuBar* menuBar = new BMenuBar(BRect(0, 0, 0, 0), NULL);
AddChild(menuBar);
BMenu* menu = new BMenu("File");
fSaveMenuItem = new BMenuItem("Save", new BMessage(kMsgSave), 'S');
fSaveMenuItem->SetEnabled(false);
menu->AddItem(fSaveMenuItem);
BMenuItem* item;
menu->AddItem(item = new BMenuItem("Save Into Resource File" B_UTF8_ELLIPSIS,
NULL));
item->SetEnabled(false);
menu->AddSeparatorItem();
menu->AddItem(new BMenuItem("Close", new BMessage(B_QUIT_REQUESTED),
'W', B_COMMAND_KEY));
menuBar->AddItem(menu);
BRect rect = Bounds();
rect.top = menuBar->Bounds().Height() + 1.0f;
BView* topView = new BView(rect, NULL, B_FOLLOW_ALL, B_WILL_DRAW);
topView->SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR));
AddChild(topView);
rect = topView->Bounds().InsetByCopy(8.0f, 8.0f);
fSignatureControl = new BTextControl(rect, "signature", "Signature:", NULL,
new BMessage(kMsgSignatureChanged), B_FOLLOW_LEFT_RIGHT);
fSignatureControl->SetModificationMessage(
new BMessage(kMsgSignatureChanged));
fSignatureControl->SetDivider(fSignatureControl->StringWidth(
fSignatureControl->Label()) + 4.0f);
float width, height;
fSignatureControl->GetPreferredSize(&width, &height);
fSignatureControl->ResizeTo(rect.Width(), height);
topView->AddChild(fSignatureControl);
BTextView* textView = fSignatureControl->TextView();
textView->SetMaxBytes(B_MIME_TYPE_LENGTH);
const char* disallowedCharacters = "<>@,;:\"()[]?=";
for (int32 i = 0; disallowedCharacters[i]; i++) {
textView->DisallowChar(disallowedCharacters[i]);
}
BFont font(be_bold_font);
font_height fontHeight;
font.GetHeight(&fontHeight);
width = font.StringWidth("Icon") + 16.0f;
if (width < B_LARGE_ICON + 16.0f)
width = B_LARGE_ICON + 16.0f;
rect.top = fSignatureControl->Frame().bottom + 4.0f;
rect.bottom = rect.top + 100.0f;
rect.right -= width + 8.0f;
BBox* box = new BBox(rect, NULL, B_FOLLOW_LEFT_RIGHT);
topView->AddChild(box);
fFlagsCheckBox = new BCheckBox(rect, "flags", "Application Flags",
new BMessage(kMsgToggleAppFlags));
fFlagsCheckBox->SetValue(B_CONTROL_ON);
fFlagsCheckBox->ResizeToPreferred();
box->SetLabel(fFlagsCheckBox);
rect.top = fFlagsCheckBox->Bounds().Height() + 4.0f;
fSingleLaunchButton = new BRadioButton(rect, "single", "Single Launch",
new BMessage(kMsgAppFlagsChanged));
fSingleLaunchButton->ResizeToPreferred();
box->AddChild(fSingleLaunchButton);
rect.OffsetBy(0.0f, fSingleLaunchButton->Bounds().Height() + 0.0f);
fMultipleLaunchButton = new BRadioButton(rect, "multiple",
"Multiple Launch", new BMessage(kMsgAppFlagsChanged));
fMultipleLaunchButton->ResizeToPreferred();
box->AddChild(fMultipleLaunchButton);
rect.OffsetBy(0.0f, fSingleLaunchButton->Bounds().Height() + 0.0f);
fExclusiveLaunchButton = new BRadioButton(rect, "exclusive",
"Exclusive Launch", new BMessage(kMsgAppFlagsChanged));
fExclusiveLaunchButton->ResizeToPreferred();
box->AddChild(fExclusiveLaunchButton);
rect.top = fSingleLaunchButton->Frame().top;
rect.left = fExclusiveLaunchButton->Frame().right + 4.0f;
fArgsOnlyCheckBox = new BCheckBox(rect, "args only", "Args Only",
new BMessage(kMsgAppFlagsChanged));
fArgsOnlyCheckBox->ResizeToPreferred();
box->AddChild(fArgsOnlyCheckBox);
rect.top += fArgsOnlyCheckBox->Bounds().Height();
fBackgroundAppCheckBox = new BCheckBox(rect, "background",
"Background App", new BMessage(kMsgAppFlagsChanged));
fBackgroundAppCheckBox->ResizeToPreferred();
box->AddChild(fBackgroundAppCheckBox);
box->ResizeTo(box->Bounds().Width(),
fExclusiveLaunchButton->Frame().bottom + 8.0f);
rect = box->Frame();
#ifdef __HAIKU__
rect.top += box->TopBorderOffset();
#endif
rect.left = rect.right + 8.0f;
rect.right += width + 8.0f;
float iconBoxWidth = rect.Width();
box = new BBox(rect, NULL, B_FOLLOW_RIGHT | B_FOLLOW_TOP);
box->SetLabel("Icon");
#ifdef __HAIKU__
box->MoveBy(0.0f, -box->TopBorderOffset());
box->ResizeBy(0.0f, box->TopBorderOffset());
#endif
topView->AddChild(box);
rect = BRect(8.0f, 0.0f, 7.0f + B_LARGE_ICON, B_LARGE_ICON - 1.0f);
#ifdef __HAIKU__
rect.OffsetBy(0.0f, (box->Bounds().Height() + box->TopBorderOffset()
- rect.Height()) / 2.0f);
#else
rect.OffsetBy(0.0f, (box->Bounds().Height() - rect.Height()) / 2.0f);
#endif
if (rect.top < fontHeight.ascent + fontHeight.descent + 4.0f)
rect.top = fontHeight.ascent + fontHeight.descent + 4.0f;
fIconView = new IconView(rect, "icon");
fIconView->SetModificationMessage(new BMessage(kMsgIconChanged));
box->AddChild(fIconView);
rect.top = box->Frame().bottom + 8.0f;
rect.bottom = rect.top + box->Bounds().Height();
rect.left = 8.0f;
rect.right = Bounds().Width() - 8.0f;
BBox* typeBox = new BBox(rect, NULL, B_FOLLOW_LEFT_RIGHT);
typeBox->SetLabel("Supported Types");
topView->AddChild(typeBox);
rect = typeBox->Bounds().InsetByCopy(8.0f, 6.0f);
rect.top += ceilf(fontHeight.ascent);
fAddTypeButton = new BButton(rect, "add type", "Add" B_UTF8_ELLIPSIS,
new BMessage(kMsgAddType), B_FOLLOW_RIGHT);
fAddTypeButton->ResizeToPreferred();
fAddTypeButton->MoveBy(rect.right - fAddTypeButton->Bounds().Width()
- B_LARGE_ICON - 16.0f, 0.0f);
typeBox->AddChild(fAddTypeButton);
rect = fAddTypeButton->Frame();
rect.OffsetBy(0, rect.Height() + 4.0f);
fRemoveTypeButton = new BButton(rect, "remove type", "Remove",
new BMessage(kMsgRemoveType), B_FOLLOW_RIGHT);
typeBox->AddChild(fRemoveTypeButton);
rect.right = rect.left - 10.0f - B_V_SCROLL_BAR_WIDTH;
rect.left = 10.0f;
rect.top = 8.0f + ceilf(fontHeight.ascent);
rect.bottom -= 2.0f;
fTypeListView = new SupportedTypeListView(rect, "type listview",
B_SINGLE_SELECTION_LIST, B_FOLLOW_ALL);
fTypeListView->SetSelectionMessage(new BMessage(kMsgTypeSelected));
BScrollView* scrollView = new BScrollView("type scrollview", fTypeListView,
B_FOLLOW_ALL, B_FRAME_EVENTS | B_WILL_DRAW, false, true);
typeBox->ResizeTo(typeBox->Bounds().Width(), fRemoveTypeButton->Frame().bottom + 8.0f);
typeBox->AddChild(scrollView);
rect.left = fRemoveTypeButton->Frame().right + 8.0f;
#ifdef __HAIKU__
rect.top = (box->Bounds().Height() + box->TopBorderOffset() - B_LARGE_ICON) / 2.0f;
#else
rect.top = (box->Bounds().Height() - B_LARGE_ICON) / 2.0f;
#endif
rect.right = rect.left + B_LARGE_ICON - 1.0f;
rect.bottom = rect.top + B_LARGE_ICON - 1.0f;
fTypeIconView = new IconView(rect, "type icon", B_FOLLOW_RIGHT | B_FOLLOW_TOP);
fTypeIconView->SetModificationMessage(new BMessage(kMsgTypeIconsChanged));
typeBox->AddChild(fTypeIconView);
rect.top = typeBox->Frame().bottom + 8.0f;
rect.bottom = rect.top + typeBox->Bounds().Height();
rect.left = 8.0f;
rect.right = Bounds().Width() - 8.0f;
box = new BBox(rect, NULL, B_FOLLOW_LEFT_RIGHT);
box->SetLabel("Version Info");
topView->AddChild(box);
BMenuField* menuField;
#if 0
BPopUpMenu *popUpMenu = new BPopUpMenu("version info", true, true);
item = new BMenuItem("Version Info", NULL);
item->SetMarked(true);
popUpMenu->AddItem(item);
item = new BMenuItem("System Version Info", NULL);
popUpMenu->AddItem(item);
menuField = new BMenuField(BRect(0, 0, 100, 15),
"version kind", NULL, popUpMenu, true);
menuField->ResizeToPreferred();
box->SetLabel(menuField);
#endif
rect.top = 4.0f + ceilf(fontHeight.ascent + fontHeight.descent);
rect.bottom = rect.top + height;
fMajorVersionControl = new BTextControl(rect, "major", "Version:", NULL,
NULL);
fMajorVersionControl->SetDivider(fMajorVersionControl->StringWidth(
fMajorVersionControl->Label()) + 4.0f);
fMajorVersionControl->GetPreferredSize(&width, &height);
width = 12.0f + fMajorVersionControl->StringWidth("99");
fMajorVersionControl->ResizeTo(fMajorVersionControl->Divider() + width, height);
_MakeNumberTextControl(fMajorVersionControl);
box->AddChild(fMajorVersionControl);
rect.left = fMajorVersionControl->Frame().right + 1.0f;
fMiddleVersionControl = new BTextControl(rect, "middle", ".", NULL,
NULL);
fMiddleVersionControl->SetDivider(fMiddleVersionControl->StringWidth(
fMiddleVersionControl->Label()) + 4.0f);
fMiddleVersionControl->ResizeTo(fMiddleVersionControl->Divider() + width, height);
_MakeNumberTextControl(fMiddleVersionControl);
box->AddChild(fMiddleVersionControl);
rect.left = fMiddleVersionControl->Frame().right + 1.0f;
fMinorVersionControl = new BTextControl(rect, "middle", ".", NULL,
NULL);
fMinorVersionControl->SetDivider(fMinorVersionControl->StringWidth(
fMinorVersionControl->Label()) + 4.0f);
fMinorVersionControl->ResizeTo(fMinorVersionControl->Divider() + width, height);
_MakeNumberTextControl(fMinorVersionControl);
box->AddChild(fMinorVersionControl);
fVarietyMenu = new BPopUpMenu("variety", true, true);
fVarietyMenu->AddItem(new BMenuItem("Development", NULL));
fVarietyMenu->AddItem(new BMenuItem("Alpha", NULL));
fVarietyMenu->AddItem(new BMenuItem("Beta", NULL));
fVarietyMenu->AddItem(new BMenuItem("Gamma", NULL));
fVarietyMenu->AddItem(item = new BMenuItem("Golden Master", NULL));
item->SetMarked(true);
fVarietyMenu->AddItem(new BMenuItem("Final", NULL));
rect.top--;
rect.left = fMinorVersionControl->Frame().right + 6.0f;
menuField = new BMenuField(rect,
"variety", NULL, fVarietyMenu, true);
menuField->ResizeToPreferred();
box->AddChild(menuField);
rect.top++;
rect.left = menuField->Frame().right;
rect.right = rect.left + 30.0f;
fInternalVersionControl = new BTextControl(rect, "internal", "/", NULL,
NULL);
fInternalVersionControl->SetDivider(fInternalVersionControl->StringWidth(
fInternalVersionControl->Label()) + 4.0f);
fInternalVersionControl->ResizeTo(fInternalVersionControl->Divider() + width, height);
box->AddChild(fInternalVersionControl);
rect = box->Bounds().InsetByCopy(8.0f, 0.0f);
rect.top = fInternalVersionControl->Frame().bottom + 8.0f;
fShortDescriptionControl = new BTextControl(rect, "short desc", "Short Description:",
NULL, NULL, B_FOLLOW_LEFT_RIGHT | B_FOLLOW_TOP);
float labelWidth = fShortDescriptionControl->StringWidth(
fShortDescriptionControl->Label()) + 4.0f;
fShortDescriptionControl->SetDivider(labelWidth);
fShortDescriptionControl->GetPreferredSize(&width, &height);
fShortDescriptionControl->ResizeTo(rect.Width(), height);
version_info versionInfo;
fShortDescriptionControl->TextView()->SetMaxBytes(sizeof(versionInfo.short_info));
box->AddChild(fShortDescriptionControl);
rect.OffsetBy(0.0f, fShortDescriptionControl->Bounds().Height() + 5.0f);
rect.right = rect.left + labelWidth;
StringView* label = new StringView(rect, NULL, "Long Description:", NULL);
label->SetDivider(labelWidth);
box->AddChild(label);
rect.left = rect.right + 3.0f;
rect.top += 1.0f;
rect.right = box->Bounds().Width() - 10.0f - B_V_SCROLL_BAR_WIDTH;
rect.bottom = rect.top + fShortDescriptionControl->Bounds().Height() * 3.0f - 1.0f;
fLongDescriptionView = new TabFilteringTextView(rect, "long desc",
rect.OffsetToCopy(B_ORIGIN), B_FOLLOW_ALL, B_WILL_DRAW | B_FRAME_EVENTS
| B_NAVIGABLE);
fLongDescriptionView->SetMaxBytes(sizeof(versionInfo.long_info));
scrollView = new BScrollView("desc scrollview", fLongDescriptionView,
B_FOLLOW_LEFT_RIGHT | B_FOLLOW_TOP, B_FRAME_EVENTS | B_WILL_DRAW, false, true);
box->ResizeTo(box->Bounds().Width(), scrollView->Frame().bottom + 8.0f);
box->AddChild(scrollView);
width = fInternalVersionControl->Frame().right + 16.0f;
float minWidth = fBackgroundAppCheckBox->Frame().right + iconBoxWidth + 32.0f;
if (width > minWidth)
minWidth = width;
ResizeTo(Bounds().Width() > minWidth ? Bounds().Width() : minWidth,
box->Frame().bottom + topView->Frame().top + 8.0f);
SetSizeLimits(minWidth, 32767.0f, Bounds().Height(), 32767.0f);
typeBox->SetResizingMode(B_FOLLOW_ALL);
box->SetResizingMode(B_FOLLOW_LEFT_RIGHT | B_FOLLOW_BOTTOM);
fSignatureControl->MakeFocus(true);
BMimeType::StartWatching(this);
_SetTo(entry);
}
ApplicationTypeWindow::~ApplicationTypeWindow()
{
BMimeType::StopWatching(this);
}
BString
ApplicationTypeWindow::_Title(const BEntry& entry)
{
char name[B_FILE_NAME_LENGTH];
if (entry.GetName(name) != B_OK)
strcpy(name, "\"-\"");
BString title(name);
title.Append(" Application Type");
return title;
}
void
ApplicationTypeWindow::_SetTo(const BEntry& entry)
{
SetTitle(_Title(entry).String());
fEntry = entry;
BFile file(&entry, B_READ_ONLY);
if (file.InitCheck() != B_OK)
return;
BAppFileInfo info(&file);
if (info.InitCheck() != B_OK)
return;
char signature[B_MIME_TYPE_LENGTH];
if (info.GetSignature(signature) != B_OK)
signature[0] = '\0';
bool gotFlags = false;
uint32 flags;
if (info.GetAppFlags(&flags) == B_OK)
gotFlags = true;
else
flags = B_MULTIPLE_LAUNCH;
version_info versionInfo;
if (info.GetVersionInfo(&versionInfo, B_APP_VERSION_KIND) != B_OK)
memset(&versionInfo, 0, sizeof(version_info));
fSignatureControl->SetModificationMessage(NULL);
fSignatureControl->SetText(signature);
fSignatureControl->SetModificationMessage(
new BMessage(kMsgSignatureChanged));
switch (flags & (B_SINGLE_LAUNCH | B_MULTIPLE_LAUNCH | B_EXCLUSIVE_LAUNCH)) {
case B_SINGLE_LAUNCH:
fSingleLaunchButton->SetValue(B_CONTROL_ON);
break;
case B_EXCLUSIVE_LAUNCH:
fExclusiveLaunchButton->SetValue(B_CONTROL_ON);
break;
case B_MULTIPLE_LAUNCH:
default:
fMultipleLaunchButton->SetValue(B_CONTROL_ON);
break;
}
fArgsOnlyCheckBox->SetValue((flags & B_ARGV_ONLY) != 0);
fBackgroundAppCheckBox->SetValue((flags & B_BACKGROUND_APP) != 0);
fFlagsCheckBox->SetValue(gotFlags);
_UpdateAppFlagsEnabled();
entry_ref ref;
if (entry.GetRef(&ref) == B_OK)
fIcon.SetTo(ref);
else
fIcon.Unset();
fIconView->SetModificationMessage(NULL);
fIconView->SetTo(&fIcon);
fIconView->SetModificationMessage(new BMessage(kMsgIconChanged));
BMessage supportedTypes;
info.GetSupportedTypes(&supportedTypes);
for (int32 i = fTypeListView->CountItems(); i-- > 0;) {
BListItem* item = fTypeListView->RemoveItem(i);
delete item;
}
const char* type;
for (int32 i = 0; supportedTypes.FindString("types", i, &type) == B_OK; i++) {
SupportedTypeItem* item = new SupportedTypeItem(type);
entry_ref ref;
if (fEntry.GetRef(&ref) == B_OK)
item->SetIcon(ref, type);
fTypeListView->AddItem(item);
}
fTypeListView->SortItems(&SupportedTypeItem::Compare);
fTypeIconView->SetModificationMessage(NULL);
fTypeIconView->SetTo(NULL);
fTypeIconView->SetModificationMessage(new BMessage(kMsgTypeIconsChanged));
fTypeIconView->SetEnabled(false);
fRemoveTypeButton->SetEnabled(false);
char text[256];
snprintf(text, sizeof(text), "%ld", versionInfo.major);
fMajorVersionControl->SetText(text);
snprintf(text, sizeof(text), "%ld", versionInfo.middle);
fMiddleVersionControl->SetText(text);
snprintf(text, sizeof(text), "%ld", versionInfo.minor);
fMinorVersionControl->SetText(text);
if (versionInfo.variety >= (uint32)fVarietyMenu->CountItems())
versionInfo.variety = 0;
BMenuItem* item = fVarietyMenu->ItemAt(versionInfo.variety);
if (item != NULL)
item->SetMarked(true);
snprintf(text, sizeof(text), "%ld", versionInfo.internal);
fInternalVersionControl->SetText(text);
fShortDescriptionControl->SetText(versionInfo.short_info);
fLongDescriptionView->SetText(versionInfo.long_info);
fOriginalInfo.signature = signature;
fOriginalInfo.gotFlags = gotFlags;
fOriginalInfo.flags = gotFlags ? flags : 0;
fOriginalInfo.versionInfo = versionInfo;
fOriginalInfo.supportedTypes = _SupportedTypes();
fOriginalInfo.iconChanged = false;
fOriginalInfo.typeIconsChanged = false;
fChangedProperties = 0;
_CheckSaveMenuItem(0);
}
void
ApplicationTypeWindow::_UpdateAppFlagsEnabled()
{
bool enabled = fFlagsCheckBox->Value() != B_CONTROL_OFF;
fSingleLaunchButton->SetEnabled(enabled);
fMultipleLaunchButton->SetEnabled(enabled);
fExclusiveLaunchButton->SetEnabled(enabled);
fArgsOnlyCheckBox->SetEnabled(enabled);
fBackgroundAppCheckBox->SetEnabled(enabled);
}
void
ApplicationTypeWindow::_MakeNumberTextControl(BTextControl* control)
{
BTextView* textView = control->TextView();
textView->SetMaxBytes(10);
for (int32 i = 0; i < 256; i++) {
if (!isdigit(i))
textView->DisallowChar(i);
}
}
void
ApplicationTypeWindow::_Save()
{
BFile file;
status_t status = file.SetTo(&fEntry, B_READ_WRITE);
if (status != B_OK)
return;
BAppFileInfo info(&file);
status = info.InitCheck();
if (status != B_OK)
return;
uint32 flags = 0;
bool gotFlags = _Flags(flags);
BMessage supportedTypes = _SupportedTypes();
version_info versionInfo = _VersionInfo();
status = info.SetSignature(fSignatureControl->Text());
if (status == B_OK) {
if (gotFlags)
status = info.SetAppFlags(flags);
else
status = info.RemoveAppFlags();
}
if (status == B_OK)
status = info.SetVersionInfo(&versionInfo, B_APP_VERSION_KIND);
if (status == B_OK)
fIcon.CopyTo(info, NULL, true);
if (status == B_OK)
status = info.SetSupportedTypes(&supportedTypes);
for (int32 i = 0; i < fTypeListView->CountItems(); i++) {
SupportedTypeItem* item = dynamic_cast<SupportedTypeItem*>(
fTypeListView->ItemAt(i));
if (item != NULL)
item->Icon().CopyTo(info, item->Type(), true);
}
fOriginalInfo.signature = fSignatureControl->Text();
fOriginalInfo.gotFlags = gotFlags;
fOriginalInfo.flags = flags;
fOriginalInfo.versionInfo = versionInfo;
fOriginalInfo.supportedTypes = supportedTypes;
fOriginalInfo.iconChanged = false;
fOriginalInfo.typeIconsChanged = false;
fChangedProperties = 0;
_CheckSaveMenuItem(0);
}
void
ApplicationTypeWindow::_CheckSaveMenuItem(uint32 flags)
{
fChangedProperties = _NeedsSaving(flags);
fSaveMenuItem->SetEnabled(fChangedProperties != 0);
}
bool
operator!=(const version_info& a, const version_info& b)
{
return a.major != b.major || a.middle != b.middle || a.minor != b.minor
|| a.variety != b.variety || a.internal != b.internal
|| strcmp(a.short_info, b.short_info) != 0
|| strcmp(a.long_info, b.long_info) != 0;
}
uint32
ApplicationTypeWindow::_NeedsSaving(uint32 _flags) const
{
uint32 flags = fChangedProperties;
if (_flags & CHECK_SIGNATUR) {
if (fOriginalInfo.signature != fSignatureControl->Text())
flags |= CHECK_SIGNATUR;
else
flags &= ~CHECK_SIGNATUR;
}
if (_flags & CHECK_FLAGS) {
uint32 appFlags = 0;
bool gotFlags = _Flags(appFlags);
if (fOriginalInfo.gotFlags != gotFlags
|| fOriginalInfo.flags != appFlags) {
flags |= CHECK_FLAGS;
} else
flags &= ~CHECK_FLAGS;
}
if (_flags & CHECK_VERSION) {
if (fOriginalInfo.versionInfo != _VersionInfo())
flags |= CHECK_VERSION;
else
flags &= ~CHECK_VERSION;
}
if (_flags & CHECK_ICON) {
if (fOriginalInfo.iconChanged)
flags |= CHECK_ICON;
else
flags &= ~CHECK_ICON;
}
if (_flags & CHECK_TYPES) {
if (!fOriginalInfo.supportedTypes.HasSameData(_SupportedTypes()))
flags |= CHECK_TYPES;
else
flags &= ~CHECK_TYPES;
}
if (_flags & CHECK_TYPE_ICONS) {
if (fOriginalInfo.typeIconsChanged)
flags |= CHECK_TYPE_ICONS;
else
flags &= ~CHECK_TYPE_ICONS;
}
return flags;
}
bool
ApplicationTypeWindow::_Flags(uint32& flags) const
{
flags = 0;
if (fFlagsCheckBox->Value() != B_CONTROL_OFF) {
if (fSingleLaunchButton->Value() != B_CONTROL_OFF)
flags |= B_SINGLE_LAUNCH;
else if (fMultipleLaunchButton->Value() != B_CONTROL_OFF)
flags |= B_MULTIPLE_LAUNCH;
else if (fExclusiveLaunchButton->Value() != B_CONTROL_OFF)
flags |= B_EXCLUSIVE_LAUNCH;
if (fArgsOnlyCheckBox->Value() != B_CONTROL_OFF)
flags |= B_ARGV_ONLY;
if (fBackgroundAppCheckBox->Value() != B_CONTROL_OFF)
flags |= B_BACKGROUND_APP;
return true;
}
return false;
}
BMessage
ApplicationTypeWindow::_SupportedTypes() const
{
BMessage supportedTypes;
for (int32 i = 0; i < fTypeListView->CountItems(); i++) {
SupportedTypeItem* item = dynamic_cast<SupportedTypeItem*>(
fTypeListView->ItemAt(i));
if (item != NULL)
supportedTypes.AddString("types", item->Type());
}
return supportedTypes;
}
version_info
ApplicationTypeWindow::_VersionInfo() const
{
version_info versionInfo;
versionInfo.major = atol(fMajorVersionControl->Text());
versionInfo.middle = atol(fMiddleVersionControl->Text());
versionInfo.minor = atol(fMinorVersionControl->Text());
versionInfo.variety = fVarietyMenu->IndexOf(fVarietyMenu->FindMarked());
versionInfo.internal = atol(fInternalVersionControl->Text());
strlcpy(versionInfo.short_info, fShortDescriptionControl->Text(),
sizeof(versionInfo.short_info));
strlcpy(versionInfo.long_info, fLongDescriptionView->Text(),
sizeof(versionInfo.long_info));
return versionInfo;
}
void
ApplicationTypeWindow::FrameResized(float width, float height)
{
fLongDescriptionView->SetTextRect(fLongDescriptionView->Bounds());
}
void
ApplicationTypeWindow::MessageReceived(BMessage* message)
{
switch (message->what) {
case kMsgToggleAppFlags:
_UpdateAppFlagsEnabled();
_CheckSaveMenuItem(CHECK_FLAGS);
break;
case kMsgSignatureChanged:
_CheckSaveMenuItem(CHECK_SIGNATUR);
break;
case kMsgAppFlagsChanged:
_CheckSaveMenuItem(CHECK_FLAGS);
break;
case kMsgIconChanged:
fOriginalInfo.iconChanged = true;
_CheckSaveMenuItem(CHECK_ICON);
break;
case kMsgTypeIconsChanged:
fOriginalInfo.typeIconsChanged = true;
_CheckSaveMenuItem(CHECK_TYPE_ICONS);
break;
case kMsgSave:
_Save();
break;
case kMsgTypeSelected:
{
int32 index;
if (message->FindInt32("index", &index) == B_OK) {
SupportedTypeItem* item = (SupportedTypeItem*)fTypeListView->ItemAt(index);
fTypeIconView->SetModificationMessage(NULL);
fTypeIconView->SetTo(item != NULL ? &item->Icon() : NULL);
fTypeIconView->SetModificationMessage(
new BMessage(kMsgTypeIconsChanged));
fTypeIconView->SetEnabled(item != NULL);
fRemoveTypeButton->SetEnabled(item != NULL);
_CheckSaveMenuItem(CHECK_TYPES);
}
break;
}
case kMsgAddType:
{
BWindow* window = new TypeListWindow(NULL,
kMsgTypeAdded, this);
window->Show();
break;
}
case kMsgTypeAdded:
{
const char* type;
if (message->FindString("type", &type) != B_OK)
break;
SupportedTypeItem* newItem = new SupportedTypeItem(type);
int32 insertAt = 0;
for (int32 i = fTypeListView->CountItems(); i-- > 0;) {
SupportedTypeItem* item = dynamic_cast<SupportedTypeItem*>(
fTypeListView->ItemAt(i));
if (item == NULL)
continue;
int compare = strcasecmp(item->Type(), type);
if (!compare) {
delete newItem;
newItem = NULL;
fTypeListView->Select(i);
break;
}
if (compare < 0)
insertAt = i + 1;
}
if (newItem == NULL)
break;
fTypeListView->AddItem(newItem, insertAt);
fTypeListView->Select(insertAt);
_CheckSaveMenuItem(CHECK_TYPES);
break;
}
case kMsgRemoveType:
{
int32 index = fTypeListView->CurrentSelection();
if (index < 0)
break;
delete fTypeListView->RemoveItem(index);
fTypeIconView->SetModificationMessage(NULL);
fTypeIconView->SetTo(NULL);
fTypeIconView->SetModificationMessage(
new BMessage(kMsgTypeIconsChanged));
fTypeIconView->SetEnabled(false);
fRemoveTypeButton->SetEnabled(false);
_CheckSaveMenuItem(CHECK_TYPES);
break;
}
case B_SIMPLE_DATA:
{
entry_ref ref;
if (message->FindRef("refs", &ref) != B_OK)
break;
break;
}
case B_META_MIME_CHANGED:
const char* type;
int32 which;
if (message->FindString("be:type", &type) != B_OK
|| message->FindInt32("be:which", &which) != B_OK)
break;
break;
default:
BWindow::MessageReceived(message);
}
}
bool
ApplicationTypeWindow::QuitRequested()
{
if (_NeedsSaving(CHECK_ALL) != 0) {
BAlert* alert = new BAlert("Save Request", "Do you want to save "
"the changes?", "Quit, Don't save", "Cancel", "Save",
B_WIDTH_AS_USUAL, B_WARNING_ALERT);
int32 choice = alert->Go();
switch (choice) {
case 0:
break;
case 1:
return false;
case 2:
_Save();
break;
}
}
be_app->PostMessage(kMsgTypeWindowClosed);
return true;
}