#include "StyledTextImporter.h"
#include <new>
#include <stdint.h>
#include <stdio.h>
#include <Alert.h>
#include <Archivable.h>
#include <ByteOrder.h>
#include <Catalog.h>
#include <DataIO.h>
#include <File.h>
#include <List.h>
#include <Locale.h>
#include <Message.h>
#include <NodeInfo.h>
#include <Shape.h>
#include <String.h>
#include <TextView.h>
#include "Defines.h"
#include "Icon.h"
#include "PathSourceShape.h"
#include "Style.h"
#include "VectorPath.h"
#undef B_TRANSLATION_CONTEXT
#define B_TRANSLATION_CONTEXT "Icon-O-Matic-StyledTextImport"
#define CALLED() do {} while (0)
using std::nothrow;
class ShapeIterator : public BShapeIterator {
public:
ShapeIterator(Icon *icon, PathSourceShape *to, BPoint offset, const char *name);
~ShapeIterator() {};
virtual status_t IterateMoveTo(BPoint *point);
virtual status_t IterateLineTo(int32 lineCount, BPoint *linePts);
virtual status_t IterateBezierTo(int32 bezierCount, BPoint *bezierPts);
virtual status_t IterateClose();
private:
VectorPath *CurrentPath();
void NextPath();
Icon *fIcon;
PathSourceShape *fShape;
VectorPath *fPath;
BPoint fOffset;
const char *fName;
BPoint fLastPoint;
bool fHasLastPoint;
};
ShapeIterator::ShapeIterator(Icon *icon, PathSourceShape *to, BPoint offset,
const char *name)
{
CALLED();
fIcon = icon;
fShape = to;
fPath = NULL;
fOffset = offset;
fName = name;
fLastPoint = offset;
fHasLastPoint = false;
}
status_t
ShapeIterator::IterateMoveTo(BPoint *point)
{
CALLED();
if (fPath)
NextPath();
if (!CurrentPath())
return B_ERROR;
fLastPoint = fOffset + *point;
fHasLastPoint = true;
return B_OK;
}
status_t
ShapeIterator::IterateLineTo(int32 lineCount, BPoint *linePts)
{
CALLED();
if (!CurrentPath())
return B_ERROR;
while (lineCount--) {
fPath->AddPoint(fOffset + *linePts);
fLastPoint = fOffset + *linePts;
fHasLastPoint = true;
linePts++;
}
return B_OK;
}
status_t
ShapeIterator::IterateBezierTo(int32 bezierCount, BPoint *bezierPts)
{
CALLED();
if (!CurrentPath())
return B_ERROR;
BPoint firstPoint(fLastPoint);
fLastPoint = fOffset + bezierPts[bezierCount * 3 - 1];
if (firstPoint == fLastPoint) {
fPath->AddPoint(firstPoint,
fOffset + bezierPts[bezierCount * 3 - 2], fOffset + bezierPts[0], false);
fPath->SetClosed(true);
} else {
fPath->AddPoint(firstPoint, firstPoint, fOffset + bezierPts[0], false);
}
for (int i = 1; i + 2 < bezierCount * 3; i += 3) {
fPath->AddPoint(fOffset + bezierPts[i + 1],
fOffset + bezierPts[i + 0], fOffset + bezierPts[i + 2], false);
}
if (firstPoint != fLastPoint) {
fPath->AddPoint(fLastPoint,
fOffset + bezierPts[bezierCount * 3 - 2], fLastPoint, false);
}
fHasLastPoint = true;
return B_OK;
}
status_t
ShapeIterator::IterateClose()
{
CALLED();
if (!CurrentPath())
return B_ERROR;
fPath->SetClosed(true);
NextPath();
fHasLastPoint = false;
return B_OK;
}
VectorPath *
ShapeIterator::CurrentPath()
{
CALLED();
if (fPath)
return fPath;
fPath = new (nothrow) VectorPath();
fPath->SetName(fName);
return fPath;
}
void
ShapeIterator::NextPath()
{
CALLED();
if (fPath) {
fPath->CleanUp();
fIcon->Paths()->AddItem(fPath);
fShape->Paths()->AddItem(fPath);
}
fPath = NULL;
}
StyledTextImporter::StyledTextImporter()
: Importer(),
fStyleMap(NULL),
fStyleCount(0)
{
CALLED();
}
StyledTextImporter::~StyledTextImporter()
{
CALLED();
}
status_t
StyledTextImporter::Import(Icon* icon, BMessage* clipping)
{
CALLED();
const char *text;
ssize_t textLength;
if (clipping == NULL)
return ENOENT;
if (clipping->FindData("text/plain",
B_MIME_TYPE, (const void **)&text, &textLength) == B_OK) {
text_run_array *runs = NULL;
ssize_t runsLength;
if (clipping->FindData("application/x-vnd.Be-text_run_array",
B_MIME_TYPE, (const void **)&runs, &runsLength) < B_OK)
runs = NULL;
BString str(text, textLength);
return _Import(icon, str.String(), runs);
}
return ENOENT;
}
status_t
StyledTextImporter::Import(Icon* icon, const entry_ref* ref)
{
CALLED();
status_t err;
BFile file(ref, B_READ_ONLY);
err = file.InitCheck();
if (err < B_OK)
return err;
BNodeInfo info(&file);
char mime[B_MIME_TYPE_LENGTH];
err = info.GetType(mime);
if (err < B_OK)
return err;
if (strncmp(mime, "text/plain", B_MIME_TYPE_LENGTH))
return EINVAL;
off_t size;
err = file.GetSize(&size);
if (err < B_OK)
return err;
if (size > 1 * 1024 * 1024)
return E2BIG;
BMallocIO mio;
mio.SetSize((size_t)size + 1);
memset((void *)mio.Buffer(), 0, (size_t)size + 1);
return _Import(icon, (const char *)mio.Buffer(), NULL);
}
status_t
StyledTextImporter::_Import(Icon* icon, const char *text, text_run_array *runs)
{
CALLED();
status_t ret = Init(icon);
if (ret < B_OK) {
printf("StyledTextImporter::Import() - Init() error: %s\n",
strerror(ret));
return ret;
}
BString str(text);
if (str.Length() > 50) {
BAlert* alert = new BAlert(B_TRANSLATE("Text too long"),
B_TRANSLATE("The text you are trying to import is quite long, "
"are you sure?"),
B_TRANSLATE("Import text"), B_TRANSLATE("Cancel"), NULL,
B_WIDTH_AS_USUAL, B_WARNING_ALERT);
if (alert->Go())
return B_CANCELED;
}
if (runs) {
delete[] fStyleMap;
fStyleMap = new struct style_map[runs->count];
for (int32 i = 0; runs && i < runs->count; i++) {
_AddStyle(icon, &runs->runs[i]);
}
}
int32 currentRun = 0;
text_run *run = NULL;
if (runs)
run = &runs->runs[0];
int32 len = str.Length();
int32 chars = str.CountChars();
BPoint origin(0, 0);
BPoint offset(origin);
for (int32 i = 0, c = 0; i < len && c < chars; c++) {
while (run && currentRun < runs->count - 1 &&
i >= runs->runs[currentRun + 1].offset) {
run = &runs->runs[++currentRun];
}
int charLen;
for (charLen = 1; str.ByteAt(i + charLen) & 0x80; charLen++);
BShape glyph;
BShape *glyphs[1] = { &glyph };
BFont font(be_plain_font);
if (run)
font = run->font;
if (offset == BPoint(0,0)) {
font_height height;
font.GetHeight(&height);
origin.y += height.ascent;
offset = origin;
}
if (str[i] == '\n') {
font_height height;
font.GetHeight(&height);
origin.y += height.ascent + height.descent + height.leading;
offset = origin;
i++;
continue;
}
float charWidth;
charWidth = font.StringWidth(str.String() + i, charLen);
BString glyphName(str.String() + i, charLen);
glyphName.Prepend("Glyph (");
glyphName.Append(")");
font.GetGlyphShapes((str.String() + i), 1, glyphs);
if (glyph.Bounds().IsValid()) {
offset.x += charWidth;
PathSourceShape* shape = new (nothrow) PathSourceShape(NULL);
if (shape == NULL)
return B_NO_MEMORY;
shape->SetName(glyphName.String());
if (!icon->Shapes()->AddItem(shape)) {
delete shape;
return B_NO_MEMORY;
}
for (int j = 0; run && j < fStyleCount; j++) {
if (fStyleMap[j].run == run) {
shape->SetStyle(fStyleMap[j].style);
break;
}
}
ShapeIterator iterator(icon, shape, offset, glyphName.String());
if (iterator.Iterate(&glyph) < B_OK)
return B_ERROR;
}
for (i++; i < len && str[i] & 0x80; i++);
}
delete[] fStyleMap;
fStyleMap = NULL;
return B_OK;
}
status_t
StyledTextImporter::_AddStyle(Icon *icon, text_run *run)
{
CALLED();
if (!run)
return EINVAL;
rgb_color color = run->color;
Style* style = new(std::nothrow) Style(color);
if (style == NULL)
return B_NO_MEMORY;
char name[30];
sprintf(name, B_TRANSLATE_COMMENT("Color (#%02x%02x%02x)",
"Style name after dropping a color"),
color.red, color.green, color.blue);
style->SetName(name);
bool found = false;
for (int i = 0; i < fStyleCount; i++) {
if (*style == *(fStyleMap[i].style)) {
delete style;
style = fStyleMap[i].style;
found = true;
break;
}
}
if (!found && !icon->Styles()->AddItem(style)) {
delete style;
return B_NO_MEMORY;
}
fStyleMap[fStyleCount].run = run;
fStyleMap[fStyleCount].style = style;
fStyleCount++;
return B_OK;
}
status_t
StyledTextImporter::_AddPaths(Icon *icon, BShape *shape)
{
CALLED();
return B_ERROR;
}
status_t
StyledTextImporter::_AddShape(Icon *icon, BShape *shape, text_run *run)
{
CALLED();
return B_ERROR;
}