xref: /haiku/src/libs/icon/Icon.cpp (revision 445d4fd926c569e7b9ae28017da86280aaecbae2)
1 /*
2  * Copyright 2006, 2023, Haiku.
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 "Icon.h"
11 
12 #include <new>
13 #include <stdio.h>
14 
15 #include "PathContainer.h"
16 #include "PathSourceShape.h"
17 #include "ReferenceImage.h"
18 #include "Shape.h"
19 #include "Style.h"
20 #include "StyleContainer.h"
21 
22 using std::nothrow;
23 
24 #ifdef ICON_O_MATIC
25 IconListener::IconListener() {}
26 IconListener::~IconListener() {}
27 #endif
28 
29 // #pragma mark -
30 
31 // constructor
32 Icon::Icon()
33 	: fStyles(new (nothrow) StyleContainer()),
34 	  fPaths(new (nothrow) PathContainer(true)),
35 	  fShapes(new (nothrow) ShapeContainer())
36 #ifdef ICON_O_MATIC
37 	, fListeners(2)
38 #endif
39 {
40 #ifdef ICON_O_MATIC
41 	if (fShapes)
42 		fShapes->AddListener(this);
43 #endif
44 }
45 
46 // constructor
47 Icon::Icon(const Icon& other)
48 	: fStyles(new (nothrow) StyleContainer()),
49 	  fPaths(new (nothrow) PathContainer(true)),
50 	  fShapes(new (nothrow) ShapeContainer())
51 #ifdef ICON_O_MATIC
52 	, fListeners(2)
53 #endif
54 {
55 	if (!fStyles || !fPaths || !fShapes)
56 		return;
57 
58 #ifdef ICON_O_MATIC
59 	fShapes->AddListener(this);
60 #endif
61 
62 	int32 styleCount = other.fStyles->CountStyles();
63 	for (int32 i = 0; i < styleCount; i++) {
64 		Style* style = other.fStyles->StyleAtFast(i);
65 		Style* clone = new (nothrow) Style(*style);
66 		if (!clone || !fStyles->AddStyle(clone)) {
67 			delete clone;
68 			return;
69 		}
70 	}
71 
72 	int32 pathCount = other.fPaths->CountPaths();
73 	for (int32 i = 0; i < pathCount; i++) {
74 		VectorPath* path = other.fPaths->PathAtFast(i);
75 		VectorPath* clone = new (nothrow) VectorPath(*path);
76 		if (!clone || !fPaths->AddPath(clone)) {
77 			delete clone;
78 			return;
79 		}
80 	}
81 
82 	int32 shapeCount = other.fShapes->CountShapes();
83 	for (int32 i = 0; i < shapeCount; i++) {
84 		Shape* shape = other.fShapes->ShapeAtFast(i);
85 		Shape* clone = shape->Clone();
86 		if (!clone || !fShapes->AddShape(clone)) {
87 			delete clone;
88 			return;
89 		}
90 
91 		// PathSourceShapes require further handling
92 		PathSourceShape* pathSourceShape = dynamic_cast<PathSourceShape*>(shape);
93 		PathSourceShape* pathSourceShapeClone = dynamic_cast<PathSourceShape*>(clone);
94 		if (pathSourceShape != NULL && pathSourceShapeClone != NULL) {
95 			// the cloned shape references styles and paths in
96 			// the "other" icon, replace them with "local" styles
97 			// and paths
98 
99 			int32 styleIndex = other.fStyles->IndexOf(pathSourceShape->Style());
100 			pathSourceShapeClone->SetStyle(fStyles->StyleAt(styleIndex));
101 
102 			pathSourceShapeClone->Paths()->MakeEmpty();
103 			pathCount = pathSourceShape->Paths()->CountPaths();
104 			for (int32 j = 0; j < pathCount; j++) {
105 				VectorPath* remote = pathSourceShape->Paths()->PathAtFast(j);
106 				int32 index = other.fPaths->IndexOf(remote);
107 				VectorPath* local = fPaths->PathAt(index);
108 				if (!local) {
109 					printf("failed to match remote and "
110 						   "local paths while cloning icon\n");
111 					continue;
112 				}
113 				if (!pathSourceShapeClone->Paths()->AddPath(local)) {
114 					return;
115 				}
116 			}
117 		}
118 	}
119 }
120 
121 // destructor
122 Icon::~Icon()
123 {
124 	if (fShapes) {
125 		fShapes->MakeEmpty();
126 #ifdef ICON_O_MATIC
127 		fShapes->RemoveListener(this);
128 #endif
129 		delete fShapes;
130 	}
131 	delete fPaths;
132 	delete fStyles;
133 }
134 
135 // InitCheck
136 status_t
137 Icon::InitCheck() const
138 {
139 	return fStyles && fPaths && fShapes ? B_OK : B_NO_MEMORY;
140 }
141 
142 #ifdef ICON_O_MATIC
143 // ShapeAdded
144 void
145 Icon::ShapeAdded(Shape* shape, int32 index)
146 {
147 	shape->AddObserver(this);
148 	_NotifyAreaInvalidated(shape->Bounds(true));
149 }
150 
151 // ShapeRemoved
152 void
153 Icon::ShapeRemoved(Shape* shape)
154 {
155 	shape->RemoveObserver(this);
156 	_NotifyAreaInvalidated(shape->Bounds(true));
157 }
158 
159 // ObjectChanged
160 void
161 Icon::ObjectChanged(const Observable* object)
162 {
163 	const Shape* shape = dynamic_cast<const Shape*>(object);
164 	if (shape) {
165 		BRect area = shape->LastBounds();
166 		area = area | shape->Bounds(true);
167 		area.InsetBy(-1, -1);
168 		_NotifyAreaInvalidated(area);
169 	}
170 }
171 
172 // AddListener
173 bool
174 Icon::AddListener(IconListener* listener)
175 {
176 	if (listener && !fListeners.HasItem((void*)listener)) {
177 		if (fListeners.AddItem((void*)listener)) {
178 			listener->AreaInvalidated(BRect(0, 0, 63, 63));
179 			return true;
180 		}
181 	}
182 	return false;
183 }
184 
185 // RemoveListener
186 bool
187 Icon::RemoveListener(IconListener* listener)
188 {
189 	return fListeners.RemoveItem((void*)listener);
190 }
191 #endif // ICON_O_MATIC
192 
193 
194 // Clone
195 Icon*
196 Icon::Clone() const
197 {
198 	return new (nothrow) Icon(*this);
199 }
200 
201 // MakeEmpty
202 void
203 Icon::MakeEmpty()
204 {
205 	fShapes->MakeEmpty();
206 	fPaths->MakeEmpty();
207 	fStyles->MakeEmpty();
208 }
209 
210 // #pragma mark -
211 
212 #ifdef ICON_O_MATIC
213 // _NotifyAreaInvalidated
214 void
215 Icon::_NotifyAreaInvalidated(const BRect& area) const
216 {
217 	BList listeners(fListeners);
218 	int32 count = listeners.CountItems();
219 	for (int32 i = 0; i < count; i++) {
220 		IconListener* listener
221 			= (IconListener*)listeners.ItemAtFast(i);
222 		listener->AreaInvalidated(area);
223 	}
224 }
225 #endif // ICON_O_MATIC
226 
227 
228