1/*
2 * Copyright 2006, Haiku. All rights reserved.
3 * Distributed under the terms of the MIT License.
4 *
5 * Authors:
6 *		Stephan Aßmus <superstippi@gmx.de>
7 */
8
9#include "Shape.h"
10
11#include <Message.h>
12#include <TypeConstants.h>
13
14#include <new>
15#include <limits.h>
16#include <stdio.h>
17
18#include "agg_bounding_rect.h"
19
20#ifdef ICON_O_MATIC
21# include "CommonPropertyIDs.h"
22# include "Property.h"
23# include "PropertyObject.h"
24#endif // ICON_O_MATIC
25#include "Style.h"
26#include "TransformerFactory.h"
27
28using std::nothrow;
29
30#ifdef ICON_O_MATIC
31// constructor
32ShapeListener::ShapeListener()
33{
34}
35
36// destructor
37ShapeListener::~ShapeListener()
38{
39}
40#endif // ICON_O_MATIC
41
42// #pragma mark -
43
44// constructor
45Shape::Shape(::Style* style)
46#ifdef ICON_O_MATIC
47	: IconObject("<shape>"),
48	  Transformable(),
49	  Observer(),
50	  PathContainerListener(),
51#else
52	: Transformable(),
53#endif
54
55	  fPaths(new (nothrow) PathContainer(false)),
56	  fStyle(NULL),
57
58	  fPathSource(fPaths),
59	  fTransformers(4),
60	  fNeedsUpdate(true),
61
62	  fLastBounds(0, 0, -1, -1),
63
64	  fHinting(false),
65	  fMinVisibilityScale(0.0),
66	  fMaxVisibilityScale(4.0)
67
68#ifdef ICON_O_MATIC
69	, fListeners(8)
70#endif
71{
72	SetStyle(style);
73
74#ifdef ICON_O_MATIC
75	if (fPaths)
76		fPaths->AddListener(this);
77#endif
78}
79
80// constructor
81Shape::Shape(const Shape& other)
82#ifdef ICON_O_MATIC
83	: IconObject(other),
84	  Transformable(other),
85	  Observer(),
86	  PathContainerListener(),
87#else
88	: Transformable(other),
89#endif
90
91	  fPaths(new (nothrow) PathContainer(false)),
92	  fStyle(NULL),
93
94	  fPathSource(fPaths),
95	  fTransformers(4),
96	  fNeedsUpdate(true),
97
98	  fLastBounds(0, 0, -1, -1),
99
100	  fHinting(other.fHinting),
101	  fMinVisibilityScale(other.fMinVisibilityScale),
102	  fMaxVisibilityScale(other.fMaxVisibilityScale)
103
104#ifdef ICON_O_MATIC
105	, fListeners(8)
106#endif
107{
108	SetStyle(other.fStyle);
109
110	if (fPaths) {
111#ifdef ICON_O_MATIC
112		fPaths->AddListener(this);
113#endif
114		// copy the path references from
115		// the other shape
116		if (other.fPaths) {
117			int32 count = other.fPaths->CountPaths();
118			for (int32 i = 0; i < count; i++) {
119				if (!fPaths->AddPath(other.fPaths->PathAtFast(i)))
120					break;
121			}
122		}
123	}
124	// clone vertex transformers
125	int32 count = other.CountTransformers();
126	for (int32 i = 0; i < count; i++) {
127		Transformer* original = other.TransformerAtFast(i);
128		Transformer* cloned = original->Clone(fPathSource);
129		if (!AddTransformer(cloned)) {
130			delete cloned;
131			break;
132		}
133	}
134}
135
136// destructor
137Shape::~Shape()
138{
139	int32 count = fTransformers.CountItems();
140	for (int32 i = 0; i < count; i++) {
141		Transformer* t = (Transformer*)fTransformers.ItemAtFast(i);
142#ifdef ICON_O_MATIC
143		t->RemoveObserver(this);
144		_NotifyTransformerRemoved(t);
145#endif
146		delete t;
147	}
148
149	fPaths->MakeEmpty();
150#ifdef ICON_O_MATIC
151	fPaths->RemoveListener(this);
152#endif
153	delete fPaths;
154
155	SetStyle(NULL);
156}
157
158// #pragma mark -
159
160// Unarchive
161status_t
162Shape::Unarchive(const 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	// recreate transformers
174	BMessage transformerArchive;
175	for (int32 i = 0;
176		 archive->FindMessage("transformer", i,
177		 					  &transformerArchive) == B_OK;
178		 i++) {
179		Transformer* transformer
180			= TransformerFactory::TransformerFor(
181				&transformerArchive, VertexSource());
182		if (!transformer || !AddTransformer(transformer)) {
183			delete transformer;
184		}
185	}
186
187	// read transformation
188	int32 size = Transformable::matrix_size;
189	const void* matrix;
190	ssize_t dataSize = size * sizeof(double);
191	ret = archive->FindData("transformation", B_DOUBLE_TYPE,
192							&matrix, &dataSize);
193	if (ret == B_OK && dataSize == (ssize_t)(size * sizeof(double)))
194		LoadFrom((const double*)matrix);
195
196	// hinting
197	if (archive->FindBool("hinting", &fHinting) < B_OK)
198		fHinting = false;
199
200	// min visibility scale
201	if (archive->FindFloat("min visibility scale",
202						   &fMinVisibilityScale) < B_OK)
203		fMinVisibilityScale = 0.0;
204
205	// max visibility scale
206	if (archive->FindFloat("max visibility scale",
207						   &fMaxVisibilityScale) < B_OK)
208		fMaxVisibilityScale = 4.0;
209
210	if (fMinVisibilityScale < 0.0)
211		fMinVisibilityScale = 0.0;
212	if (fMinVisibilityScale > 4.0)
213		fMinVisibilityScale = 4.0;
214	if (fMaxVisibilityScale < 0.0)
215		fMaxVisibilityScale = 0.0;
216	if (fMaxVisibilityScale > 4.0)
217		fMaxVisibilityScale = 4.0;
218
219	return B_OK;
220}
221
222#ifdef ICON_O_MATIC
223
224// Archive
225status_t
226Shape::Archive(BMessage* into, bool deep) const
227{
228	status_t ret = IconObject::Archive(into, deep);
229
230	// transformers
231	if (ret == B_OK) {
232		int32 count = CountTransformers();
233		for (int32 i = 0; i < count; i++) {
234			Transformer* transformer = TransformerAtFast(i);
235			BMessage transformerArchive;
236			ret = transformer->Archive(&transformerArchive);
237			if (ret == B_OK)
238				ret = into->AddMessage("transformer", &transformerArchive);
239			if (ret < B_OK)
240				break;
241		}
242	}
243
244	// transformation
245	if (ret == B_OK) {
246		int32 size = Transformable::matrix_size;
247		double matrix[size];
248		StoreTo(matrix);
249		ret = into->AddData("transformation", B_DOUBLE_TYPE,
250							matrix, size * sizeof(double));
251	}
252
253	// hinting
254	if (ret ==B_OK)
255		ret = into->AddBool("hinting", fHinting);
256
257	// min visibility scale
258	if (ret ==B_OK)
259		ret = into->AddFloat("min visibility scale",
260							 fMinVisibilityScale);
261
262	// max visibility scale
263	if (ret ==B_OK)
264		ret = into->AddFloat("max visibility scale",
265							 fMaxVisibilityScale);
266
267	return ret;
268}
269
270// MakePropertyObject
271PropertyObject*
272Shape::MakePropertyObject() const
273{
274	PropertyObject* object = IconObject::MakePropertyObject();
275	if (!object)
276		return NULL;
277
278//	object->AddProperty(new BoolProperty(PROPERTY_HINTING, fHinting));
279
280	object->AddProperty(new FloatProperty(PROPERTY_MIN_VISIBILITY_SCALE,
281										  fMinVisibilityScale, 0, 4));
282
283	object->AddProperty(new FloatProperty(PROPERTY_MAX_VISIBILITY_SCALE,
284										  fMaxVisibilityScale, 0, 4));
285
286	return object;
287}
288
289// SetToPropertyObject
290bool
291Shape::SetToPropertyObject(const PropertyObject* object)
292{
293	AutoNotificationSuspender _(this);
294	IconObject::SetToPropertyObject(object);
295
296	// hinting
297//	SetHinting(object->Value(PROPERTY_HINTING, fHinting));
298
299	// min visibility scale
300	SetMinVisibilityScale(object->Value(PROPERTY_MIN_VISIBILITY_SCALE,
301										fMinVisibilityScale));
302
303	// max visibility scale
304	SetMaxVisibilityScale(object->Value(PROPERTY_MAX_VISIBILITY_SCALE,
305										fMaxVisibilityScale));
306
307	return HasPendingNotifications();
308}
309
310// #pragma mark -
311
312// TransformationChanged
313void
314Shape::TransformationChanged()
315{
316	// TODO: notify appearance change
317	_NotifyRerender();
318}
319
320// #pragma mark -
321
322// ObjectChanged
323void
324Shape::ObjectChanged(const Observable* object)
325{
326	// simply pass on the event for now
327	// (a path, transformer or the style changed,
328	// the shape needs to be re-rendered)
329	_NotifyRerender();
330}
331
332// #pragma mark -
333
334// PathAdded
335void
336Shape::PathAdded(VectorPath* path, int32 index)
337{
338	path->AcquireReference();
339	path->AddListener(this);
340	_NotifyRerender();
341}
342
343// PathRemoved
344void
345Shape::PathRemoved(VectorPath* path)
346{
347	path->RemoveListener(this);
348	_NotifyRerender();
349	path->ReleaseReference();
350}
351
352// #pragma mark -
353
354// PointAdded
355void
356Shape::PointAdded(int32 index)
357{
358	_NotifyRerender();
359}
360
361// PointRemoved
362void
363Shape::PointRemoved(int32 index)
364{
365	_NotifyRerender();
366}
367
368// PointChanged
369void
370Shape::PointChanged(int32 index)
371{
372	_NotifyRerender();
373}
374
375// PathChanged
376void
377Shape::PathChanged()
378{
379	_NotifyRerender();
380}
381
382// PathClosedChanged
383void
384Shape::PathClosedChanged()
385{
386	_NotifyRerender();
387}
388
389// PathReversed
390void
391Shape::PathReversed()
392{
393	_NotifyRerender();
394}
395
396#endif // ICON_O_MATIC
397
398
399// #pragma mark -
400
401// InitCheck
402status_t
403Shape::InitCheck() const
404{
405	return fPaths ? B_OK : B_NO_MEMORY;
406}
407
408// #pragma mark -
409
410// SetStyle
411void
412Shape::SetStyle(::Style* style)
413{
414	if (fStyle == style)
415		return;
416
417#ifdef ICON_O_MATIC
418	if (fStyle) {
419		fStyle->RemoveObserver(this);
420		fStyle->ReleaseReference();
421	}
422	::Style* oldStyle = fStyle;
423#endif
424
425	fStyle = style;
426
427#ifdef ICON_O_MATIC
428	if (fStyle) {
429		fStyle->AcquireReference();
430		fStyle->AddObserver(this);
431	}
432
433	_NotifyStyleChanged(oldStyle, fStyle);
434#endif
435}
436
437// #pragma mark -
438
439// Bounds
440BRect
441Shape::Bounds(bool updateLast) const
442{
443	// TODO: what about sub-paths?!?
444	// the problem is that the path ids are
445	// nowhere stored while converting VectorPath
446	// to agg::path_storage, but it is also unclear
447	// if those would mean anything later on in
448	// the Transformer pipeline
449	uint32 pathID[1];
450	pathID[0] = 0;
451	double left, top, right, bottom;
452
453	::VertexSource& source = const_cast<Shape*>(this)->VertexSource();
454	agg::conv_transform< ::VertexSource, Transformable>
455			transformedSource(source, *this);
456	agg::bounding_rect(transformedSource, pathID, 0, 1,
457					   &left, &top, &right, &bottom);
458
459	BRect bounds(left, top, right, bottom);
460
461	if (updateLast)
462		fLastBounds = bounds;
463
464	return bounds;
465}
466
467// VertexSource
468::VertexSource&
469Shape::VertexSource()
470{
471	::VertexSource* source = &fPathSource;
472
473	int32 count = fTransformers.CountItems();
474	for (int32 i = 0; i < count; i++) {
475		Transformer* t = (Transformer*)fTransformers.ItemAtFast(i);
476		t->SetSource(*source);
477		source = t;
478	}
479
480	if (fNeedsUpdate) {
481		fPathSource.Update(source->WantsOpenPaths(),
482						   source->ApproximationScale());
483		fNeedsUpdate = false;
484	}
485
486	return *source;
487}
488
489// SetGlobalScale
490void
491Shape::SetGlobalScale(double scale)
492{
493	fPathSource.SetGlobalScale(scale);
494}
495
496// AddTransformer
497bool
498Shape::AddTransformer(Transformer* transformer)
499{
500	return AddTransformer(transformer, CountTransformers());
501}
502
503// AddTransformer
504bool
505Shape::AddTransformer(Transformer* transformer, int32 index)
506{
507	if (!transformer)
508		return false;
509
510	if (!fTransformers.AddItem((void*)transformer, index))
511		return false;
512
513#ifdef ICON_O_MATIC
514	transformer->AddObserver(this);
515
516	_NotifyTransformerAdded(transformer, index);
517#else
518	fNeedsUpdate = true;
519#endif
520	return true;
521}
522
523// RemoveTransformer
524bool
525Shape::RemoveTransformer(Transformer* transformer)
526{
527	if (fTransformers.RemoveItem((void*)transformer)) {
528#ifdef ICON_O_MATIC
529		transformer->RemoveObserver(this);
530
531		_NotifyTransformerRemoved(transformer);
532#else
533		fNeedsUpdate = true;
534#endif
535		return true;
536	}
537
538	return false;
539}
540
541// #pragma mark -
542
543// CountShapes
544int32
545Shape::CountTransformers() const
546{
547	return fTransformers.CountItems();
548}
549
550// HasTransformer
551bool
552Shape::HasTransformer(Transformer* transformer) const
553{
554	return fTransformers.HasItem((void*)transformer);
555}
556
557// IndexOf
558int32
559Shape::IndexOf(Transformer* transformer) const
560{
561	return fTransformers.IndexOf((void*)transformer);
562}
563
564// TransformerAt
565Transformer*
566Shape::TransformerAt(int32 index) const
567{
568	return (Transformer*)fTransformers.ItemAt(index);
569}
570
571// TransformerAtFast
572Transformer*
573Shape::TransformerAtFast(int32 index) const
574{
575	return (Transformer*)fTransformers.ItemAtFast(index);
576}
577
578// #pragma mark -
579
580// SetHinting
581void
582Shape::SetHinting(bool hinting)
583{
584	if (fHinting == hinting)
585		return;
586
587	fHinting = hinting;
588	Notify();
589}
590
591// SetMinVisibilityScale
592void
593Shape::SetMinVisibilityScale(float scale)
594{
595	if (fMinVisibilityScale == scale)
596		return;
597
598	fMinVisibilityScale = scale;
599	Notify();
600}
601
602// SetMaxVisibilityScale
603void
604Shape::SetMaxVisibilityScale(float scale)
605{
606	if (fMaxVisibilityScale == scale)
607		return;
608
609	fMaxVisibilityScale = scale;
610	Notify();
611}
612
613// #pragma mark -
614
615#ifdef ICON_O_MATIC
616
617// AddListener
618bool
619Shape::AddListener(ShapeListener* listener)
620{
621	if (listener && !fListeners.HasItem((void*)listener))
622		return fListeners.AddItem((void*)listener);
623	return false;
624}
625
626// RemoveListener
627bool
628Shape::RemoveListener(ShapeListener* listener)
629{
630	return fListeners.RemoveItem((void*)listener);
631}
632
633// #pragma mark -
634
635// _NotifyTransformerAdded
636void
637Shape::_NotifyTransformerAdded(Transformer* transformer, int32 index) const
638{
639	BList listeners(fListeners);
640	int32 count = listeners.CountItems();
641	for (int32 i = 0; i < count; i++) {
642		ShapeListener* listener
643			= (ShapeListener*)listeners.ItemAtFast(i);
644		listener->TransformerAdded(transformer, index);
645	}
646	// TODO: merge Observable and ShapeListener interface
647	_NotifyRerender();
648}
649
650// _NotifyTransformerRemoved
651void
652Shape::_NotifyTransformerRemoved(Transformer* transformer) const
653{
654	BList listeners(fListeners);
655	int32 count = listeners.CountItems();
656	for (int32 i = 0; i < count; i++) {
657		ShapeListener* listener
658			= (ShapeListener*)listeners.ItemAtFast(i);
659		listener->TransformerRemoved(transformer);
660	}
661	// TODO: merge Observable and ShapeListener interface
662	_NotifyRerender();
663}
664
665// _NotifyStyleChanged
666void
667Shape::_NotifyStyleChanged(::Style* oldStyle, ::Style* newStyle) const
668{
669	BList listeners(fListeners);
670	int32 count = listeners.CountItems();
671	for (int32 i = 0; i < count; i++) {
672		ShapeListener* listener
673			= (ShapeListener*)listeners.ItemAtFast(i);
674		listener->StyleChanged(oldStyle, newStyle);
675	}
676	// TODO: merge Observable and ShapeListener interface
677	_NotifyRerender();
678}
679
680// _NotifyRerender
681void
682Shape::_NotifyRerender() const
683{
684	fNeedsUpdate = true;
685	Notify();
686}
687
688#endif // ICON_O_MATIC
689
690