xref: /haiku/src/libs/icon/shape/Shape.cpp (revision 3386b8b7858f5659c205b8f088eb568692699591)
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 "Style.h"
28 #include "TransformerFactory.h"
29 #include "VectorPath.h"
30 
31 using std::nothrow;
32 
33 
34 #ifdef ICON_O_MATIC
35 ShapeListener::ShapeListener()
36 {
37 }
38 
39 
40 ShapeListener::~ShapeListener()
41 {
42 }
43 #endif // ICON_O_MATIC
44 
45 
46 // #pragma mark -
47 
48 
49 Shape::Shape(::Style* style)
50 #ifdef ICON_O_MATIC
51 	: IconObject("<shape>"),
52 	  Transformable(),
53 	  Observer(),
54 	  ContainerListener<VectorPath>(),
55 #else
56 	: Transformable(),
57 #endif
58 
59 	  fPaths(new (nothrow) Container<VectorPath>(false)),
60 	  fStyle(NULL),
61 
62 	  fPathSource(fPaths),
63 	  fTransformers(true),
64 	  fNeedsUpdate(true),
65 
66 	  fLastBounds(0, 0, -1, -1),
67 
68 	  fHinting(false)
69 
70 #ifdef ICON_O_MATIC
71 	, fListeners(8)
72 #endif
73 {
74 	SetStyle(style);
75 
76 	fTransformers.AddListener(this);
77 
78 #ifdef ICON_O_MATIC
79 	if (fPaths)
80 		fPaths->AddListener(this);
81 #endif
82 }
83 
84 
85 Shape::Shape(const Shape& other)
86 #ifdef ICON_O_MATIC
87 	: IconObject(other),
88 	  Transformable(other),
89 	  Observer(),
90 	  ContainerListener<VectorPath>(),
91 #else
92 	: Transformable(other),
93 #endif
94 
95 	  fPaths(new (nothrow) Container<VectorPath>(false)),
96 	  fStyle(NULL),
97 
98 	  fPathSource(fPaths),
99 	  fTransformers(true),
100 	  fNeedsUpdate(true),
101 
102 	  fLastBounds(0, 0, -1, -1),
103 
104 	  fHinting(false)
105 
106 #ifdef ICON_O_MATIC
107 	, fListeners(8)
108 #endif
109 {
110 	SetStyle(other.fStyle);
111 
112 	fTransformers.AddListener(this);
113 
114 	if (fPaths) {
115 #ifdef ICON_O_MATIC
116 		fPaths->AddListener(this);
117 #endif
118 
119 		// copy the path references from
120 		// the other shape
121 		if (other.fPaths) {
122 			int32 count = other.fPaths->CountItems();
123 			for (int32 i = 0; i < count; i++) {
124 				if (!fPaths->AddItem(other.fPaths->ItemAtFast(i)))
125 					break;
126 			}
127 		}
128 	}
129 	// clone vertex transformers
130 	int32 count = other.Transformers()->CountItems();
131 	for (int32 i = 0; i < count; i++) {
132 		Transformer* original = other.Transformers()->ItemAtFast(i);
133 		Transformer* cloned = original->Clone(fPathSource);
134 		if (!fTransformers.AddItem(cloned)) {
135 			delete cloned;
136 			break;
137 		}
138 	}
139 }
140 
141 
142 Shape::~Shape()
143 {
144 	fPaths->MakeEmpty();
145 #ifdef ICON_O_MATIC
146 	fPaths->RemoveListener(this);
147 #endif
148 	delete fPaths;
149 
150 	fTransformers.MakeEmpty();
151 	fTransformers.RemoveListener(this);
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 || !fTransformers.AddItem(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 = fTransformers.CountItems();
216 		for (int32 i = 0; i < count; i++) {
217 			Transformer* transformer = fTransformers.ItemAtFast(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::ItemAdded(VectorPath* path, int32 index)
285 {
286 	path->AcquireReference();
287 	path->AddListener(this);
288 	_NotifyRerender();
289 }
290 
291 
292 void
293 Shape::ItemRemoved(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 void
351 Shape::ItemAdded(Transformer* transformer, int32 index)
352 {
353 #ifdef ICON_O_MATIC
354 	transformer->AddObserver(this);
355 
356 	// TODO: merge Observable and ShapeListener interface
357 	_NotifyRerender();
358 #else
359 	fNeedsUpdate = true;
360 #endif
361 }
362 
363 
364 void
365 Shape::ItemRemoved(Transformer* transformer)
366 {
367 #ifdef ICON_O_MATIC
368 	transformer->RemoveObserver(this);
369 
370 	_NotifyRerender();
371 #else
372 	fNeedsUpdate = true;
373 #endif
374 }
375 
376 
377 // #pragma mark -
378 
379 
380 status_t
381 Shape::InitCheck() const
382 {
383 	return fPaths ? B_OK : B_NO_MEMORY;
384 }
385 
386 
387 // #pragma mark -
388 
389 
390 void
391 Shape::SetStyle(::Style* style)
392 {
393 	if (fStyle == style)
394 		return;
395 
396 #ifdef ICON_O_MATIC
397 	if (fStyle) {
398 		fStyle->RemoveObserver(this);
399 		fStyle->ReleaseReference();
400 	}
401 	::Style* oldStyle = fStyle;
402 #endif
403 
404 	fStyle = style;
405 
406 #ifdef ICON_O_MATIC
407 	if (fStyle) {
408 		fStyle->AcquireReference();
409 		fStyle->AddObserver(this);
410 	}
411 
412 	_NotifyStyleChanged(oldStyle, fStyle);
413 #endif
414 }
415 
416 
417 // #pragma mark -
418 
419 
420 BRect
421 Shape::Bounds(bool updateLast) const
422 {
423 	// TODO: what about sub-paths?!?
424 	// the problem is that the path ids are
425 	// nowhere stored while converting VectorPath
426 	// to agg::path_storage, but it is also unclear
427 	// if those would mean anything later on in
428 	// the Transformer pipeline
429 	uint32 pathID[1];
430 	pathID[0] = 0;
431 	double left, top, right, bottom;
432 
433 	::VertexSource& source = const_cast<Shape*>(this)->VertexSource();
434 	agg::conv_transform< ::VertexSource, Transformable>
435 		transformedSource(source, *this);
436 	agg::bounding_rect(transformedSource, pathID, 0, 1,
437 		&left, &top, &right, &bottom);
438 
439 	BRect bounds(left, top, right, bottom);
440 
441 	if (updateLast)
442 		fLastBounds = bounds;
443 
444 	return bounds;
445 }
446 
447 
448 ::VertexSource&
449 Shape::VertexSource()
450 {
451 	::VertexSource* source = &fPathSource;
452 
453 	int32 count = fTransformers.CountItems();
454 	for (int32 i = 0; i < count; i++) {
455 		Transformer* t = (Transformer*)fTransformers.ItemAtFast(i);
456 		t->SetSource(*source);
457 		source = t;
458 	}
459 
460 	if (fNeedsUpdate) {
461 		fPathSource.Update(source->WantsOpenPaths(),
462 			source->ApproximationScale());
463 		fNeedsUpdate = false;
464 	}
465 
466 	return *source;
467 }
468 
469 
470 void
471 Shape::SetGlobalScale(double scale)
472 {
473 	fPathSource.SetGlobalScale(scale);
474 }
475 
476 
477 // #pragma mark -
478 
479 
480 #ifdef ICON_O_MATIC
481 bool
482 Shape::AddListener(ShapeListener* listener)
483 {
484 	if (listener && !fListeners.HasItem((void*)listener))
485 		return fListeners.AddItem((void*)listener);
486 	return false;
487 }
488 
489 
490 bool
491 Shape::RemoveListener(ShapeListener* listener)
492 {
493 	return fListeners.RemoveItem((void*)listener);
494 }
495 
496 
497 // #pragma mark -
498 
499 
500 void
501 Shape::_NotifyStyleChanged(::Style* oldStyle, ::Style* newStyle) const
502 {
503 	BList listeners(fListeners);
504 	int32 count = listeners.CountItems();
505 	for (int32 i = 0; i < count; i++) {
506 		ShapeListener* listener
507 			= (ShapeListener*)listeners.ItemAtFast(i);
508 		listener->StyleChanged(oldStyle, newStyle);
509 	}
510 	// TODO: merge Observable and ShapeListener interface
511 	_NotifyRerender();
512 }
513 
514 
515 void
516 Shape::_NotifyRerender() const
517 {
518 	fNeedsUpdate = true;
519 	Notify();
520 }
521 #endif // ICON_O_MATIC
522 
523 
524 void
525 Shape::SetHinting(bool hinting)
526 {
527 	if (fHinting == hinting)
528 		return;
529 
530 	fHinting = hinting;
531 	Notify();
532 }
533 
534