* Copyright 2006-2010, Axel Dörfler, axeld@pinc-software.de.
* Distributed under the terms of the MIT License.
*/
#include "AttributeListView.h"
#include "AttributeWindow.h"
#include "DropTargetListView.h"
#include "ExtensionWindow.h"
#include "FileTypes.h"
#include "FileTypesWindow.h"
#include "IconView.h"
#include "MimeTypeListView.h"
#include "NewFileTypeWindow.h"
#include "PreferredAppMenu.h"
#include "StringView.h"
#include <Alignment.h>
#include <AppFileInfo.h>
#include <Application.h>
#include <Bitmap.h>
#include <Box.h>
#include <Button.h>
#include <Catalog.h>
#include <ControlLook.h>
#include <LayoutBuilder.h>
#include <ListView.h>
#include <Locale.h>
#include <MenuBar.h>
#include <MenuField.h>
#include <MenuItem.h>
#include <Mime.h>
#include <NodeInfo.h>
#include <OutlineListView.h>
#include <PopUpMenu.h>
#include <ScrollView.h>
#include <SpaceLayoutItem.h>
#include <SplitView.h>
#include <TextControl.h>
#include <OverrideAlert.h>
#include <be_apps/Tracker/RecentItems.h>
#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
#undef B_TRANSLATION_CONTEXT
#define B_TRANSLATION_CONTEXT "FileTypes Window"
const uint32 kMsgTypeSelected = 'typs';
const uint32 kMsgAddType = 'atyp';
const uint32 kMsgRemoveType = 'rtyp';
const uint32 kMsgExtensionSelected = 'exts';
const uint32 kMsgExtensionInvoked = 'exti';
const uint32 kMsgAddExtension = 'aext';
const uint32 kMsgRemoveExtension = 'rext';
const uint32 kMsgRuleEntered = 'rule';
const uint32 kMsgAttributeSelected = 'atrs';
const uint32 kMsgAttributeInvoked = 'atri';
const uint32 kMsgAddAttribute = 'aatr';
const uint32 kMsgRemoveAttribute = 'ratr';
const uint32 kMsgMoveUpAttribute = 'muat';
const uint32 kMsgMoveDownAttribute = 'mdat';
const uint32 kMsgPreferredAppChosen = 'papc';
const uint32 kMsgSelectPreferredApp = 'slpa';
const uint32 kMsgSamePreferredAppAs = 'spaa';
const uint32 kMsgPreferredAppOpened = 'paOp';
const uint32 kMsgSamePreferredAppAsOpened = 'spaO';
const uint32 kMsgTypeEntered = 'type';
const uint32 kMsgDescriptionEntered = 'dsce';
const uint32 kMsgToggleIcons = 'tgic';
const uint32 kMsgToggleRule = 'tgrl';
static const char* kAttributeNames[] = {
"attr:public_name",
"attr:name",
"attr:type",
"attr:editable",
"attr:viewable",
"attr:extra",
"attr:alignment",
"attr:width",
"attr:display_as"
};
class TypeIconView : public IconView {
typedef IconView _inherited;
public:
TypeIconView(const char* name);
virtual ~TypeIconView();
virtual void Draw(BRect updateRect);
virtual void GetPreferredSize(float* _width, float* _height);
protected:
virtual BRect BitmapRect() const;
};
class ExtensionListView : public DropTargetListView {
public:
ExtensionListView(const char* name,
list_view_type type = B_SINGLE_SELECTION_LIST,
uint32 flags = B_WILL_DRAW | B_FRAME_EVENTS | B_NAVIGABLE);
virtual ~ExtensionListView();
virtual BSize MinSize();
virtual void MessageReceived(BMessage* message);
virtual bool AcceptsDrag(const BMessage* message);
void SetType(BMimeType* type);
private:
BMimeType fType;
BSize fMinSize;
};
TypeIconView::TypeIconView(const char* name)
: IconView(name)
{
ShowEmptyFrame(false);
SetIconSize((icon_size)48);
}
TypeIconView::~TypeIconView()
{
}
void
TypeIconView::Draw(BRect updateRect)
{
if (!IsEnabled())
return;
IconView::Draw(updateRect);
const char* text = NULL;
switch (IconSource()) {
case kNoIcon:
text = B_TRANSLATE("no icon");
break;
case kApplicationIcon:
text = B_TRANSLATE("(from application)");
break;
case kSupertypeIcon:
text = B_TRANSLATE("(from super type)");
break;
default:
return;
}
SetHighColor(tint_color(ui_color(B_PANEL_BACKGROUND_COLOR),
B_DISABLED_LABEL_TINT));
SetLowColor(ViewColor());
font_height fontHeight;
GetFontHeight(&fontHeight);
const BRect bitmapRect = _inherited::BitmapRect();
float y = fontHeight.ascent;
if (IconSource() == kNoIcon) {
y += (bitmapRect.Height() - fontHeight.ascent - fontHeight.descent) / 2.0f;
} else
y += bitmapRect.Height() + 3.0f;
DrawString(text, BPoint(ceilf((Bounds().Width() - StringWidth(text)) / 2.0f),
ceilf(y)));
}
void
TypeIconView::GetPreferredSize(float* _width, float* _height)
{
const BRect bitmapRect = _inherited::BitmapRect();
if (_width) {
float a = StringWidth(B_TRANSLATE("(from application)"));
float b = StringWidth(B_TRANSLATE("(from super type)"));
float width = max_c(a, b);
if (width < bitmapRect.Width())
width = bitmapRect.Width();
*_width = ceilf(width);
}
if (_height) {
font_height fontHeight;
GetFontHeight(&fontHeight);
*_height = bitmapRect.Height() + 3.0f + ceilf(fontHeight.ascent
+ fontHeight.descent);
}
}
BRect
TypeIconView::BitmapRect() const
{
const BRect bitmapRect = _inherited::BitmapRect();
if (IconSource() == kNoIcon) {
font_height fontHeight;
GetFontHeight(&fontHeight);
float width = StringWidth(B_TRANSLATE("no icon")) + 8.0f;
float height = ceilf(fontHeight.ascent + fontHeight.descent) + 6.0f;
float x = (Bounds().Width() - width) / 2.0f;
float y = ceilf((bitmapRect.Height() - fontHeight.ascent - fontHeight.descent)
/ 2.0f) - 3.0f;
return BRect(x, y, x + width, y + height);
}
float x = (Bounds().Width() - bitmapRect.Width()) / 2.0f;
return BRect(x, 0.0f, x + bitmapRect.Width(), bitmapRect.Height());
}
ExtensionListView::ExtensionListView(const char* name,
list_view_type type, uint32 flags)
:
DropTargetListView(name, type, flags)
{
}
ExtensionListView::~ExtensionListView()
{
}
BSize
ExtensionListView::MinSize()
{
if (!fMinSize.IsWidthSet()) {
BFont font;
GetFont(&font);
fMinSize.width = font.StringWidth(".mmmmm");
font_height height;
font.GetHeight(&height);
fMinSize.height = (height.ascent + height.descent + height.leading) * 3;
}
return fMinSize;
}
void
ExtensionListView::MessageReceived(BMessage* message)
{
if (message->WasDropped() && AcceptsDrag(message)) {
BList list;
entry_ref ref;
for (int32 index = 0; message->FindRef("refs", index, &ref) == B_OK;
index++) {
const char* point = strchr(ref.name, '.');
if (point != NULL && point[1])
list.AddItem(strdup(++point));
}
merge_extensions(fType, list);
for (int32 index = list.CountItems(); index-- > 0;) {
free(list.ItemAt(index));
}
} else
DropTargetListView::MessageReceived(message);
}
bool
ExtensionListView::AcceptsDrag(const BMessage* message)
{
if (fType.Type() == NULL)
return false;
int32 count = 0;
entry_ref ref;
for (int32 index = 0; message->FindRef("refs", index, &ref) == B_OK;
index++) {
const char* point = strchr(ref.name, '.');
if (point != NULL && point[1])
count++;
}
return count > 0;
}
void
ExtensionListView::SetType(BMimeType* type)
{
if (type != NULL)
fType.SetTo(type->Type());
else
fType.Unset();
}
FileTypesWindow::FileTypesWindow(const BMessage& settings)
:
BWindow(_Frame(settings), B_TRANSLATE_SYSTEM_NAME("FileTypes"),
B_TITLED_WINDOW, B_NOT_ZOOMABLE | B_ASYNCHRONOUS_CONTROLS
| B_AUTO_UPDATE_SIZE_LIMITS),
fNewTypeWindow(NULL)
{
bool showIcons;
bool showRule;
if (settings.FindBool("show_icons", &showIcons) != B_OK)
showIcons = true;
if (settings.FindBool("show_rule", &showRule) != B_OK)
showRule = false;
float padding = be_control_look->DefaultItemSpacing();
BAlignment labelAlignment = be_control_look->DefaultLabelAlignment();
BAlignment fullAlignment(B_ALIGN_USE_FULL_WIDTH, B_ALIGN_USE_FULL_HEIGHT);
BMenuBar* menuBar = new BMenuBar("");
BMenu* menu = new BMenu(B_TRANSLATE("File"));
BMenuItem* item = new BMenuItem(
B_TRANSLATE("New resource file" B_UTF8_ELLIPSIS), NULL, 'N',
B_COMMAND_KEY);
item->SetEnabled(false);
menu->AddItem(item);
BMenu* recentsMenu = BRecentFilesList::NewFileListMenu(
B_TRANSLATE("Open" B_UTF8_ELLIPSIS), NULL, NULL,
be_app, 10, false, NULL, kSignature);
item = new BMenuItem(recentsMenu, new BMessage(kMsgOpenFilePanel));
item->SetShortcut('O', B_COMMAND_KEY);
menu->AddItem(item);
menu->AddItem(new BMenuItem(
B_TRANSLATE("Application types" B_UTF8_ELLIPSIS),
new BMessage(kMsgOpenApplicationTypesWindow)));
menu->AddSeparatorItem();
menu->AddItem(new BMenuItem(B_TRANSLATE("Quit"),
new BMessage(B_QUIT_REQUESTED), 'Q', B_COMMAND_KEY));
menu->SetTargetForItems(be_app);
menuBar->AddItem(menu);
menu = new BMenu(B_TRANSLATE("Settings"));
item = new BMenuItem(B_TRANSLATE("Show icons in list"),
new BMessage(kMsgToggleIcons));
item->SetMarked(showIcons);
item->SetTarget(this);
menu->AddItem(item);
item = new BMenuItem(B_TRANSLATE("Show recognition rule"),
new BMessage(kMsgToggleRule));
item->SetMarked(showRule);
item->SetTarget(this);
menu->AddItem(item);
menuBar->AddItem(menu);
menuBar->SetExplicitAlignment(BAlignment(B_ALIGN_LEFT, B_ALIGN_TOP));
BButton* addTypeButton = new BButton("add",
B_TRANSLATE("Add" B_UTF8_ELLIPSIS), new BMessage(kMsgAddType));
fRemoveTypeButton = new BButton("remove", B_TRANSLATE("Remove"),
new BMessage(kMsgRemoveType) );
fTypeListView = new MimeTypeListView("typeview", NULL, showIcons, false);
fTypeListView->SetSelectionMessage(new BMessage(kMsgTypeSelected));
fTypeListView->SetExplicitMinSize(BSize(200, B_SIZE_UNSET));
BScrollView* typeListScrollView = new BScrollView("scrollview",
fTypeListView, B_FRAME_EVENTS | B_WILL_DRAW, false, true);
fIconView = new TypeIconView("icon");
fIconBox = new BBox("Icon BBox");
fIconBox->SetLabel(B_TRANSLATE("Icon"));
BLayoutBuilder::Group<>(fIconBox, B_VERTICAL, padding)
.SetInsets(padding)
.AddGlue(1)
.Add(fIconView, 3)
.AddGlue(1);
fRecognitionBox = new BBox("Recognition Box");
fRecognitionBox->SetLabel(B_TRANSLATE("File recognition"));
fRecognitionBox->SetExplicitAlignment(fullAlignment);
fExtensionLabel = new StringView(B_TRANSLATE("Extensions:"), NULL);
fExtensionLabel->LabelView()->SetExplicitAlignment(labelAlignment);
fAddExtensionButton = new BButton("add ext",
B_TRANSLATE("Add" B_UTF8_ELLIPSIS), new BMessage(kMsgAddExtension));
fAddExtensionButton->SetExplicitMaxSize(
BSize(B_SIZE_UNLIMITED, B_SIZE_UNSET));
fRemoveExtensionButton = new BButton("remove ext", B_TRANSLATE("Remove"),
new BMessage(kMsgRemoveExtension));
fExtensionListView = new ExtensionListView("listview ext",
B_SINGLE_SELECTION_LIST);
fExtensionListView->SetSelectionMessage(
new BMessage(kMsgExtensionSelected));
fExtensionListView->SetInvocationMessage(
new BMessage(kMsgExtensionInvoked));
BScrollView* scrollView = new BScrollView("scrollview ext",
fExtensionListView, B_FRAME_EVENTS | B_WILL_DRAW, false, true);
fRuleControl = new BTextControl("rule", B_TRANSLATE("Rule:"), "",
new BMessage(kMsgRuleEntered));
fRuleControl->SetAlignment(B_ALIGN_RIGHT, B_ALIGN_LEFT);
fRuleControl->Hide();
BLayoutBuilder::Grid<>(fRecognitionBox, padding, padding / 2)
.SetInsets(padding, padding * 2, padding, padding)
.Add(fExtensionLabel->LabelView(), 0, 0)
.Add(scrollView, 0, 1, 2, 2)
.Add(fAddExtensionButton, 2, 1)
.Add(fRemoveExtensionButton, 2, 2)
.Add(fRuleControl, 0, 3, 3, 1);
fDescriptionBox = new BBox("description BBox");
fDescriptionBox->SetLabel(B_TRANSLATE("Description"));
fDescriptionBox->SetExplicitAlignment(fullAlignment);
fInternalNameView = new StringView(B_TRANSLATE("Internal name:"), NULL);
fInternalNameView->SetEnabled(false);
fTypeNameControl = new BTextControl("type", B_TRANSLATE("Type name:"), "",
new BMessage(kMsgTypeEntered));
fDescriptionControl = new BTextControl("description",
B_TRANSLATE("Description:"), "", new BMessage(kMsgDescriptionEntered));
BLayoutBuilder::Grid<>(fDescriptionBox, padding / 2, padding / 2)
.SetInsets(padding, padding * 2, padding, padding)
.Add(fInternalNameView->LabelView(), 0, 0)
.Add(fInternalNameView->TextView(), 1, 0)
.Add(fTypeNameControl->CreateLabelLayoutItem(), 0, 1)
.Add(fTypeNameControl->CreateTextViewLayoutItem(), 1, 1, 2)
.Add(fDescriptionControl->CreateLabelLayoutItem(), 0, 2)
.Add(fDescriptionControl->CreateTextViewLayoutItem(), 1, 2, 2);
fPreferredBox = new BBox("preferred BBox");
fPreferredBox->SetLabel(B_TRANSLATE("Preferred application"));
menu = new BPopUpMenu("preferred");
menu->AddItem(item = new BMenuItem(B_TRANSLATE("None"),
new BMessage(kMsgPreferredAppChosen)));
item->SetMarked(true);
fPreferredField = new BMenuField("preferred", (char*)NULL, menu);
fSelectButton = new BButton("select",
B_TRANSLATE("Select" B_UTF8_ELLIPSIS),
new BMessage(kMsgSelectPreferredApp));
fSameAsButton = new BButton("same as",
B_TRANSLATE("Same as" B_UTF8_ELLIPSIS),
new BMessage(kMsgSamePreferredAppAs));
BLayoutBuilder::Group<>(fPreferredBox, B_HORIZONTAL, padding)
.SetInsets(padding, padding * 2, padding, padding)
.Add(fPreferredField)
.Add(fSelectButton)
.Add(fSameAsButton);
fAttributeBox = new BBox("Attribute Box");
fAttributeBox->SetLabel(B_TRANSLATE("Extra attributes"));
fAddAttributeButton = new BButton("add attr",
B_TRANSLATE("Add" B_UTF8_ELLIPSIS), new BMessage(kMsgAddAttribute));
fAddAttributeButton->SetExplicitMaxSize(
BSize(B_SIZE_UNLIMITED, B_SIZE_UNSET));
fRemoveAttributeButton = new BButton("remove attr", B_TRANSLATE("Remove"),
new BMessage(kMsgRemoveAttribute));
fRemoveAttributeButton->SetExplicitMaxSize(
BSize(B_SIZE_UNLIMITED, B_SIZE_UNSET));
fMoveUpAttributeButton = new BButton("move up attr", B_TRANSLATE("Move up"),
new BMessage(kMsgMoveUpAttribute));
fMoveUpAttributeButton->SetExplicitMaxSize(
BSize(B_SIZE_UNLIMITED, B_SIZE_UNSET));
fMoveDownAttributeButton = new BButton("move down attr",
B_TRANSLATE("Move down"), new BMessage(kMsgMoveDownAttribute));
fMoveDownAttributeButton->SetExplicitMaxSize(
BSize(B_SIZE_UNLIMITED, B_SIZE_UNSET));
fAttributeListView = new AttributeListView("listview attr");
fAttributeListView->SetSelectionMessage(
new BMessage(kMsgAttributeSelected));
fAttributeListView->SetInvocationMessage(
new BMessage(kMsgAttributeInvoked));
BScrollView* attributesScroller = new BScrollView("scrollview attr",
fAttributeListView, B_FRAME_EVENTS | B_WILL_DRAW, false, true);
BLayoutBuilder::Group<>(fAttributeBox, B_HORIZONTAL, padding)
.SetInsets(padding, padding * 2, padding, padding)
.Add(attributesScroller, 1.0f)
.AddGroup(B_VERTICAL, padding / 2, 0.0f)
.SetInsets(0)
.Add(fAddAttributeButton)
.Add(fRemoveAttributeButton)
.AddStrut(padding)
.Add(fMoveUpAttributeButton)
.Add(fMoveDownAttributeButton)
.AddGlue();
fMainSplitView = new BSplitView(B_HORIZONTAL, floorf(padding / 2));
BLayoutBuilder::Group<>(this, B_VERTICAL, 0)
.SetInsets(0)
.Add(menuBar)
.AddGroup(B_HORIZONTAL, 0)
.SetInsets(B_USE_WINDOW_SPACING)
.AddSplit(fMainSplitView)
.AddGroup(B_VERTICAL, padding)
.Add(typeListScrollView)
.AddGroup(B_HORIZONTAL, padding)
.Add(addTypeButton)
.Add(fRemoveTypeButton)
.AddGlue()
.End()
.End()
.AddGroup(B_VERTICAL, padding)
.AddGroup(B_HORIZONTAL, padding)
.Add(fIconBox, 1)
.Add(fRecognitionBox, 3)
.End()
.Add(fDescriptionBox)
.Add(fPreferredBox)
.Add(fAttributeBox, 5);
_SetType(NULL);
_ShowSnifferRule(showRule);
float leftWeight;
float rightWeight;
if (settings.FindFloat("left_split_weight", &leftWeight) != B_OK
|| settings.FindFloat("right_split_weight", &rightWeight) != B_OK) {
leftWeight = 0.2;
rightWeight = 1.0 - leftWeight;
}
fMainSplitView->SetItemWeight(0, leftWeight, false);
fMainSplitView->SetItemWeight(1, rightWeight, true);
BMimeType::StartWatching(this);
}
FileTypesWindow::~FileTypesWindow()
{
BMimeType::StopWatching(this);
}
void
FileTypesWindow::MessageReceived(BMessage* message)
{
switch (message->what) {
case B_SIMPLE_DATA:
{
type_code type;
if (message->GetInfo("refs", &type) == B_OK
&& type == B_REF_TYPE) {
be_app->PostMessage(message);
}
break;
}
case kMsgToggleIcons:
{
BMenuItem* item;
if (message->FindPointer("source", (void **)&item) != B_OK)
break;
item->SetMarked(!fTypeListView->IsShowingIcons());
fTypeListView->ShowIcons(item->IsMarked());
BMessage update(kMsgSettingsChanged);
update.AddBool("show_icons", item->IsMarked());
be_app_messenger.SendMessage(&update);
break;
}
case kMsgToggleRule:
{
BMenuItem* item;
if (message->FindPointer("source", (void **)&item) != B_OK)
break;
item->SetMarked(fRuleControl->IsHidden());
_ShowSnifferRule(item->IsMarked());
BMessage update(kMsgSettingsChanged);
update.AddBool("show_rule", item->IsMarked());
be_app_messenger.SendMessage(&update);
break;
}
case kMsgTypeSelected:
{
int32 index;
if (message->FindInt32("index", &index) == B_OK) {
MimeTypeItem* item
= (MimeTypeItem*)fTypeListView->ItemAt(index);
if (item != NULL) {
BMimeType type(item->Type());
_SetType(&type);
} else
_SetType(NULL);
}
break;
}
case kMsgAddType:
if (fNewTypeWindow == NULL) {
fNewTypeWindow
= new NewFileTypeWindow(this, fCurrentType.Type());
fNewTypeWindow->Show();
} else
fNewTypeWindow->Activate();
break;
case kMsgNewTypeWindowClosed:
fNewTypeWindow = NULL;
break;
case kMsgRemoveType:
{
if (fCurrentType.Type() == NULL)
break;
BAlert* alert;
if (fCurrentType.IsSupertypeOnly()) {
alert = new BPrivate::OverrideAlert(
B_TRANSLATE("FileTypes request"),
B_TRANSLATE("Removing a super type cannot be reverted.\n"
"All file types that belong to this super type "
"will be lost!\n\n"
"Are you sure you want to do this? To remove the whole "
"group, hold down the Shift key and press \"Remove\"."),
B_TRANSLATE("Remove"), B_SHIFT_KEY, B_TRANSLATE("Cancel"),
0, NULL, 0, B_WIDTH_AS_USUAL, B_STOP_ALERT);
alert->SetShortcut(1, B_ESCAPE);
} else {
alert = new BAlert(B_TRANSLATE("FileTypes request"),
B_TRANSLATE("Removing a file type cannot be reverted.\n"
"Are you sure you want to remove it?"),
B_TRANSLATE("Remove"), B_TRANSLATE("Cancel"),
NULL, B_WIDTH_AS_USUAL, B_WARNING_ALERT);
alert->SetShortcut(1, B_ESCAPE);
}
if (alert->Go())
break;
status_t status = fCurrentType.Delete();
if (status != B_OK) {
fprintf(stderr, B_TRANSLATE(
"Could not remove file type: %s\n"), strerror(status));
}
break;
}
case kMsgSelectNewType:
{
const char* type;
if (message->FindString("type", &type) == B_OK)
fTypeListView->SelectNewType(type);
break;
}
case kMsgExtensionSelected:
{
int32 index;
if (message->FindInt32("index", &index) == B_OK) {
BStringItem* item
= (BStringItem*)fExtensionListView->ItemAt(index);
fRemoveExtensionButton->SetEnabled(item != NULL);
}
break;
}
case kMsgExtensionInvoked:
{
if (fCurrentType.Type() == NULL)
break;
int32 index;
if (message->FindInt32("index", &index) == B_OK) {
BStringItem* item
= (BStringItem*)fExtensionListView->ItemAt(index);
if (item == NULL)
break;
BWindow* window
= new ExtensionWindow(this, fCurrentType, item->Text());
window->Show();
}
break;
}
case kMsgAddExtension:
{
if (fCurrentType.Type() == NULL)
break;
BWindow* window = new ExtensionWindow(this, fCurrentType, NULL);
window->Show();
break;
}
case kMsgRemoveExtension:
{
int32 index = fExtensionListView->CurrentSelection();
if (index < 0 || fCurrentType.Type() == NULL)
break;
BMessage extensions;
if (fCurrentType.GetFileExtensions(&extensions) == B_OK) {
extensions.RemoveData("extensions", index);
fCurrentType.SetFileExtensions(&extensions);
}
break;
}
case kMsgRuleEntered:
{
BString parseError;
if (BMimeType::CheckSnifferRule(fRuleControl->Text(),
&parseError) != B_OK) {
parseError.Prepend(
B_TRANSLATE("Recognition rule is not valid:\n\n"));
error_alert(parseError.String());
} else
fCurrentType.SetSnifferRule(fRuleControl->Text());
break;
}
case kMsgTypeEntered:
{
fCurrentType.SetShortDescription(fTypeNameControl->Text());
break;
}
case kMsgDescriptionEntered:
{
fCurrentType.SetLongDescription(fDescriptionControl->Text());
break;
}
case kMsgPreferredAppChosen:
{
const char* signature;
if (message->FindString("signature", &signature) != B_OK)
signature = NULL;
fCurrentType.SetPreferredApp(signature);
break;
}
case kMsgSelectPreferredApp:
{
BMessage panel(kMsgOpenFilePanel);
panel.AddString("title",
B_TRANSLATE("Select preferred application"));
panel.AddInt32("message", kMsgPreferredAppOpened);
panel.AddMessenger("target", this);
be_app_messenger.SendMessage(&panel);
break;
}
case kMsgPreferredAppOpened:
_AdoptPreferredApplication(message, false);
break;
case kMsgSamePreferredAppAs:
{
BMessage panel(kMsgOpenFilePanel);
panel.AddString("title",
B_TRANSLATE("Select same preferred application as"));
panel.AddInt32("message", kMsgSamePreferredAppAsOpened);
panel.AddMessenger("target", this);
panel.AddBool("allowDirs", true);
be_app_messenger.SendMessage(&panel);
break;
}
case kMsgSamePreferredAppAsOpened:
_AdoptPreferredApplication(message, true);
break;
case kMsgAttributeSelected:
{
int32 index;
if (message->FindInt32("index", &index) == B_OK) {
AttributeItem* item
= (AttributeItem*)fAttributeListView->ItemAt(index);
fRemoveAttributeButton->SetEnabled(item != NULL);
fMoveUpAttributeButton->SetEnabled(index > 0);
fMoveDownAttributeButton->SetEnabled(index >= 0
&& index < fAttributeListView->CountItems() - 1);
}
break;
}
case kMsgAttributeInvoked:
{
if (fCurrentType.Type() == NULL)
break;
int32 index;
if (message->FindInt32("index", &index) == B_OK) {
AttributeItem* item
= (AttributeItem*)fAttributeListView->ItemAt(index);
if (item == NULL)
break;
BWindow* window = new AttributeWindow(this, fCurrentType,
item);
window->Show();
}
break;
}
case kMsgAddAttribute:
{
if (fCurrentType.Type() == NULL)
break;
BWindow* window = new AttributeWindow(this, fCurrentType, NULL);
window->Show();
break;
}
case kMsgRemoveAttribute:
{
int32 index = fAttributeListView->CurrentSelection();
if (index < 0 || fCurrentType.Type() == NULL)
break;
BMessage attributes;
if (fCurrentType.GetAttrInfo(&attributes) == B_OK) {
for (uint32 i = 0; i <
sizeof(kAttributeNames) / sizeof(kAttributeNames[0]);
i++) {
attributes.RemoveData(kAttributeNames[i], index);
}
fCurrentType.SetAttrInfo(&attributes);
}
break;
}
case kMsgMoveUpAttribute:
{
int32 index = fAttributeListView->CurrentSelection();
if (index < 1 || fCurrentType.Type() == NULL)
break;
_MoveUpAttributeIndex(index);
break;
}
case kMsgMoveDownAttribute:
{
int32 index = fAttributeListView->CurrentSelection();
if (index < 0 || index == fAttributeListView->CountItems() - 1
|| fCurrentType.Type() == NULL) {
break;
}
_MoveUpAttributeIndex(index + 1);
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;
if (fCurrentType.Type() == NULL)
break;
if (!strcasecmp(fCurrentType.Type(), type)) {
if (which != B_MIME_TYPE_DELETED)
_SetType(&fCurrentType, which);
else
_SetType(NULL);
} else {
if (which == B_MIME_TYPE_DELETED
|| which == B_SUPPORTED_TYPES_CHANGED
|| which == B_PREFERRED_APP_CHANGED) {
_UpdatePreferredApps(&fCurrentType);
}
}
break;
}
default:
BWindow::MessageReceived(message);
}
}
void
FileTypesWindow::SelectType(const char* type)
{
fTypeListView->SelectType(type);
}
bool
FileTypesWindow::QuitRequested()
{
BMessage update(kMsgSettingsChanged);
update.AddRect("file_types_frame", Frame());
update.AddFloat("left_split_weight", fMainSplitView->ItemWeight((int32)0));
update.AddFloat("right_split_weight", fMainSplitView->ItemWeight(1));
be_app_messenger.SendMessage(&update);
be_app->PostMessage(kMsgTypesWindowClosed);
return true;
}
void
FileTypesWindow::PlaceSubWindow(BWindow* window)
{
window->MoveTo(Frame().left + (Frame().Width() - window->Frame().Width())
/ 2.0f, Frame().top + (Frame().Height() - window->Frame().Height())
/ 2.0f);
}
BRect
FileTypesWindow::_Frame(const BMessage& settings) const
{
BRect rect;
if (settings.FindRect("file_types_frame", &rect) == B_OK)
return rect;
return BRect(80.0f, 80.0f, 0.0f, 0.0f);
}
void
FileTypesWindow::_ShowSnifferRule(bool show)
{
if (fRuleControl->IsHidden() == !show)
return;
if (!show)
fRuleControl->Hide();
else
fRuleControl->Show();
}
void
FileTypesWindow::_UpdateExtensions(BMimeType* type)
{
for (int32 i = fExtensionListView->CountItems(); i-- > 0;) {
delete fExtensionListView->ItemAt(i);
}
fExtensionListView->MakeEmpty();
if (type == NULL)
return;
BMessage extensions;
if (type->GetFileExtensions(&extensions) != B_OK)
return;
const char* extension;
int32 i = 0;
while (extensions.FindString("extensions", i++, &extension) == B_OK) {
char dotExtension[B_FILE_NAME_LENGTH];
snprintf(dotExtension, B_FILE_NAME_LENGTH, ".%s", extension);
fExtensionListView->AddItem(new BStringItem(dotExtension));
}
}
void
FileTypesWindow::_AdoptPreferredApplication(BMessage* message, bool sameAs)
{
if (fCurrentType.Type() == NULL)
return;
BString preferred;
if (retrieve_preferred_app(message, sameAs, fCurrentType.Type(), preferred)
!= B_OK) {
return;
}
status_t status = fCurrentType.SetPreferredApp(preferred.String());
if (status != B_OK)
error_alert(B_TRANSLATE("Could not set preferred application"),
status);
}
void
FileTypesWindow::_UpdatePreferredApps(BMimeType* type)
{
update_preferred_app_menu(fPreferredField->Menu(), type,
kMsgPreferredAppChosen);
}
void
FileTypesWindow::_UpdateIcon(BMimeType* type)
{
if (type != NULL)
fIconView->SetTo(*type);
else
fIconView->Unset();
}
void
FileTypesWindow::_SetType(BMimeType* type, int32 forceUpdate)
{
bool enabled = type != NULL;
if (type != NULL) {
if (fCurrentType == *type) {
if (!forceUpdate)
return;
} else
forceUpdate = B_EVERYTHING_CHANGED;
if (&fCurrentType != type)
fCurrentType.SetTo(type->Type());
fInternalNameView->SetText(type->Type());
char description[B_MIME_TYPE_LENGTH];
if ((forceUpdate & B_SHORT_DESCRIPTION_CHANGED) != 0) {
if (type->GetShortDescription(description) != B_OK)
description[0] = '\0';
fTypeNameControl->SetText(description);
}
if ((forceUpdate & B_LONG_DESCRIPTION_CHANGED) != 0) {
if (type->GetLongDescription(description) != B_OK)
description[0] = '\0';
fDescriptionControl->SetText(description);
}
if ((forceUpdate & B_SNIFFER_RULE_CHANGED) != 0) {
BString rule;
if (type->GetSnifferRule(&rule) != B_OK)
rule = "";
fRuleControl->SetText(rule.String());
}
fExtensionListView->SetType(&fCurrentType);
} else {
fCurrentType.Unset();
fInternalNameView->SetText(NULL);
fTypeNameControl->SetText(NULL);
fDescriptionControl->SetText(NULL);
fRuleControl->SetText(NULL);
fPreferredField->Menu()->ItemAt(0)->SetMarked(true);
fExtensionListView->SetType(NULL);
fAttributeListView->SetTo(NULL);
}
if ((forceUpdate & B_FILE_EXTENSIONS_CHANGED) != 0)
_UpdateExtensions(type);
if ((forceUpdate & B_PREFERRED_APP_CHANGED) != 0)
_UpdatePreferredApps(type);
if ((forceUpdate & (B_ICON_CHANGED | B_PREFERRED_APP_CHANGED)) != 0)
_UpdateIcon(type);
if ((forceUpdate & B_ATTR_INFO_CHANGED) != 0)
fAttributeListView->SetTo(type);
fIconView->SetEnabled(enabled);
fInternalNameView->SetEnabled(enabled);
fTypeNameControl->SetEnabled(enabled);
fDescriptionControl->SetEnabled(enabled);
fPreferredField->SetEnabled(enabled);
fRemoveTypeButton->SetEnabled(enabled);
fSelectButton->SetEnabled(enabled);
fSameAsButton->SetEnabled(enabled);
fExtensionLabel->SetEnabled(enabled);
fAddExtensionButton->SetEnabled(enabled);
fRemoveExtensionButton->SetEnabled(false);
fRuleControl->SetEnabled(enabled);
fAddAttributeButton->SetEnabled(enabled);
fRemoveAttributeButton->SetEnabled(false);
fMoveUpAttributeButton->SetEnabled(false);
fMoveDownAttributeButton->SetEnabled(false);
}
void
FileTypesWindow::_MoveUpAttributeIndex(int32 index)
{
BMessage attributes;
if (fCurrentType.GetAttrInfo(&attributes) != B_OK)
return;
BMessage resortedAttributes;
for (uint32 i = 0; i <
sizeof(kAttributeNames) / sizeof(kAttributeNames[0]);
i++) {
type_code type;
int32 count;
bool isFixedSize;
if (attributes.GetInfo(kAttributeNames[i], &type, &count,
&isFixedSize) != B_OK) {
continue;
}
for (int32 j = 0; j < count; j++) {
const void* data;
ssize_t size;
int32 originalIndex;
if (j == index - 1)
originalIndex = j + 1;
else if (j == index)
originalIndex = j - 1;
else
originalIndex = j;
attributes.FindData(kAttributeNames[i], type,
originalIndex, &data, &size);
if (j == 0) {
resortedAttributes.AddData(kAttributeNames[i], type,
data, size, isFixedSize);
} else {
resortedAttributes.AddData(kAttributeNames[i], type,
data, size);
}
}
}
fCurrentType.SetAttrInfo(&resortedAttributes);
}