xref: /haiku/src/libs/icon/shape/Shape.cpp (revision 1564b19c80637a2ba13949db0056215699b7b418)
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(255.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 #ifdef ICON_O_MATIC
161 
162 // Unarchive
163 status_t
164 Shape::Unarchive(const BMessage* archive)
165 {
166 	// IconObject properties
167 	status_t ret = IconObject::Unarchive(archive);
168 	if (ret < B_OK)
169 		return ret;
170 
171 	// recreate transformers
172 	BMessage transformerArchive;
173 	for (int32 i = 0;
174 		 archive->FindMessage("transformer", i,
175 		 					  &transformerArchive) == B_OK;
176 		 i++) {
177 		Transformer* transformer
178 			= TransformerFactory::TransformerFor(
179 				&transformerArchive, VertexSource());
180 		if (!transformer || !AddTransformer(transformer)) {
181 			delete transformer;
182 		}
183 	}
184 
185 	// read transformation
186 	int32 size = Transformable::matrix_size;
187 	const void* matrix;
188 	ssize_t dataSize = size * sizeof(double);
189 	ret = archive->FindData("transformation", B_DOUBLE_TYPE,
190 							&matrix, &dataSize);
191 	if (ret == B_OK && dataSize == (ssize_t)(size * sizeof(double)))
192 		LoadFrom((const double*)matrix);
193 
194 	// hinting
195 	if (archive->FindBool("hinting", &fHinting) < B_OK)
196 		fHinting = false;
197 
198 	// min visibility scale
199 	if (archive->FindFloat("min visibility scale",
200 						   &fMinVisibilityScale) < B_OK)
201 		fMinVisibilityScale = 0.0;
202 
203 	// max visibility scale
204 	if (archive->FindFloat("max visibility scale",
205 						   &fMaxVisibilityScale) < B_OK)
206 		fMaxVisibilityScale = 255.0;
207 
208 	if (fMinVisibilityScale < 0.0)
209 		fMinVisibilityScale = 0.0;
210 	if (fMinVisibilityScale > 255.0)
211 		fMinVisibilityScale = 255.0;
212 	if (fMaxVisibilityScale < 0.0)
213 		fMaxVisibilityScale = 0.0;
214 	if (fMaxVisibilityScale > 255.0)
215 		fMaxVisibilityScale = 255.0;
216 
217 	return B_OK;
218 }
219 
220 // Archive
221 status_t
222 Shape::Archive(BMessage* into, bool deep) const
223 {
224 	status_t ret = IconObject::Archive(into, deep);
225 
226 	// transformers
227 	if (ret == B_OK) {
228 		int32 count = CountTransformers();
229 		for (int32 i = 0; i < count; i++) {
230 			Transformer* transformer = TransformerAtFast(i);
231 			BMessage transformerArchive;
232 			ret = transformer->Archive(&transformerArchive);
233 			if (ret == B_OK)
234 				ret = into->AddMessage("transformer", &transformerArchive);
235 			if (ret < B_OK)
236 				break;
237 		}
238 	}
239 
240 	// transformation
241 	if (ret == B_OK) {
242 		int32 size = Transformable::matrix_size;
243 		double matrix[size];
244 		StoreTo(matrix);
245 		ret = into->AddData("transformation", B_DOUBLE_TYPE,
246 							matrix, size * sizeof(double));
247 	}
248 
249 	// hinting
250 	if (ret ==B_OK)
251 		ret = into->AddBool("hinting", fHinting);
252 
253 	// min visibility scale
254 	if (ret ==B_OK)
255 		ret = into->AddFloat("min visibility scale",
256 							 fMinVisibilityScale);
257 
258 	// max visibility scale
259 	if (ret ==B_OK)
260 		ret = into->AddFloat("max visibility scale",
261 							 fMaxVisibilityScale);
262 
263 	return ret;
264 }
265 
266 // MakePropertyObject
267 PropertyObject*
268 Shape::MakePropertyObject() const
269 {
270 	PropertyObject* object = IconObject::MakePropertyObject();
271 	if (!object)
272 		return NULL;
273 
274 	object->AddProperty(new BoolProperty(PROPERTY_HINTING, fHinting));
275 
276 	object->AddProperty(new FloatProperty(PROPERTY_MIN_VISIBILITY_SCALE,
277 										  fMinVisibilityScale, 0, 255));
278 
279 	object->AddProperty(new FloatProperty(PROPERTY_MAX_VISIBILITY_SCALE,
280 										  fMaxVisibilityScale, 0, 255));
281 
282 	return object;
283 }
284 
285 // SetToPropertyObject
286 bool
287 Shape::SetToPropertyObject(const PropertyObject* object)
288 {
289 	AutoNotificationSuspender _(this);
290 	IconObject::SetToPropertyObject(object);
291 
292 	// hinting
293 	SetHinting(object->Value(PROPERTY_HINTING, fHinting));
294 
295 	// min visibility scale
296 	SetMinVisibilityScale(object->Value(PROPERTY_MIN_VISIBILITY_SCALE,
297 										fMinVisibilityScale));
298 
299 	// max visibility scale
300 	SetMaxVisibilityScale(object->Value(PROPERTY_MAX_VISIBILITY_SCALE,
301 										fMaxVisibilityScale));
302 
303 	return HasPendingNotifications();
304 }
305 
306 // #pragma mark -
307 
308 // TransformationChanged
309 void
310 Shape::TransformationChanged()
311 {
312 	// TODO: notify appearance change
313 	_NotifyRerender();
314 }
315 
316 // #pragma mark -
317 
318 // ObjectChanged
319 void
320 Shape::ObjectChanged(const Observable* object)
321 {
322 	// simply pass on the event for now
323 	// (a path, transformer or the style changed,
324 	// the shape needs to be re-rendered)
325 	_NotifyRerender();
326 }
327 
328 // #pragma mark -
329 
330 // PathAdded
331 void
332 Shape::PathAdded(VectorPath* path)
333 {
334 	path->Acquire();
335 	path->AddListener(this);
336 	_NotifyRerender();
337 }
338 
339 // PathRemoved
340 void
341 Shape::PathRemoved(VectorPath* path)
342 {
343 	path->RemoveListener(this);
344 	_NotifyRerender();
345 	path->Release();
346 }
347 
348 // #pragma mark -
349 
350 // PointAdded
351 void
352 Shape::PointAdded(int32 index)
353 {
354 	_NotifyRerender();
355 }
356 
357 // PointRemoved
358 void
359 Shape::PointRemoved(int32 index)
360 {
361 	_NotifyRerender();
362 }
363 
364 // PointChanged
365 void
366 Shape::PointChanged(int32 index)
367 {
368 	_NotifyRerender();
369 }
370 
371 // PathChanged
372 void
373 Shape::PathChanged()
374 {
375 	_NotifyRerender();
376 }
377 
378 // PathClosedChanged
379 void
380 Shape::PathClosedChanged()
381 {
382 	_NotifyRerender();
383 }
384 
385 // PathReversed
386 void
387 Shape::PathReversed()
388 {
389 	_NotifyRerender();
390 }
391 
392 #endif // ICON_O_MATIC
393 
394 
395 // #pragma mark -
396 
397 // InitCheck
398 status_t
399 Shape::InitCheck() const
400 {
401 	return fPaths ? B_OK : B_NO_MEMORY;
402 }
403 
404 // #pragma mark -
405 
406 // SetStyle
407 void
408 Shape::SetStyle(::Style* style)
409 {
410 #ifdef ICON_O_MATIC
411 	if (fStyle == style)
412 		return;
413 
414 	if (fStyle) {
415 		fStyle->RemoveObserver(this);
416 		fStyle->Release();
417 	}
418 	::Style* oldStyle = fStyle;
419 #endif
420 
421 	fStyle = style;
422 
423 #ifdef ICON_O_MATIC
424 	if (fStyle) {
425 		fStyle->Acquire();
426 		fStyle->AddObserver(this);
427 	}
428 
429 	_NotifyStyleChanged(oldStyle, fStyle);
430 #endif
431 }
432 
433 // #pragma mark -
434 
435 // Bounds
436 BRect
437 Shape::Bounds(bool updateLast) const
438 {
439 	// TODO: what about sub-paths?!?
440 	// the problem is that the path ids are
441 	// nowhere stored while converting VectorPath
442 	// to agg::path_storage, but it is also unclear
443 	// if those would mean anything later on in
444 	// the Transformer pipeline
445 	uint32 pathID[1];
446 	pathID[0] = 0;
447 	double left, top, right, bottom;
448 
449 	::VertexSource& source = const_cast<Shape*>(this)->VertexSource();
450 	agg::conv_transform< ::VertexSource, Transformable>
451 			transformedSource(source, *this);
452 	agg::bounding_rect(transformedSource, pathID, 0, 1,
453 					   &left, &top, &right, &bottom);
454 
455 	BRect bounds(left, top, right, bottom);
456 
457 	if (updateLast)
458 		fLastBounds = bounds;
459 
460 	return bounds;
461 }
462 
463 // VertexSource
464 ::VertexSource&
465 Shape::VertexSource()
466 {
467 	::VertexSource* source = &fPathSource;
468 
469 	int32 count = fTransformers.CountItems();
470 	for (int32 i = 0; i < count; i++) {
471 		Transformer* t = (Transformer*)fTransformers.ItemAtFast(i);
472 		t->SetSource(*source);
473 		source = t;
474 	}
475 
476 	if (fNeedsUpdate) {
477 		fPathSource.Update(source->WantsOpenPaths(),
478 						   source->ApproximationScale());
479 		fNeedsUpdate = false;
480 	}
481 
482 	return *source;
483 }
484 
485 // AddTransformer
486 bool
487 Shape::AddTransformer(Transformer* transformer)
488 {
489 	return AddTransformer(transformer, CountTransformers());
490 }
491 
492 // AddTransformer
493 bool
494 Shape::AddTransformer(Transformer* transformer, int32 index)
495 {
496 	if (!transformer)
497 		return false;
498 
499 	if (!fTransformers.AddItem((void*)transformer, index))
500 		return false;
501 
502 #ifdef ICON_O_MATIC
503 	transformer->AddObserver(this);
504 
505 	_NotifyTransformerAdded(transformer, index);
506 #else
507 	fNeedsUpdate = true;
508 #endif
509 	return true;
510 }
511 
512 // RemoveTransformer
513 bool
514 Shape::RemoveTransformer(Transformer* transformer)
515 {
516 	if (fTransformers.RemoveItem((void*)transformer)) {
517 #ifdef ICON_O_MATIC
518 		transformer->RemoveObserver(this);
519 
520 		_NotifyTransformerRemoved(transformer);
521 #else
522 		fNeedsUpdate = true;
523 #endif
524 		return true;
525 	}
526 
527 	return false;
528 }
529 
530 // #pragma mark -
531 
532 // CountShapes
533 int32
534 Shape::CountTransformers() const
535 {
536 	return fTransformers.CountItems();
537 }
538 
539 // HasTransformer
540 bool
541 Shape::HasTransformer(Transformer* transformer) const
542 {
543 	return fTransformers.HasItem((void*)transformer);
544 }
545 
546 // IndexOf
547 int32
548 Shape::IndexOf(Transformer* transformer) const
549 {
550 	return fTransformers.IndexOf((void*)transformer);
551 }
552 
553 // TransformerAt
554 Transformer*
555 Shape::TransformerAt(int32 index) const
556 {
557 	return (Transformer*)fTransformers.ItemAt(index);
558 }
559 
560 // TransformerAtFast
561 Transformer*
562 Shape::TransformerAtFast(int32 index) const
563 {
564 	return (Transformer*)fTransformers.ItemAtFast(index);
565 }
566 
567 // #pragma mark -
568 
569 // SetHinting
570 void
571 Shape::SetHinting(bool hinting)
572 {
573 	if (fHinting == hinting)
574 		return;
575 
576 	fHinting = hinting;
577 	Notify();
578 }
579 
580 // SetMinVisibilityScale
581 void
582 Shape::SetMinVisibilityScale(float scale)
583 {
584 	if (fMinVisibilityScale == scale)
585 		return;
586 
587 	fMinVisibilityScale = scale;
588 	Notify();
589 }
590 
591 // SetMaxVisibilityScale
592 void
593 Shape::SetMaxVisibilityScale(float scale)
594 {
595 	if (fMaxVisibilityScale == scale)
596 		return;
597 
598 	fMaxVisibilityScale = scale;
599 	Notify();
600 }
601 
602 // #pragma mark -
603 
604 #ifdef ICON_O_MATIC
605 
606 // AddListener
607 bool
608 Shape::AddListener(ShapeListener* listener)
609 {
610 	if (listener && !fListeners.HasItem((void*)listener))
611 		return fListeners.AddItem((void*)listener);
612 	return false;
613 }
614 
615 // RemoveListener
616 bool
617 Shape::RemoveListener(ShapeListener* listener)
618 {
619 	return fListeners.RemoveItem((void*)listener);
620 }
621 
622 // #pragma mark -
623 
624 // _NotifyTransformerAdded
625 void
626 Shape::_NotifyTransformerAdded(Transformer* transformer, int32 index) const
627 {
628 	BList listeners(fListeners);
629 	int32 count = listeners.CountItems();
630 	for (int32 i = 0; i < count; i++) {
631 		ShapeListener* listener
632 			= (ShapeListener*)listeners.ItemAtFast(i);
633 		listener->TransformerAdded(transformer, index);
634 	}
635 	// TODO: merge Observable and ShapeListener interface
636 	_NotifyRerender();
637 }
638 
639 // _NotifyTransformerRemoved
640 void
641 Shape::_NotifyTransformerRemoved(Transformer* transformer) const
642 {
643 	BList listeners(fListeners);
644 	int32 count = listeners.CountItems();
645 	for (int32 i = 0; i < count; i++) {
646 		ShapeListener* listener
647 			= (ShapeListener*)listeners.ItemAtFast(i);
648 		listener->TransformerRemoved(transformer);
649 	}
650 	// TODO: merge Observable and ShapeListener interface
651 	_NotifyRerender();
652 }
653 
654 // _NotifyStyleChanged
655 void
656 Shape::_NotifyStyleChanged(::Style* oldStyle, ::Style* newStyle) const
657 {
658 	BList listeners(fListeners);
659 	int32 count = listeners.CountItems();
660 	for (int32 i = 0; i < count; i++) {
661 		ShapeListener* listener
662 			= (ShapeListener*)listeners.ItemAtFast(i);
663 		listener->StyleChanged(oldStyle, newStyle);
664 	}
665 	// TODO: merge Observable and ShapeListener interface
666 	_NotifyRerender();
667 }
668 
669 // _NotifyRerender
670 void
671 Shape::_NotifyRerender() const
672 {
673 	fNeedsUpdate = true;
674 	Notify();
675 }
676 
677 #endif // ICON_O_MATIC
678 
679