xref: /haiku/src/libs/icon/shape/Shape.cpp (revision 2b76973fa2401f7a5edf68e6470f3d3210cbcff3)
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 
28 using std::nothrow;
29 
30 #ifdef ICON_O_MATIC
31 // constructor
32 ShapeListener::ShapeListener()
33 {
34 }
35 
36 // destructor
37 ShapeListener::~ShapeListener()
38 {
39 }
40 #endif // ICON_O_MATIC
41 
42 // #pragma mark -
43 
44 // constructor
45 Shape::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
81 Shape::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
137 Shape::~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
161 status_t
162 Shape::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
225 status_t
226 Shape::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
271 PropertyObject*
272 Shape::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
290 bool
291 Shape::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
313 void
314 Shape::TransformationChanged()
315 {
316 	// TODO: notify appearance change
317 	_NotifyRerender();
318 }
319 
320 // #pragma mark -
321 
322 // ObjectChanged
323 void
324 Shape::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
335 void
336 Shape::PathAdded(VectorPath* path, int32 index)
337 {
338 	path->Acquire();
339 	path->AddListener(this);
340 	_NotifyRerender();
341 }
342 
343 // PathRemoved
344 void
345 Shape::PathRemoved(VectorPath* path)
346 {
347 	path->RemoveListener(this);
348 	_NotifyRerender();
349 	path->Release();
350 }
351 
352 // #pragma mark -
353 
354 // PointAdded
355 void
356 Shape::PointAdded(int32 index)
357 {
358 	_NotifyRerender();
359 }
360 
361 // PointRemoved
362 void
363 Shape::PointRemoved(int32 index)
364 {
365 	_NotifyRerender();
366 }
367 
368 // PointChanged
369 void
370 Shape::PointChanged(int32 index)
371 {
372 	_NotifyRerender();
373 }
374 
375 // PathChanged
376 void
377 Shape::PathChanged()
378 {
379 	_NotifyRerender();
380 }
381 
382 // PathClosedChanged
383 void
384 Shape::PathClosedChanged()
385 {
386 	_NotifyRerender();
387 }
388 
389 // PathReversed
390 void
391 Shape::PathReversed()
392 {
393 	_NotifyRerender();
394 }
395 
396 #endif // ICON_O_MATIC
397 
398 
399 // #pragma mark -
400 
401 // InitCheck
402 status_t
403 Shape::InitCheck() const
404 {
405 	return fPaths ? B_OK : B_NO_MEMORY;
406 }
407 
408 // #pragma mark -
409 
410 // SetStyle
411 void
412 Shape::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->Release();
421 	}
422 	::Style* oldStyle = fStyle;
423 #endif
424 
425 	fStyle = style;
426 
427 #ifdef ICON_O_MATIC
428 	if (fStyle) {
429 		fStyle->Acquire();
430 		fStyle->AddObserver(this);
431 	}
432 
433 	_NotifyStyleChanged(oldStyle, fStyle);
434 #endif
435 }
436 
437 // #pragma mark -
438 
439 // Bounds
440 BRect
441 Shape::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&
469 Shape::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
490 void
491 Shape::SetGlobalScale(double scale)
492 {
493 	fPathSource.SetGlobalScale(scale);
494 }
495 
496 // AddTransformer
497 bool
498 Shape::AddTransformer(Transformer* transformer)
499 {
500 	return AddTransformer(transformer, CountTransformers());
501 }
502 
503 // AddTransformer
504 bool
505 Shape::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
524 bool
525 Shape::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
544 int32
545 Shape::CountTransformers() const
546 {
547 	return fTransformers.CountItems();
548 }
549 
550 // HasTransformer
551 bool
552 Shape::HasTransformer(Transformer* transformer) const
553 {
554 	return fTransformers.HasItem((void*)transformer);
555 }
556 
557 // IndexOf
558 int32
559 Shape::IndexOf(Transformer* transformer) const
560 {
561 	return fTransformers.IndexOf((void*)transformer);
562 }
563 
564 // TransformerAt
565 Transformer*
566 Shape::TransformerAt(int32 index) const
567 {
568 	return (Transformer*)fTransformers.ItemAt(index);
569 }
570 
571 // TransformerAtFast
572 Transformer*
573 Shape::TransformerAtFast(int32 index) const
574 {
575 	return (Transformer*)fTransformers.ItemAtFast(index);
576 }
577 
578 // #pragma mark -
579 
580 // SetHinting
581 void
582 Shape::SetHinting(bool hinting)
583 {
584 	if (fHinting == hinting)
585 		return;
586 
587 	fHinting = hinting;
588 	Notify();
589 }
590 
591 // SetMinVisibilityScale
592 void
593 Shape::SetMinVisibilityScale(float scale)
594 {
595 	if (fMinVisibilityScale == scale)
596 		return;
597 
598 	fMinVisibilityScale = scale;
599 	Notify();
600 }
601 
602 // SetMaxVisibilityScale
603 void
604 Shape::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
618 bool
619 Shape::AddListener(ShapeListener* listener)
620 {
621 	if (listener && !fListeners.HasItem((void*)listener))
622 		return fListeners.AddItem((void*)listener);
623 	return false;
624 }
625 
626 // RemoveListener
627 bool
628 Shape::RemoveListener(ShapeListener* listener)
629 {
630 	return fListeners.RemoveItem((void*)listener);
631 }
632 
633 // #pragma mark -
634 
635 // _NotifyTransformerAdded
636 void
637 Shape::_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
651 void
652 Shape::_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
666 void
667 Shape::_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
681 void
682 Shape::_NotifyRerender() const
683 {
684 	fNeedsUpdate = true;
685 	Notify();
686 }
687 
688 #endif // ICON_O_MATIC
689 
690