* Copyright 2006-2010, Axel Dörfler, axeld@pinc-software.de.
* Distributed under the terms of the MIT License.
*/
#include "FileTypes.h"
#include "FileTypeWindow.h"
#include "IconView.h"
#include "PreferredAppMenu.h"
#include "TypeListWindow.h"
#include <Application.h>
#include <Bitmap.h>
#include <Box.h>
#include <Button.h>
#include <Catalog.h>
#include <ControlLook.h>
#include <File.h>
#include <LayoutBuilder.h>
#include <Locale.h>
#include <MenuField.h>
#include <MenuItem.h>
#include <Mime.h>
#include <NodeInfo.h>
#include <PopUpMenu.h>
#include <SpaceLayoutItem.h>
#include <TextControl.h>
#include <stdio.h>
#undef B_TRANSLATION_CONTEXT
#define B_TRANSLATION_CONTEXT "FileType Window"
const uint32 kMsgTypeEntered = 'type';
const uint32 kMsgSelectType = 'sltp';
const uint32 kMsgTypeSelected = 'tpsd';
const uint32 kMsgSameTypeAs = 'stpa';
const uint32 kMsgSameTypeAsOpened = 'stpO';
const uint32 kMsgPreferredAppChosen = 'papc';
const uint32 kMsgSelectPreferredApp = 'slpa';
const uint32 kMsgSamePreferredAppAs = 'spaa';
const uint32 kMsgPreferredAppOpened = 'paOp';
const uint32 kMsgSamePreferredAppAsOpened = 'spaO';
FileTypeWindow::FileTypeWindow(BPoint position, const BMessage& refs)
:
BWindow(BRect(0.0f, 0.0f, 300.0f, 200.0f).OffsetBySelf(position),
B_TRANSLATE("File type"), B_TITLED_WINDOW,
B_NOT_V_RESIZABLE | B_NOT_ZOOMABLE
| B_ASYNCHRONOUS_CONTROLS | B_AUTO_UPDATE_SIZE_LIMITS)
{
float padding = be_control_look->DefaultItemSpacing();
BBox* fileTypeBox = new BBox("file type BBox");
fileTypeBox->SetLabel(B_TRANSLATE("File type"));
fTypeControl = new BTextControl("type", NULL, "Type Control",
new BMessage(kMsgTypeEntered));
BTextView* textView = fTypeControl->TextView();
const char* disallowedCharacters = "<>@,;:\"()[]?=";
for (int32 i = 0; disallowedCharacters[i]; i++) {
textView->DisallowChar(disallowedCharacters[i]);
}
fSelectTypeButton = new BButton("select type",
B_TRANSLATE("Select" B_UTF8_ELLIPSIS), new BMessage(kMsgSelectType));
fSameTypeAsButton = new BButton("same type as",
B_TRANSLATE_COMMENT("Same as" B_UTF8_ELLIPSIS,
"The same TYPE as ..."), new BMessage(kMsgSameTypeAs));
BLayoutBuilder::Grid<>(fileTypeBox, padding, padding / 2)
.SetInsets(padding, padding * 2, padding, padding)
.Add(fTypeControl, 0, 0, 3, 1)
.Add(fSelectTypeButton, 0, 1)
.Add(fSameTypeAsButton, 1, 1);
BBox* iconBox = new BBox("icon BBox");
iconBox->SetLabel(B_TRANSLATE("Icon"));
fIconView = new IconView("icon");
BLayoutBuilder::Group<>(iconBox, B_HORIZONTAL)
.SetInsets(padding, padding * 2, padding, padding)
.Add(fIconView);
BBox* preferredBox = new BBox("preferred BBox");
preferredBox->SetLabel(B_TRANSLATE("Preferred application"));
BMenu* menu = new BPopUpMenu("preferred");
BMenuItem* item;
menu->AddItem(item = new BMenuItem(B_TRANSLATE("Default application"),
new BMessage(kMsgPreferredAppChosen)));
item->SetMarked(true);
fPreferredField = new BMenuField("preferred", NULL, menu);
fSelectAppButton = new BButton("select app",
B_TRANSLATE("Select" B_UTF8_ELLIPSIS),
new BMessage(kMsgSelectPreferredApp));
fSameAppAsButton = new BButton("same app as",
B_TRANSLATE_COMMENT("Same as" B_UTF8_ELLIPSIS,
"The same APPLICATION as ..."),
new BMessage(kMsgSamePreferredAppAs));
BLayoutBuilder::Grid<>(preferredBox, padding, padding / 2)
.SetInsets(padding, padding * 2, padding, padding)
.Add(fPreferredField, 0, 0, 2, 1)
.Add(fSelectAppButton, 0, 1)
.Add(fSameAppAsButton, 1, 1);
BLayoutBuilder::Grid<>(this)
.SetInsets(padding)
.Add(fileTypeBox, 0, 0, 2, 1)
.Add(preferredBox, 0, 1, 1, 1)
.Add(iconBox, 1, 1, 1, 1);
fTypeControl->MakeFocus(true);
BMimeType::StartWatching(this);
_SetTo(refs);
}
FileTypeWindow::~FileTypeWindow()
{
BMimeType::StopWatching(this);
}
BString
FileTypeWindow::_Title(const BMessage& refs)
{
BString title;
entry_ref ref;
if (refs.FindRef("refs", 1, &ref) == B_OK) {
bool same = false;
BEntry entry, parent;
if (entry.SetTo(&ref) == B_OK
&& entry.GetParent(&parent) == B_OK) {
same = true;
for (int32 i = 0; refs.FindRef("refs", i, &ref) == B_OK; i++) {
BEntry directory;
if (entry.SetTo(&ref) == B_OK
&& entry.GetParent(&directory) == B_OK) {
if (directory != parent) {
same = false;
break;
}
}
}
}
char name[B_FILE_NAME_LENGTH];
if (same && parent.GetName(name) == B_OK) {
char buffer[512];
snprintf(buffer, sizeof(buffer),
B_TRANSLATE("Multiple files from \"%s\" file type"), name);
title = buffer;
} else
title = B_TRANSLATE("[Multiple files] file types");
} else if (refs.FindRef("refs", 0, &ref) == B_OK) {
char buffer[512];
snprintf(buffer, sizeof(buffer), B_TRANSLATE("%s file type"), ref.name);
title = buffer;
}
return title;
}
void
FileTypeWindow::_SetTo(const BMessage& refs)
{
SetTitle(_Title(refs).String());
fCommonPreferredApp = "";
fCommonType = "";
entry_ref ref;
for (int32 i = 0; refs.FindRef("refs", i, &ref) == B_OK; i++) {
BNode node(&ref);
if (node.InitCheck() != B_OK)
continue;
BNodeInfo info(&node);
if (info.InitCheck() != B_OK)
continue;
entry_ref* copiedRef = new entry_ref(ref);
fEntries.AddItem(copiedRef);
char type[B_MIME_TYPE_LENGTH];
if (info.GetType(type) != B_OK)
type[0] = '\0';
if (i > 0) {
if (fCommonType != type)
fCommonType = "";
} else
fCommonType = type;
char preferredApp[B_MIME_TYPE_LENGTH];
if (info.GetPreferredApp(preferredApp) != B_OK)
preferredApp[0] = '\0';
if (i > 0) {
if (fCommonPreferredApp != preferredApp)
fCommonPreferredApp = "";
} else
fCommonPreferredApp = preferredApp;
if (i == 0)
fIconView->SetTo(ref);
}
fTypeControl->SetText(fCommonType.String());
_UpdatePreferredApps();
fIconView->ShowIconHeap(fEntries.CountItems() != 1);
}
void
FileTypeWindow::_AdoptType(BMessage* message)
{
entry_ref ref;
if (message == NULL || message->FindRef("refs", &ref) != B_OK)
return;
BNode node(&ref);
status_t status = node.InitCheck();
char type[B_MIME_TYPE_LENGTH];
if (status == B_OK) {
BNodeInfo nodeInfo(&node);
status = nodeInfo.InitCheck();
if (status == B_OK) {
if (nodeInfo.GetType(type) != B_OK)
type[0] = '\0';
}
}
if (status != B_OK) {
error_alert(B_TRANSLATE("Could not open file"), status);
return;
}
fCommonType = type;
fTypeControl->SetText(type);
_AdoptType();
}
void
FileTypeWindow::_AdoptType()
{
for (int32 i = 0; i < fEntries.CountItems(); i++) {
BNode node(fEntries.ItemAt(i));
BNodeInfo info(&node);
if (node.InitCheck() != B_OK
|| info.InitCheck() != B_OK)
continue;
info.SetType(fCommonType.String());
}
}
void
FileTypeWindow::_AdoptPreferredApp(BMessage* message, bool sameAs)
{
if (retrieve_preferred_app(message, sameAs, fCommonType.String(),
fCommonPreferredApp) == B_OK) {
_AdoptPreferredApp();
_UpdatePreferredApps();
}
}
void
FileTypeWindow::_AdoptPreferredApp()
{
for (int32 i = 0; i < fEntries.CountItems(); i++) {
BNode node(fEntries.ItemAt(i));
if (fCommonPreferredApp.Length() == 0) {
node.RemoveAttr("BEOS:PREF_APP");
continue;
}
BNodeInfo info(&node);
if (node.InitCheck() != B_OK
|| info.InitCheck() != B_OK)
continue;
info.SetPreferredApp(fCommonPreferredApp.String());
}
}
void
FileTypeWindow::_UpdatePreferredApps()
{
BMimeType type(fCommonType.String());
update_preferred_app_menu(fPreferredField->Menu(), &type,
kMsgPreferredAppChosen, fCommonPreferredApp.String());
}
void
FileTypeWindow::MessageReceived(BMessage* message)
{
switch (message->what) {
case kMsgTypeEntered:
fCommonType = fTypeControl->Text();
_AdoptType();
break;
case kMsgSelectType:
{
BWindow* window = new TypeListWindow(fCommonType.String(),
kMsgTypeSelected, this);
window->Show();
break;
}
case kMsgTypeSelected:
{
const char* type;
if (message->FindString("type", &type) == B_OK) {
fCommonType = type;
fTypeControl->SetText(type);
_AdoptType();
}
break;
}
case kMsgSameTypeAs:
{
BMessage panel(kMsgOpenFilePanel);
panel.AddString("title", B_TRANSLATE("Select same type as"));
panel.AddInt32("message", kMsgSameTypeAsOpened);
panel.AddMessenger("target", this);
panel.AddBool("allowDirs", true);
be_app_messenger.SendMessage(&panel);
break;
}
case kMsgSameTypeAsOpened:
_AdoptType(message);
break;
case kMsgPreferredAppChosen:
{
const char* signature;
if (message->FindString("signature", &signature) == B_OK)
fCommonPreferredApp = signature;
else
fCommonPreferredApp = "";
_AdoptPreferredApp();
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:
_AdoptPreferredApp(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:
_AdoptPreferredApp(message, true);
break;
case B_SIMPLE_DATA:
{
entry_ref ref;
if (message->FindRef("refs", &ref) != B_OK)
break;
BFile file(&ref, B_READ_ONLY);
if (is_application(file))
_AdoptPreferredApp(message, false);
else
_AdoptType(message);
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 (which == B_MIME_TYPE_DELETED
|| which == B_SUPPORTED_TYPES_CHANGED) {
_UpdatePreferredApps();
}
break;
default:
BWindow::MessageReceived(message);
}
}
bool
FileTypeWindow::QuitRequested()
{
be_app->PostMessage(kMsgTypeWindowClosed);
return true;
}