xref: /haiku/src/libs/icon/shape/Shape.cpp (revision e1c4049fed1047bdb957b0529e1921e97ef94770)
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 
32 using std::nothrow;
33 
34 
35 #ifdef ICON_O_MATIC
36 ShapeListener::ShapeListener()
37 {
38 }
39 
40 
41 ShapeListener::~ShapeListener()
42 {
43 }
44 #endif // ICON_O_MATIC
45 
46 
47 // #pragma mark -
48 
49 
50 Shape::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 
86 Shape::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 
143 Shape::~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 
161 status_t
162 Shape::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
205 status_t
206 Shape::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 
241 PropertyObject*
242 Shape::MakePropertyObject() const
243 {
244 	PropertyObject* object = IconObject::MakePropertyObject();
245 	return object;
246 }
247 
248 
249 bool
250 Shape::SetToPropertyObject(const PropertyObject* object)
251 {
252 	IconObject::SetToPropertyObject(object);
253 	return true;
254 }
255 
256 
257 // #pragma mark -
258 
259 
260 void
261 Shape::TransformationChanged()
262 {
263 	// TODO: notify appearance change
264 	_NotifyRerender();
265 }
266 
267 
268 // #pragma mark -
269 
270 
271 void
272 Shape::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 
284 void
285 Shape::ItemAdded(VectorPath* path, int32 index)
286 {
287 	path->AcquireReference();
288 	path->AddListener(this);
289 	_NotifyRerender();
290 }
291 
292 
293 void
294 Shape::ItemRemoved(VectorPath* path)
295 {
296 	path->RemoveListener(this);
297 	_NotifyRerender();
298 	path->ReleaseReference();
299 }
300 
301 
302 // #pragma mark -
303 
304 
305 void
306 Shape::PointAdded(int32 index)
307 {
308 	_NotifyRerender();
309 }
310 
311 
312 void
313 Shape::PointRemoved(int32 index)
314 {
315 	_NotifyRerender();
316 }
317 
318 
319 void
320 Shape::PointChanged(int32 index)
321 {
322 	_NotifyRerender();
323 }
324 
325 
326 void
327 Shape::PathChanged()
328 {
329 	_NotifyRerender();
330 }
331 
332 
333 void
334 Shape::PathClosedChanged()
335 {
336 	_NotifyRerender();
337 }
338 
339 
340 void
341 Shape::PathReversed()
342 {
343 	_NotifyRerender();
344 }
345 #endif // ICON_O_MATIC
346 
347 
348 // #pragma mark -
349 
350 
351 void
352 Shape::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 
365 void
366 Shape::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 
381 status_t
382 Shape::InitCheck() const
383 {
384 	return fPaths ? B_OK : B_NO_MEMORY;
385 }
386 
387 
388 // #pragma mark -
389 
390 
391 void
392 Shape::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 
421 BRect
422 Shape::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&
450 Shape::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 
473 void
474 Shape::SetGlobalScale(double scale)
475 {
476 	fPathSource.SetGlobalScale(scale);
477 }
478 
479 
480 // #pragma mark -
481 
482 
483 #ifdef ICON_O_MATIC
484 bool
485 Shape::AddListener(ShapeListener* listener)
486 {
487 	if (listener && !fListeners.HasItem((void*)listener))
488 		return fListeners.AddItem((void*)listener);
489 	return false;
490 }
491 
492 
493 bool
494 Shape::RemoveListener(ShapeListener* listener)
495 {
496 	return fListeners.RemoveItem((void*)listener);
497 }
498 
499 
500 // #pragma mark -
501 
502 
503 void
504 Shape::_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 
518 void
519 Shape::_NotifyRerender() const
520 {
521 	fNeedsUpdate = true;
522 	Notify();
523 }
524 #endif // ICON_O_MATIC
525 
526 
527 void
528 Shape::SetHinting(bool hinting)
529 {
530 	if (fHinting == hinting)
531 		return;
532 
533 	fHinting = hinting;
534 	Notify();
535 }
536 
537