#include <Application.h>
#include <Bitmap.h>
#include <BitmapStream.h>
#include <CharacterSet.h>
#include <CharacterSetRoster.h>
#include <Entry.h>
#include <File.h>
#include <MenuItem.h>
#include <NodeInfo.h>
#include <ObjectList.h>
#include <Path.h>
#include <Resources.h>
#include <Roster.h>
#include <String.h>
#include <TextView.h>
#include <TranslationUtils.h>
#include <TranslatorFormats.h>
#include <TranslatorRoster.h>
#include <UTF8.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
using namespace BPrivate;
BTranslationUtils::BTranslationUtils()
{
}
BTranslationUtils::~BTranslationUtils()
{
}
BTranslationUtils::BTranslationUtils(const BTranslationUtils &kUtils)
{
}
BTranslationUtils &
BTranslationUtils::operator=(const BTranslationUtils &kUtils)
{
return *this;
}
BBitmap *
BTranslationUtils::GetBitmap(const char *kName, BTranslatorRoster *roster)
{
BBitmap *pBitmap = GetBitmapFile(kName, roster);
if (pBitmap == NULL)
pBitmap = GetBitmap(B_TRANSLATOR_BITMAP, kName, roster);
return pBitmap;
}
BBitmap *
BTranslationUtils::GetBitmap(uint32 type, int32 id, BTranslatorRoster *roster)
{
BResources *pResources = BApplication::AppResources();
if (pResources == NULL || pResources->HasResource(type, id) == false)
return NULL;
size_t bitmapSize = 0;
const void *kpRawData = pResources->LoadResource(type, id, &bitmapSize);
if (kpRawData == NULL || bitmapSize == 0)
return NULL;
BMemoryIO memio(kpRawData, bitmapSize);
return GetBitmap(&memio, roster);
}
BBitmap *
BTranslationUtils::GetBitmap(uint32 type, const char *kName,
BTranslatorRoster *roster)
{
BResources *pResources = BApplication::AppResources();
if (pResources == NULL || pResources->HasResource(type, kName) == false)
return NULL;
size_t bitmapSize = 0;
const void *kpRawData = pResources->LoadResource(type, kName, &bitmapSize);
if (kpRawData == NULL || bitmapSize == 0)
return NULL;
BMemoryIO memio(kpRawData, bitmapSize);
return GetBitmap(&memio, roster);
}
BBitmap *
BTranslationUtils::GetBitmapFile(const char *kName, BTranslatorRoster *roster)
{
if (!be_app || !kName || kName[0] == '\0')
return NULL;
BPath path;
if (kName[0] != '/') {
app_info info;
if (be_app->GetAppInfo(&info) != B_OK)
return NULL;
BEntry appRef(&info.ref);
if (path.SetTo(&appRef) != B_OK)
return NULL;
if (path.GetParent(&path) != B_OK)
return NULL;
if (path.Append(kName) != B_OK)
return NULL;
} else if (path.SetTo(kName) != B_OK)
return NULL;
BFile bitmapFile(path.Path(), B_READ_ONLY);
if (bitmapFile.InitCheck() != B_OK)
return NULL;
return GetBitmap(&bitmapFile, roster);
}
BBitmap *
BTranslationUtils::GetBitmap(const entry_ref *kRef, BTranslatorRoster *roster)
{
BFile bitmapFile(kRef, B_READ_ONLY);
if (bitmapFile.InitCheck() != B_OK)
return NULL;
return GetBitmap(&bitmapFile, roster);
}
BBitmap *
BTranslationUtils::GetBitmap(BPositionIO *stream, BTranslatorRoster *roster)
{
if (stream == NULL)
return NULL;
if (roster == NULL) {
roster = BTranslatorRoster::Default();
if (roster == NULL)
return NULL;
}
BBitmapStream bitmapStream;
if (roster->Translate(stream, NULL, NULL, &bitmapStream,
B_TRANSLATOR_BITMAP) < B_OK)
return NULL;
BBitmap *pBitmap = NULL;
if (bitmapStream.DetachBitmap(&pBitmap) == B_NO_ERROR)
return pBitmap;
else
return NULL;
}
status_t
BTranslationUtils::GetStyledText(BPositionIO* source, BTextView* intoView,
const char* encoding, BTranslatorRoster* roster)
{
if (source == NULL || intoView == NULL)
return B_BAD_VALUE;
if (roster == NULL) {
roster = BTranslatorRoster::Default();
if (roster == NULL)
return B_ERROR;
}
BMessage config;
if (encoding != NULL && encoding[0])
config.AddString("be:encoding", encoding);
BMallocIO mallocIO;
if (roster->Translate(source, NULL, &config, &mallocIO,
B_STYLED_TEXT_FORMAT) < B_OK)
return B_BAD_TYPE;
const uint8* buffer = (const uint8*)mallocIO.Buffer();
const size_t kStreamHeaderSize = sizeof(TranslatorStyledTextStreamHeader);
if (mallocIO.BufferLength() < kStreamHeaderSize)
return B_BAD_DATA;
TranslatorStyledTextStreamHeader header =
*(reinterpret_cast<const TranslatorStyledTextStreamHeader *>(buffer));
const size_t kRecordHeaderSize = sizeof(TranslatorStyledTextRecordHeader);
swap_data(B_UINT32_TYPE, &header.header, kRecordHeaderSize, B_SWAP_BENDIAN_TO_HOST);
swap_data(B_INT32_TYPE, &header.version, sizeof(int32), B_SWAP_BENDIAN_TO_HOST);
if (header.header.magic != 'STXT')
return B_BAD_TYPE;
uint32 offset = header.header.header_size + header.header.data_size;
const size_t kTextHeaderSize = sizeof(TranslatorStyledTextTextHeader);
if (mallocIO.BufferLength() < offset + kTextHeaderSize)
return B_BAD_DATA;
TranslatorStyledTextTextHeader textHeader =
*(const TranslatorStyledTextTextHeader *)(buffer + offset);
swap_data(B_UINT32_TYPE, &textHeader.header, kRecordHeaderSize, B_SWAP_BENDIAN_TO_HOST);
swap_data(B_INT32_TYPE, &textHeader.charset, sizeof(int32), B_SWAP_BENDIAN_TO_HOST);
if (textHeader.header.magic != 'TEXT' || textHeader.charset != B_UNICODE_UTF8)
return B_BAD_TYPE;
offset += textHeader.header.header_size;
if (mallocIO.BufferLength() < offset + textHeader.header.data_size) {
textHeader.header.data_size = mallocIO.BufferLength() - offset;
}
const char* text = (const char*)buffer + offset;
bool hasStyles = false;
if (mallocIO.BufferLength() > offset + textHeader.header.data_size) {
offset += textHeader.header.data_size;
const size_t kStyleHeaderSize =
sizeof(TranslatorStyledTextStyleHeader);
if (mallocIO.BufferLength() >= offset + kStyleHeaderSize) {
TranslatorStyledTextStyleHeader styleHeader =
*(reinterpret_cast<const TranslatorStyledTextStyleHeader *>(buffer + offset));
swap_data(B_UINT32_TYPE, &styleHeader.header, kRecordHeaderSize, B_SWAP_BENDIAN_TO_HOST);
swap_data(B_UINT32_TYPE, &styleHeader.apply_offset, sizeof(uint32), B_SWAP_BENDIAN_TO_HOST);
swap_data(B_UINT32_TYPE, &styleHeader.apply_length, sizeof(uint32), B_SWAP_BENDIAN_TO_HOST);
if (styleHeader.header.magic == 'STYL') {
offset += styleHeader.header.header_size;
if (mallocIO.BufferLength() >= offset + styleHeader.header.data_size)
hasStyles = true;
}
}
}
text_run_array *runArray = NULL;
if (hasStyles)
runArray = BTextView::UnflattenRunArray(buffer + offset);
if (runArray != NULL) {
intoView->Insert(intoView->TextLength(),
text, textHeader.header.data_size, runArray);
#ifdef HAIKU_TARGET_PLATFORM_HAIKU
BTextView::FreeRunArray(runArray);
#else
free(runArray);
#endif
} else {
intoView->Insert(intoView->TextLength(), text,
textHeader.header.data_size);
}
return B_OK;
}
status_t
BTranslationUtils::GetStyledText(BPositionIO* source, BTextView* intoView,
BTranslatorRoster* roster)
{
return GetStyledText(source, intoView, NULL, roster);
}
status_t
BTranslationUtils::PutStyledText(BTextView *fromView, BPositionIO *intoStream,
BTranslatorRoster *roster)
{
if (fromView == NULL || intoStream == NULL)
return B_BAD_VALUE;
int32 textLength = fromView->TextLength();
if (textLength < 0)
return B_ERROR;
const char *pTextData = fromView->Text();
int32 runArrayLength = 0;
text_run_array *runArray = fromView->RunArray(0, textLength,
&runArrayLength);
if (runArray == NULL)
return B_ERROR;
int32 flatRunArrayLength = 0;
void *pflatRunArray =
BTextView::FlattenRunArray(runArray, &flatRunArrayLength);
if (pflatRunArray == NULL) {
#ifdef HAIKU_TARGET_PLATFORM_HAIKU
BTextView::FreeRunArray(runArray);
#else
free(runArray);
#endif
return B_ERROR;
}
bool ok = false;
while (!ok) {
const size_t kStreamHeaderSize =
sizeof(TranslatorStyledTextStreamHeader);
TranslatorStyledTextStreamHeader stm_header;
stm_header.header.magic = 'STXT';
stm_header.header.header_size = kStreamHeaderSize;
stm_header.header.data_size = 0;
stm_header.version = 100;
const size_t kRecordHeaderSize =
sizeof(TranslatorStyledTextRecordHeader);
if (swap_data(B_UINT32_TYPE, &stm_header.header, kRecordHeaderSize,
B_SWAP_HOST_TO_BENDIAN) != B_OK)
break;
if (swap_data(B_INT32_TYPE, &stm_header.version, sizeof(int32),
B_SWAP_HOST_TO_BENDIAN) != B_OK)
break;
const size_t kTextHeaderSize = sizeof(TranslatorStyledTextTextHeader);
TranslatorStyledTextTextHeader txt_header;
txt_header.header.magic = 'TEXT';
txt_header.header.header_size = kTextHeaderSize;
txt_header.header.data_size = textLength;
txt_header.charset = B_UNICODE_UTF8;
if (swap_data(B_UINT32_TYPE, &txt_header.header, kRecordHeaderSize,
B_SWAP_HOST_TO_BENDIAN) != B_OK)
break;
if (swap_data(B_INT32_TYPE, &txt_header.charset, sizeof(int32),
B_SWAP_HOST_TO_BENDIAN) != B_OK)
break;
const size_t kStyleHeaderSize =
sizeof(TranslatorStyledTextStyleHeader);
TranslatorStyledTextStyleHeader stl_header;
stl_header.header.magic = 'STYL';
stl_header.header.header_size = kStyleHeaderSize;
stl_header.header.data_size = flatRunArrayLength;
stl_header.apply_offset = 0;
stl_header.apply_length = textLength;
if (swap_data(B_UINT32_TYPE, &stl_header.header, kRecordHeaderSize,
B_SWAP_HOST_TO_BENDIAN) != B_OK)
break;
if (swap_data(B_UINT32_TYPE, &stl_header.apply_offset, sizeof(uint32),
B_SWAP_HOST_TO_BENDIAN) != B_OK)
break;
if (swap_data(B_UINT32_TYPE, &stl_header.apply_length, sizeof(uint32),
B_SWAP_HOST_TO_BENDIAN) != B_OK)
break;
ssize_t amountWritten = 0;
amountWritten = intoStream->Write(&stm_header, kStreamHeaderSize);
if ((size_t) amountWritten != kStreamHeaderSize)
break;
amountWritten = intoStream->Write(&txt_header, kTextHeaderSize);
if ((size_t) amountWritten != kTextHeaderSize)
break;
amountWritten = intoStream->Write(pTextData, textLength);
if (amountWritten != textLength)
break;
amountWritten = intoStream->Write(&stl_header, kStyleHeaderSize);
if ((size_t) amountWritten != kStyleHeaderSize)
break;
amountWritten = intoStream->Write(pflatRunArray, flatRunArrayLength);
if (amountWritten != flatRunArrayLength)
break;
ok = true;
}
free(pflatRunArray);
#ifdef HAIKU_TARGET_PLATFORM_HAIKU
BTextView::FreeRunArray(runArray);
#else
free(runArray);
#endif
return ok ? B_OK : B_ERROR;
}
status_t
BTranslationUtils::WriteStyledEditFile(BTextView* view, BFile* file, const char *encoding)
{
if (view == NULL || file == NULL)
return B_BAD_VALUE;
int32 textLength = view->TextLength();
if (textLength < 0)
return B_ERROR;
const char *text = view->Text();
if (text == NULL && textLength != 0)
return B_ERROR;
status_t status = file->Seek(0, SEEK_SET);
if (status != B_OK)
return status;
const BCharacterSet* characterSet = NULL;
if (encoding != NULL && strcmp(encoding, ""))
characterSet = BCharacterSetRoster::FindCharacterSetByName(encoding);
if (characterSet == NULL) {
ssize_t bytesWritten = file->Write(text, textLength);
if (bytesWritten != textLength) {
if (bytesWritten < B_OK)
return bytesWritten;
return B_ERROR;
}
int32 value = 65535;
file->WriteAttr("be:encoding", B_INT32_TYPE, 0, &value, sizeof(value));
} else {
uint32 id = characterSet->GetConversionID();
const char* outText = view->Text();
int32 sourceLength = textLength;
int32 state = 0;
textLength = 0;
do {
char buffer[32768];
int32 length = sourceLength;
int32 bufferSize = sizeof(buffer);
status = convert_from_utf8(id, outText, &length, buffer, &bufferSize, &state);
if (status != B_OK)
return status;
ssize_t bytesWritten = file->Write(buffer, bufferSize);
if (bytesWritten < B_OK)
return bytesWritten;
sourceLength -= length;
textLength += bytesWritten;
outText += length;
} while (sourceLength > 0);
BString encodingStr(encoding);
file->WriteAttrString("be:encoding", &encodingStr);
}
status = file->SetSize(textLength);
if (status != B_OK)
return status;
BNodeInfo info(file);
char type[B_MIME_TYPE_LENGTH];
if (info.GetType(type) != B_OK) {
if (info.SetType("text/plain") < B_OK)
return B_OK;
}
int32 wordWrap = view->DoesWordWrap() ? 1 : 0;
ssize_t bytesWritten = file->WriteAttr("wrap", B_INT32_TYPE, 0,
&wordWrap, sizeof(int32));
if (bytesWritten != sizeof(int32))
return B_OK;
int32 alignment = view->Alignment();
bytesWritten = file->WriteAttr("alignment", B_INT32_TYPE, 0,
&alignment, sizeof(int32));
if (bytesWritten != sizeof(int32))
return B_OK;
text_run_array *runArray = view->RunArray(0, view->TextLength());
if (runArray != NULL) {
int32 runArraySize = 0;
void *flattenedRunArray = BTextView::FlattenRunArray(runArray, &runArraySize);
if (flattenedRunArray != NULL) {
file->WriteAttr("styles", B_RAW_TYPE, 0, flattenedRunArray,
runArraySize);
}
free(flattenedRunArray);
#ifdef HAIKU_TARGET_PLATFORM_HAIKU
BTextView::FreeRunArray(runArray);
#else
free(runArray);
#endif
}
return B_OK;
}
status_t
BTranslationUtils::WriteStyledEditFile(BTextView* view, BFile* file)
{
return WriteStyledEditFile(view, file, NULL);
}
BMessage *
BTranslationUtils::GetDefaultSettings(translator_id forTranslator,
BTranslatorRoster *roster)
{
if (roster == NULL) {
roster = BTranslatorRoster::Default();
if (roster == NULL)
return NULL;
}
BMessage *message = new BMessage();
if (message == NULL)
return NULL;
status_t result = roster->GetConfigurationMessage(forTranslator, message);
if (result != B_OK && result != B_NO_TRANSLATOR) {
delete message;
return NULL;
}
return message;
}
BMessage *
BTranslationUtils::GetDefaultSettings(const char *kTranslatorName,
int32 translatorVersion)
{
BTranslatorRoster *roster = BTranslatorRoster::Default();
translator_id *translators = NULL;
int32 numTranslators = 0;
if (roster == NULL
|| roster->GetAllTranslators(&translators, &numTranslators) != B_OK)
return NULL;
BMessage *pMessage = NULL;
const char *currentTranName = NULL, *currentTranInfo = NULL;
int32 currentTranVersion = 0;
for (int i = 0; i < numTranslators; i++) {
if (roster->GetTranslatorInfo(translators[i], ¤tTranName,
¤tTranInfo, ¤tTranVersion) == B_OK) {
if (currentTranVersion == translatorVersion
&& strcmp(currentTranName, kTranslatorName) == 0) {
pMessage = GetDefaultSettings(translators[i], roster);
break;
}
}
}
delete[] translators;
return pMessage;
}
status_t
BTranslationUtils::AddTranslationItems(BMenu *intoMenu, uint32 fromType,
const BMessage *kModel, const char *kTranslatorIdName,
const char *kTranslatorTypeName, BTranslatorRoster *roster)
{
if (!intoMenu)
return B_BAD_VALUE;
if (!roster)
roster = BTranslatorRoster::Default();
if (!kTranslatorIdName)
kTranslatorIdName = "be:translator";
if (!kTranslatorTypeName)
kTranslatorTypeName = "be:type";
translator_id * ids = NULL;
int32 count = 0;
status_t err = roster->GetAllTranslators(&ids, &count);
if (err < B_OK)
return err;
BObjectList<translator_info> infoList;
for (int tix = 0; tix < count; tix++) {
const translation_format *formats = NULL;
int32 numFormats = 0;
bool ok = false;
err = roster->GetInputFormats(ids[tix], &formats, &numFormats);
if (err == B_OK) {
for (int iix = 0; iix < numFormats; iix++) {
if (formats[iix].type == fromType) {
ok = true;
break;
}
}
}
if (!ok)
continue;
err = roster->GetOutputFormats(ids[tix], &formats, &numFormats);
if (err == B_OK) {
for (int oix = 0; oix < numFormats; oix++) {
if (formats[oix].type != fromType) {
infoList.AddItem(_BuildTranslatorInfo(ids[tix],
const_cast<translation_format*>(&formats[oix])));
}
}
}
}
infoList.SortItems(&_CompareTranslatorInfoByName);
for (int i = 0; i < infoList.CountItems(); i++) {
translator_info* info = infoList.ItemAt(i);
BMessage *itemmsg;
if (kModel)
itemmsg = new BMessage(*kModel);
else
itemmsg = new BMessage(B_TRANSLATION_MENU);
itemmsg->AddInt32(kTranslatorIdName, info->translator);
itemmsg->AddInt32(kTranslatorTypeName, info->type);
intoMenu->AddItem(new BMenuItem(info->name, itemmsg));
delete info;
}
delete[] ids;
return B_OK;
}
translator_info*
BTranslationUtils::_BuildTranslatorInfo(const translator_id id, const translation_format* format)
{
translator_info* info = new translator_info;
info->translator = id;
info->type = format->type;
info->group = format->group;
info->quality = format->quality;
info->capability = format->capability;
strlcpy(info->name, format->name, sizeof(info->name));
strlcpy(info->MIME, format->MIME, sizeof(info->MIME));
return info;
}
int
BTranslationUtils::_CompareTranslatorInfoByName(const translator_info* info1, const translator_info* info2)
{
return strcasecmp(info1->name, info2->name);
}