1/*
2 * Copyright 2006, 2023, Haiku. All rights reserved.
3 * Distributed under the terms of the MIT License.
4 *
5 * Authors:
6 *		Stephan A��mus <superstippi@gmx.de>
7 *		Zardshard
8 */
9
10#include "Shape.h"
11
12#include <Message.h>
13#include <TypeConstants.h>
14
15#include <new>
16#include <limits.h>
17#include <stdio.h>
18
19#include "agg_bounding_rect.h"
20
21#ifdef ICON_O_MATIC
22# include "CommonPropertyIDs.h"
23# include "Property.h"
24# include "PropertyObject.h"
25#endif // ICON_O_MATIC
26#include "Container.h"
27#include "PathTransformer.h"
28#include "Style.h"
29#include "TransformerFactory.h"
30#include "VectorPath.h"
31
32using std::nothrow;
33
34
35#ifdef ICON_O_MATIC
36ShapeListener::ShapeListener()
37{
38}
39
40
41ShapeListener::~ShapeListener()
42{
43}
44#endif // ICON_O_MATIC
45
46
47// #pragma mark -
48
49
50Shape::Shape(::Style* style)
51#ifdef ICON_O_MATIC
52	: IconObject("<shape>"),
53	  Transformable(),
54	  Observer(),
55	  ContainerListener<VectorPath>(),
56#else
57	: Transformable(),
58#endif
59
60	  fPaths(new (nothrow) Container<VectorPath>(false)),
61	  fStyle(NULL),
62
63	  fPathSource(fPaths),
64	  fTransformers(true),
65	  fNeedsUpdate(true),
66
67	  fLastBounds(0, 0, -1, -1),
68
69	  fHinting(false)
70
71#ifdef ICON_O_MATIC
72	, fListeners(8)
73#endif
74{
75	SetStyle(style);
76
77	fTransformers.AddListener(this);
78
79#ifdef ICON_O_MATIC
80	if (fPaths)
81		fPaths->AddListener(this);
82#endif
83}
84
85
86Shape::Shape(const Shape& other)
87#ifdef ICON_O_MATIC
88	: IconObject(other),
89	  Transformable(other),
90	  Observer(),
91	  ContainerListener<VectorPath>(),
92#else
93	: Transformable(other),
94#endif
95
96	  fPaths(new (nothrow) Container<VectorPath>(false)),
97	  fStyle(NULL),
98
99	  fPathSource(fPaths),
100	  fTransformers(true),
101	  fNeedsUpdate(true),
102
103	  fLastBounds(0, 0, -1, -1),
104
105	  fHinting(false)
106
107#ifdef ICON_O_MATIC
108	, fListeners(8)
109#endif
110{
111	SetStyle(other.fStyle);
112
113	fTransformers.AddListener(this);
114
115	if (fPaths) {
116#ifdef ICON_O_MATIC
117		fPaths->AddListener(this);
118#endif
119
120		// copy the path references from
121		// the other shape
122		if (other.fPaths) {
123			int32 count = other.fPaths->CountItems();
124			for (int32 i = 0; i < count; i++) {
125				if (!fPaths->AddItem(other.fPaths->ItemAtFast(i)))
126					break;
127			}
128		}
129	}
130	// clone vertex transformers
131	int32 count = other.Transformers()->CountItems();
132	for (int32 i = 0; i < count; i++) {
133		Transformer* original = other.Transformers()->ItemAtFast(i);
134		Transformer* cloned = original->Clone();
135		if (!fTransformers.AddItem(cloned)) {
136			delete cloned;
137			break;
138		}
139	}
140}
141
142
143Shape::~Shape()
144{
145	fPaths->MakeEmpty();
146#ifdef ICON_O_MATIC
147	fPaths->RemoveListener(this);
148#endif
149	delete fPaths;
150
151	fTransformers.MakeEmpty();
152	fTransformers.RemoveListener(this);
153
154	SetStyle(NULL);
155}
156
157
158// #pragma mark -
159
160
161status_t
162Shape::Unarchive(BMessage* archive)
163{
164#ifdef ICON_O_MATIC
165	// IconObject properties
166	status_t ret = IconObject::Unarchive(archive);
167	if (ret < B_OK)
168		return ret;
169#else
170	status_t ret;
171#endif
172
173	// hinting
174	if (archive->FindBool("hinting", &fHinting) < B_OK)
175		fHinting = false;
176
177	// recreate transformers
178	BMessage transformerArchive;
179	for (int32 i = 0;
180		 archive->FindMessage("transformer", i,
181			 &transformerArchive) == B_OK;
182		 i++) {
183		Transformer* transformer
184			= TransformerFactory::TransformerFor(
185				&transformerArchive, VertexSource(), this);
186		if (!transformer || !fTransformers.AddItem(transformer)) {
187			delete transformer;
188		}
189	}
190
191	// read transformation
192	int32 size = Transformable::matrix_size;
193	const void* matrix;
194	ssize_t dataSize = size * sizeof(double);
195	ret = archive->FindData("transformation", B_DOUBLE_TYPE,
196		&matrix, &dataSize);
197	if (ret == B_OK && dataSize == (ssize_t)(size * sizeof(double)))
198		LoadFrom((const double*)matrix);
199
200	return B_OK;
201}
202
203
204#ifdef ICON_O_MATIC
205status_t
206Shape::Archive(BMessage* into, bool deep) const
207{
208	status_t ret = IconObject::Archive(into, deep);
209
210	// hinting
211	if (ret == B_OK)
212		ret = into->AddBool("hinting", fHinting);
213
214	// transformers
215	if (ret == B_OK) {
216		int32 count = fTransformers.CountItems();
217		for (int32 i = 0; i < count; i++) {
218			Transformer* transformer = fTransformers.ItemAtFast(i);
219			BMessage transformerArchive;
220			ret = transformer->Archive(&transformerArchive);
221			if (ret == B_OK)
222				ret = into->AddMessage("transformer", &transformerArchive);
223			if (ret < B_OK)
224				break;
225		}
226	}
227
228	// transformation
229	if (ret == B_OK) {
230		int32 size = Transformable::matrix_size;
231		double matrix[size];
232		StoreTo(matrix);
233		ret = into->AddData("transformation", B_DOUBLE_TYPE,
234			matrix, size * sizeof(double));
235	}
236
237	return ret;
238}
239
240
241PropertyObject*
242Shape::MakePropertyObject() const
243{
244	PropertyObject* object = IconObject::MakePropertyObject();
245	return object;
246}
247
248
249bool
250Shape::SetToPropertyObject(const PropertyObject* object)
251{
252	IconObject::SetToPropertyObject(object);
253	return true;
254}
255
256
257// #pragma mark -
258
259
260void
261Shape::TransformationChanged()
262{
263	// TODO: notify appearance change
264	_NotifyRerender();
265}
266
267
268// #pragma mark -
269
270
271void
272Shape::ObjectChanged(const Observable* object)
273{
274	// simply pass on the event for now
275	// (a path, transformer or the style changed,
276	// the shape needs to be re-rendered)
277	_NotifyRerender();
278}
279
280
281// #pragma mark -
282
283
284void
285Shape::ItemAdded(VectorPath* path, int32 index)
286{
287	path->AcquireReference();
288	path->AddListener(this);
289	_NotifyRerender();
290}
291
292
293void
294Shape::ItemRemoved(VectorPath* path)
295{
296	path->RemoveListener(this);
297	_NotifyRerender();
298	path->ReleaseReference();
299}
300
301
302// #pragma mark -
303
304
305void
306Shape::PointAdded(int32 index)
307{
308	_NotifyRerender();
309}
310
311
312void
313Shape::PointRemoved(int32 index)
314{
315	_NotifyRerender();
316}
317
318
319void
320Shape::PointChanged(int32 index)
321{
322	_NotifyRerender();
323}
324
325
326void
327Shape::PathChanged()
328{
329	_NotifyRerender();
330}
331
332
333void
334Shape::PathClosedChanged()
335{
336	_NotifyRerender();
337}
338
339
340void
341Shape::PathReversed()
342{
343	_NotifyRerender();
344}
345#endif // ICON_O_MATIC
346
347
348// #pragma mark -
349
350
351void
352Shape::ItemAdded(Transformer* transformer, int32 index)
353{
354#ifdef ICON_O_MATIC
355	transformer->AddObserver(this);
356
357	// TODO: merge Observable and ShapeListener interface
358	_NotifyRerender();
359#else
360	fNeedsUpdate = true;
361#endif
362}
363
364
365void
366Shape::ItemRemoved(Transformer* transformer)
367{
368#ifdef ICON_O_MATIC
369	transformer->RemoveObserver(this);
370
371	_NotifyRerender();
372#else
373	fNeedsUpdate = true;
374#endif
375}
376
377
378// #pragma mark -
379
380
381status_t
382Shape::InitCheck() const
383{
384	return fPaths ? B_OK : B_NO_MEMORY;
385}
386
387
388// #pragma mark -
389
390
391void
392Shape::SetStyle(::Style* style)
393{
394	if (fStyle == style)
395		return;
396
397#ifdef ICON_O_MATIC
398	if (fStyle) {
399		fStyle->RemoveObserver(this);
400		fStyle->ReleaseReference();
401	}
402	::Style* oldStyle = fStyle;
403#endif
404
405	fStyle = style;
406
407#ifdef ICON_O_MATIC
408	if (fStyle) {
409		fStyle->AcquireReference();
410		fStyle->AddObserver(this);
411	}
412
413	_NotifyStyleChanged(oldStyle, fStyle);
414#endif
415}
416
417
418// #pragma mark -
419
420
421BRect
422Shape::Bounds(bool updateLast) const
423{
424	// TODO: what about sub-paths?!?
425	// the problem is that the path ids are
426	// nowhere stored while converting VectorPath
427	// to agg::path_storage, but it is also unclear
428	// if those would mean anything later on in
429	// the Transformer pipeline
430	uint32 pathID[1];
431	pathID[0] = 0;
432	double left, top, right, bottom;
433
434	::VertexSource& source = const_cast<Shape*>(this)->VertexSource();
435	agg::conv_transform< ::VertexSource, Transformable>
436		transformedSource(source, *this);
437	agg::bounding_rect(transformedSource, pathID, 0, 1,
438		&left, &top, &right, &bottom);
439
440	BRect bounds(left, top, right, bottom);
441
442	if (updateLast)
443		fLastBounds = bounds;
444
445	return bounds;
446}
447
448
449::VertexSource&
450Shape::VertexSource()
451{
452	::VertexSource* source = &fPathSource;
453
454	int32 count = fTransformers.CountItems();
455	for (int32 i = 0; i < count; i++) {
456		PathTransformer* t = dynamic_cast<PathTransformer*>(fTransformers.ItemAtFast(i));
457		if (t != NULL) {
458			t->SetSource(*source);
459			source = t;
460		}
461	}
462
463	if (fNeedsUpdate) {
464		fPathSource.Update(source->WantsOpenPaths(),
465			source->ApproximationScale());
466		fNeedsUpdate = false;
467	}
468
469	return *source;
470}
471
472
473void
474Shape::SetGlobalScale(double scale)
475{
476	fPathSource.SetGlobalScale(scale);
477}
478
479
480// #pragma mark -
481
482
483#ifdef ICON_O_MATIC
484bool
485Shape::AddListener(ShapeListener* listener)
486{
487	if (listener && !fListeners.HasItem((void*)listener))
488		return fListeners.AddItem((void*)listener);
489	return false;
490}
491
492
493bool
494Shape::RemoveListener(ShapeListener* listener)
495{
496	return fListeners.RemoveItem((void*)listener);
497}
498
499
500// #pragma mark -
501
502
503void
504Shape::_NotifyStyleChanged(::Style* oldStyle, ::Style* newStyle) const
505{
506	BList listeners(fListeners);
507	int32 count = listeners.CountItems();
508	for (int32 i = 0; i < count; i++) {
509		ShapeListener* listener
510			= (ShapeListener*)listeners.ItemAtFast(i);
511		listener->StyleChanged(oldStyle, newStyle);
512	}
513	// TODO: merge Observable and ShapeListener interface
514	_NotifyRerender();
515}
516
517
518void
519Shape::_NotifyRerender() const
520{
521	fNeedsUpdate = true;
522	Notify();
523}
524#endif // ICON_O_MATIC
525
526
527void
528Shape::SetHinting(bool hinting)
529{
530	if (fHinting == hinting)
531		return;
532
533	fHinting = hinting;
534	Notify();
535}
536
537