* Copyright 2006, 2023, Haiku. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Stephan Aßmus <superstippi@gmx.de>
* Zardshard
*/
#include "Shape.h"
#include <Message.h>
#include <TypeConstants.h>
#include <new>
#include <limits.h>
#include <stdio.h>
#include "agg_bounding_rect.h"
#ifdef ICON_O_MATIC
# include "CommonPropertyIDs.h"
# include "Property.h"
# include "PropertyObject.h"
#endif
#include "Container.h"
#include "PathTransformer.h"
#include "Style.h"
#include "TransformerFactory.h"
#include "VectorPath.h"
using std::nothrow;
#ifdef ICON_O_MATIC
ShapeListener::ShapeListener()
{
}
ShapeListener::~ShapeListener()
{
}
#endif
Shape::Shape(::Style* style)
#ifdef ICON_O_MATIC
: IconObject("<shape>"),
Transformable(),
Observer(),
ContainerListener<VectorPath>(),
#else
: Transformable(),
#endif
fPaths(new (nothrow) Container<VectorPath>(false)),
fStyle(NULL),
fPathSource(fPaths),
fTransformers(true),
fNeedsUpdate(true),
fLastBounds(0, 0, -1, -1),
fHinting(false)
#ifdef ICON_O_MATIC
, fListeners(8)
#endif
{
SetStyle(style);
fTransformers.AddListener(this);
#ifdef ICON_O_MATIC
if (fPaths)
fPaths->AddListener(this);
#endif
}
Shape::Shape(const Shape& other)
#ifdef ICON_O_MATIC
: IconObject(other),
Transformable(other),
Observer(),
ContainerListener<VectorPath>(),
#else
: Transformable(other),
#endif
fPaths(new (nothrow) Container<VectorPath>(false)),
fStyle(NULL),
fPathSource(fPaths),
fTransformers(true),
fNeedsUpdate(true),
fLastBounds(0, 0, -1, -1),
fHinting(false)
#ifdef ICON_O_MATIC
, fListeners(8)
#endif
{
SetStyle(other.fStyle);
fTransformers.AddListener(this);
if (fPaths) {
#ifdef ICON_O_MATIC
fPaths->AddListener(this);
#endif
if (other.fPaths) {
int32 count = other.fPaths->CountItems();
for (int32 i = 0; i < count; i++) {
if (!fPaths->AddItem(other.fPaths->ItemAtFast(i)))
break;
}
}
}
int32 count = other.Transformers()->CountItems();
for (int32 i = 0; i < count; i++) {
Transformer* original = other.Transformers()->ItemAtFast(i);
Transformer* cloned = original->Clone();
if (!fTransformers.AddItem(cloned)) {
delete cloned;
break;
}
}
}
Shape::~Shape()
{
fPaths->MakeEmpty();
#ifdef ICON_O_MATIC
fPaths->RemoveListener(this);
#endif
delete fPaths;
fTransformers.MakeEmpty();
fTransformers.RemoveListener(this);
SetStyle(NULL);
}
status_t
Shape::Unarchive(BMessage* archive)
{
#ifdef ICON_O_MATIC
status_t ret = IconObject::Unarchive(archive);
if (ret < B_OK)
return ret;
#else
status_t ret;
#endif
if (archive->FindBool("hinting", &fHinting) < B_OK)
fHinting = false;
BMessage transformerArchive;
for (int32 i = 0;
archive->FindMessage("transformer", i,
&transformerArchive) == B_OK;
i++) {
Transformer* transformer
= TransformerFactory::TransformerFor(
&transformerArchive, VertexSource(), this);
if (!transformer || !fTransformers.AddItem(transformer)) {
delete transformer;
}
}
int32 size = Transformable::matrix_size;
const void* matrix;
ssize_t dataSize = size * sizeof(double);
ret = archive->FindData("transformation", B_DOUBLE_TYPE,
&matrix, &dataSize);
if (ret == B_OK && dataSize == (ssize_t)(size * sizeof(double)))
LoadFrom((const double*)matrix);
return B_OK;
}
#ifdef ICON_O_MATIC
status_t
Shape::Archive(BMessage* into, bool deep) const
{
status_t ret = IconObject::Archive(into, deep);
if (ret == B_OK)
ret = into->AddBool("hinting", fHinting);
if (ret == B_OK) {
int32 count = fTransformers.CountItems();
for (int32 i = 0; i < count; i++) {
Transformer* transformer = fTransformers.ItemAtFast(i);
BMessage transformerArchive;
ret = transformer->Archive(&transformerArchive);
if (ret == B_OK)
ret = into->AddMessage("transformer", &transformerArchive);
if (ret < B_OK)
break;
}
}
if (ret == B_OK) {
int32 size = Transformable::matrix_size;
double matrix[size];
StoreTo(matrix);
ret = into->AddData("transformation", B_DOUBLE_TYPE,
matrix, size * sizeof(double));
}
return ret;
}
PropertyObject*
Shape::MakePropertyObject() const
{
PropertyObject* object = IconObject::MakePropertyObject();
return object;
}
bool
Shape::SetToPropertyObject(const PropertyObject* object)
{
IconObject::SetToPropertyObject(object);
return true;
}
void
Shape::TransformationChanged()
{
_NotifyRerender();
}
void
Shape::ObjectChanged(const Observable* object)
{
_NotifyRerender();
}
void
Shape::ItemAdded(VectorPath* path, int32 index)
{
path->AcquireReference();
path->AddListener(this);
_NotifyRerender();
}
void
Shape::ItemRemoved(VectorPath* path)
{
path->RemoveListener(this);
_NotifyRerender();
path->ReleaseReference();
}
void
Shape::PointAdded(int32 index)
{
_NotifyRerender();
}
void
Shape::PointRemoved(int32 index)
{
_NotifyRerender();
}
void
Shape::PointChanged(int32 index)
{
_NotifyRerender();
}
void
Shape::PathChanged()
{
_NotifyRerender();
}
void
Shape::PathClosedChanged()
{
_NotifyRerender();
}
void
Shape::PathReversed()
{
_NotifyRerender();
}
#endif
void
Shape::ItemAdded(Transformer* transformer, int32 index)
{
#ifdef ICON_O_MATIC
transformer->AddObserver(this);
_NotifyRerender();
#else
fNeedsUpdate = true;
#endif
}
void
Shape::ItemRemoved(Transformer* transformer)
{
#ifdef ICON_O_MATIC
transformer->RemoveObserver(this);
_NotifyRerender();
#else
fNeedsUpdate = true;
#endif
}
status_t
Shape::InitCheck() const
{
return fPaths ? B_OK : B_NO_MEMORY;
}
void
Shape::SetStyle(::Style* style)
{
if (fStyle == style)
return;
#ifdef ICON_O_MATIC
if (fStyle) {
fStyle->RemoveObserver(this);
fStyle->ReleaseReference();
}
::Style* oldStyle = fStyle;
#endif
fStyle = style;
#ifdef ICON_O_MATIC
if (fStyle) {
fStyle->AcquireReference();
fStyle->AddObserver(this);
}
_NotifyStyleChanged(oldStyle, fStyle);
#endif
}
BRect
Shape::Bounds(bool updateLast) const
{
uint32 pathID[1];
pathID[0] = 0;
double left, top, right, bottom;
::VertexSource& source = const_cast<Shape*>(this)->VertexSource();
agg::conv_transform< ::VertexSource, Transformable>
transformedSource(source, *this);
agg::bounding_rect(transformedSource, pathID, 0, 1,
&left, &top, &right, &bottom);
BRect bounds(left, top, right, bottom);
if (updateLast)
fLastBounds = bounds;
return bounds;
}
::VertexSource&
Shape::VertexSource()
{
::VertexSource* source = &fPathSource;
int32 count = fTransformers.CountItems();
for (int32 i = 0; i < count; i++) {
PathTransformer* t = dynamic_cast<PathTransformer*>(fTransformers.ItemAtFast(i));
if (t != NULL) {
t->SetSource(*source);
source = t;
}
}
if (fNeedsUpdate) {
fPathSource.Update(source->WantsOpenPaths(),
source->ApproximationScale());
fNeedsUpdate = false;
}
return *source;
}
void
Shape::SetGlobalScale(double scale)
{
fPathSource.SetGlobalScale(scale);
}
#ifdef ICON_O_MATIC
bool
Shape::AddListener(ShapeListener* listener)
{
if (listener && !fListeners.HasItem((void*)listener))
return fListeners.AddItem((void*)listener);
return false;
}
bool
Shape::RemoveListener(ShapeListener* listener)
{
return fListeners.RemoveItem((void*)listener);
}
void
Shape::_NotifyStyleChanged(::Style* oldStyle, ::Style* newStyle) const
{
BList listeners(fListeners);
int32 count = listeners.CountItems();
for (int32 i = 0; i < count; i++) {
ShapeListener* listener
= (ShapeListener*)listeners.ItemAtFast(i);
listener->StyleChanged(oldStyle, newStyle);
}
_NotifyRerender();
}
void
Shape::_NotifyRerender() const
{
fNeedsUpdate = true;
Notify();
}
#endif
void
Shape::SetHinting(bool hinting)
{
if (fHinting == hinting)
return;
fHinting = hinting;
Notify();
}