diff --git a/src/apps/icon-o-matic/Jamfile b/src/apps/icon-o-matic/Jamfile index a6ded8a408..4496a39cf6 100644 --- a/src/apps/icon-o-matic/Jamfile +++ b/src/apps/icon-o-matic/Jamfile @@ -50,6 +50,7 @@ local sourceDirs = import_export/flat_icon import_export/message import_export/svg + import_export/styled_text shape shape/commands style @@ -229,6 +230,9 @@ Application Icon-O-Matic : MessageExporter.cpp MessageImporter.cpp + # import_export/styled_text + StyledTextImporter.cpp + # import_export/svg DocumentBuilder.cpp PathTokenizer.cpp diff --git a/src/apps/icon-o-matic/MainWindow.cpp b/src/apps/icon-o-matic/MainWindow.cpp index 350721dc5f..b91b2aadfc 100644 --- a/src/apps/icon-o-matic/MainWindow.cpp +++ b/src/apps/icon-o-matic/MainWindow.cpp @@ -11,6 +11,7 @@ #include #include +#include #include #include #include @@ -63,6 +64,8 @@ #include "StyleContainer.h" #include "VectorPath.h" +#include "StyledTextImporter.h" + using std::nothrow; enum { @@ -120,6 +123,24 @@ MainWindow::MessageReceived(BMessage* message) return; } + if (message->WasDropped()) { + const rgb_color *color; + int32 len; + int32 i; + // create styles from dropped colors + for (i = 0; message->FindData("RGBColor", B_RGB_COLOR_TYPE, i, + (const void **)&color, &len) == B_OK; i++) { + if (len != sizeof(rgb_color)) + continue; + Style* style = new (nothrow) Style(*color); + Style* styles[1] = { style }; + AddStylesCommand* styleCommand = new (nothrow) AddStylesCommand( + fDocument->Icon()->Styles(), styles, 1, + fDocument->Icon()->Styles()->CountStyles()); + fDocument->CommandStack()->Perform(styleCommand); + } + } + switch (message->what) { case B_REFS_RECEIVED: @@ -130,6 +151,63 @@ MainWindow::MessageReceived(BMessage* message) be_app->PostMessage(message); break; + case B_MIME_DATA: + if (message->HasData("text/plain", B_MIME_TYPE)) { + status_t err; + Icon* icon; + icon = new (nothrow) Icon(*fDocument->Icon()); + if (icon) { + StyledTextImporter importer; + err = importer.Import(icon, message); + if (err >= B_OK) { + AutoWriteLocker locker(fDocument); + + SetIcon(NULL); + + // incorporate the loaded icon into the document + // (either replace it or append to it) + fDocument->MakeEmpty(false); + // if append, the document savers are preserved + fDocument->SetIcon(icon); + SetIcon(icon); + } + } + } + break; + + case B_PASTE: + if (be_clipboard->Lock()) { + status_t err; + BMessage *clip = be_clipboard->Data(); + + if (!clip || !clip->HasData("text/plain", B_MIME_TYPE)) { + be_clipboard->Unlock(); + break; + } + + Icon* icon; + icon = new (nothrow) Icon(*fDocument->Icon()); + if (icon) { + StyledTextImporter importer; + err = importer.Import(icon, clip); + if (err >= B_OK) { + AutoWriteLocker locker(fDocument); + + SetIcon(NULL); + + // incorporate the loaded icon into the document + // (either replace it or append to it) + fDocument->MakeEmpty(false); + // if append, the document savers are preserved + fDocument->SetIcon(icon); + SetIcon(icon); + } + } + + be_clipboard->Unlock(); + } + break; + case MSG_UNDO: fDocument->CommandStack()->Undo(); break; diff --git a/src/apps/icon-o-matic/import_export/styled_text/StyledTextImporter.cpp b/src/apps/icon-o-matic/import_export/styled_text/StyledTextImporter.cpp new file mode 100644 index 0000000000..20f93afe6a --- /dev/null +++ b/src/apps/icon-o-matic/import_export/styled_text/StyledTextImporter.cpp @@ -0,0 +1,338 @@ +/* + * Copyright 2008, Haiku. All rights reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * François Revol + */ +#include "StyledTextImporter.h" + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "Defines.h" +#include "Icon.h" +#include "PathContainer.h" +#include "Shape.h" +#include "Style.h" +#include "StyleContainer.h" +#include "VectorPath.h" + +//#define CALLED() printf("%s()\n", __FUNCTION__); +#define CALLED() do {} while (0) + +using std::nothrow; + +class ShapeIterator : public BShapeIterator { + public: + ShapeIterator(Icon *icon, Shape *to, BPoint offset); + ~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; + Shape *fShape; + VectorPath *fPath; + BPoint fOffset; +}; + +ShapeIterator::ShapeIterator(Icon *icon, Shape *to, BPoint offset) +{ + CALLED(); + fIcon = icon; + fShape = to; + fPath = NULL; + fOffset = offset; +} + +status_t +ShapeIterator::IterateMoveTo(BPoint *point) +{ + CALLED(); + if (fPath) + NextPath(); + if (!CurrentPath()) + return B_ERROR; + fPath->AddPoint(fOffset + *point); + return B_OK; +} + +status_t +ShapeIterator::IterateLineTo(int32 lineCount, BPoint *linePts) +{ + CALLED(); + if (!CurrentPath()) + return B_ERROR; + while (lineCount--) + fPath->AddPoint(fOffset + *linePts++); + return B_OK; +} + +status_t +ShapeIterator::IterateBezierTo(int32 bezierCount, BPoint *bezierPts) +{ + CALLED(); + if (!CurrentPath()) + return B_ERROR; + while (bezierCount--) { + fPath->AddPoint(fOffset + *bezierPts++, + fOffset + *bezierPts++, fOffset + *bezierPts++, false); + } + return B_OK; +} + +status_t +ShapeIterator::IterateClose() +{ + CALLED(); + if (!CurrentPath()) + return B_ERROR; + fPath->SetClosed(true); + NextPath(); + return B_OK; +} + +VectorPath * +ShapeIterator::CurrentPath() +{ + CALLED(); + if (fPath) + return fPath; + fPath = new (nothrow) VectorPath(); + return fPath; +} + +void +ShapeIterator::NextPath() +{ + CALLED(); + if (fPath) { + fIcon->Paths()->AddPath(fPath); + fShape->Paths()->AddPath(fPath); + } + fPath = NULL; +} + + +// #pragma mark - + +// constructor +StyledTextImporter::StyledTextImporter() + : Importer(), + fStyleMap(NULL), + fStyleCount(0) +{ + CALLED(); +} + +// destructor +StyledTextImporter::~StyledTextImporter() +{ + CALLED(); +} + +// Import +status_t +StyledTextImporter::Import(Icon* icon, BMessage* clipping) +{ + CALLED(); + const char *text; + int32 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; + int32 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; +} + +// Import +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 > UINT32_MAX - 1) + return E2BIG; + + BMallocIO mio; + mio.SetSize((size_t)size + 1); + memset((void *)mio.Buffer(), 0, (size_t)size + 1); + + // TODO: read runs + + return _Import(icon, (const char *)mio.Buffer(), NULL); +} + +// _Import +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; + } + + StyleContainer* styles = icon->Styles(); + // import run colors as styles + if (runs) { + fStyleMap = new struct style_map[runs->count]; + for (int32 i = 0; runs && i < runs->count; i++) { + _AddStyle(icon, &runs->runs[i]); + } + } + +#if 1 + int32 currentRun = 0; + text_run *run = NULL; + if (runs) + run = &runs->runs[0]; + BString str(text); + int32 len = str.Length(); + int32 chars = str.CountChars(); + BPoint offset(0,0); + + for (int32 i = 0, c = 0; i < len && c < chars; c++) { + // make sure we are still on the (good) run + while (run && currentRun < runs->count - 1 && + i >= runs->runs[currentRun + 1].offset) { + printf("switching to run %d\n", currentRun); + run = &runs->runs[++currentRun]; + } + + BShape glyph; + BShape *glyphs[1] = { &glyph }; + BFont font(be_plain_font); + if (run) + font = run->font; + + font.GetGlyphShapes((str.String() + i), 1, glyphs); + if (glyph.Bounds().IsValid()) { + offset.x += glyph.Bounds().Width(); + Shape* shape = new (nothrow) Shape(NULL); + if (!shape || !icon->Shapes()->AddShape(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); + if (iterator.Iterate(&glyph) < B_OK) + return B_ERROR; + + } + + // skip the rest of UTF-8 char bytes + for (i++; i < len && str[i] & 0x80; i++); + } +#endif + delete[] fStyleMap; + + return B_OK; +} + +// #pragma mark - + +// _AddStyle +status_t +StyledTextImporter::_AddStyle(Icon *icon, text_run *run) +{ + CALLED(); + if (!run) + return EINVAL; + rgb_color color = run->color; + Style* style = new (nothrow) Style(color); + if (!style) { + delete style; + return B_NO_MEMORY; + } + + 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()->AddStyle(style)) { + delete style; + return B_NO_MEMORY; + } + + fStyleMap[fStyleCount].run = run; + fStyleMap[fStyleCount].style = style; + fStyleCount++; + + return B_OK; +} + +// _AddPaths +status_t +StyledTextImporter::_AddPaths(Icon *icon, BShape *shape) +{ + CALLED(); + return B_ERROR; +} + +// _AddShape +status_t +StyledTextImporter::_AddShape(Icon *icon, BShape *shape, text_run *run) +{ + CALLED(); + return B_ERROR; +} diff --git a/src/apps/icon-o-matic/import_export/styled_text/StyledTextImporter.h b/src/apps/icon-o-matic/import_export/styled_text/StyledTextImporter.h new file mode 100644 index 0000000000..26df2809f2 --- /dev/null +++ b/src/apps/icon-o-matic/import_export/styled_text/StyledTextImporter.h @@ -0,0 +1,57 @@ +/* + * Copyright 2008, Haiku. All rights reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * François Revol + */ +#ifndef STYLED_TEXT_IMPORTER_H +#define STYLED_TEXT_IMPORTER_H + + +#include "Importer.h" + +class BMessage; +class BShape; +struct text_run; +struct text_run_array; + +namespace BPrivate { +namespace Icon { + class Icon; + class Style; + class VectorPath; + class PathContainer; + class ShapeContainer; + class StyleContainer; +} +} + +struct style_map { + text_run *run; + Style *style; +}; + +class StyledTextImporter : public Importer { + public: + StyledTextImporter(); + virtual ~StyledTextImporter(); + + status_t Import(Icon* icon, + BMessage* clipping); + status_t Import(Icon* icon, + const entry_ref* ref); + + private: + status_t _Import(Icon* icon, const char *text, + text_run_array *runs); + + status_t _AddStyle(Icon *icon, text_run *run); + status_t _AddPaths(Icon *icon, BShape *shape); + status_t _AddShape(Icon *icon, BShape *shape, text_run *run); + + struct style_map *fStyleMap; + int32 fStyleCount; +}; + +#endif // STYLED_TEXT_IMPORTER_H