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