14 * |
14 * |
15 * You should have received a copy of the GNU General Public License |
15 * You should have received a copy of the GNU General Public License |
16 * along with this program. If not, see <http://www.gnu.org/licenses/>. |
16 * along with this program. If not, see <http://www.gnu.org/licenses/>. |
17 */ |
17 */ |
18 |
18 |
|
19 #include <cassert> |
19 #include "model.h" |
20 #include "model.h" |
20 #include "linetypes/modelobject.h" |
21 #include "linetypes/modelobject.h" |
21 #include "documentmanager.h" |
22 #include "documentmanager.h" |
22 #include "linetypes/comment.h" |
23 #include "linetypes/comment.h" |
23 #include "linetypes/conditionaledge.h" |
24 #include "linetypes/conditionaledge.h" |
28 |
29 |
29 Model::Model(DocumentManager* manager) : |
30 Model::Model(DocumentManager* manager) : |
30 QObject {manager}, |
31 QObject {manager}, |
31 _manager {manager} {} |
32 _manager {manager} {} |
32 |
33 |
33 Model::~Model() |
|
34 { |
|
35 for (int i = 0; i < countof(_objects); ++i) |
|
36 delete _objects[i]; |
|
37 } |
|
38 |
|
39 /* |
|
40 * Takes an existing object and migrates it to the end of this model. The object is removed from its old model in the process. |
|
41 */ |
|
42 void Model::addObject(LDObject *object) |
|
43 { |
|
44 insertObject(size(), object); |
|
45 } |
|
46 |
|
47 /* |
34 /* |
48 * Returns the amount of objects in this model. |
35 * Returns the amount of objects in this model. |
49 */ |
36 */ |
50 int Model::size() const |
37 int Model::size() const |
51 { |
38 { |
53 } |
40 } |
54 |
41 |
55 /* |
42 /* |
56 * Returns the vector of objects in this model. |
43 * Returns the vector of objects in this model. |
57 */ |
44 */ |
58 const QVector<LDObject*>& Model::objects() const |
45 const ResourceVector<LDObject>& Model::objects() const |
59 { |
46 { |
60 return _objects; |
47 return _objects; |
61 } |
48 } |
62 |
49 |
63 /* |
50 /* |
64 * Takes an existing object and migrates it to this model, at the specified position. |
51 * Returns the vector of objects in this model (private mutable access) |
65 */ |
52 */ |
66 void Model::insertObject(int position, LDObject* object) |
53 ResourceVector<LDObject>& Model::mutableObjects() |
67 { |
54 { |
68 if (object->model() and object->model() != this) |
55 return _objects; |
69 object->model()->withdraw(object); |
56 } |
70 |
57 |
71 // TODO: check that the object isn't in the vector once there's a cheap way to do so! |
58 /* |
72 _objects.insert(position, object); |
59 * Takes an existing object and adds it to this model, at the specified position. |
|
60 * The object is assumed to be ownerless. |
|
61 */ |
|
62 void Model::finalizeNewObject(int position, LDObject* object) |
|
63 { |
|
64 assert(object->model() == this); |
73 _needsTriangleRecount = true; |
65 _needsTriangleRecount = true; |
74 object->setDocument(this); |
66 object->setDocument(this); |
75 emit objectAdded(object); |
67 |
76 } |
68 // Set default color. Relying on virtual functions, this cannot be done in the c-tor. |
77 |
69 // TODO: store -1 as the default color |
78 /* |
70 if (object->isColored()) |
79 * Swaps one object with another, assuming they both are in this model. |
71 object->setColor(object->defaultColor()); |
|
72 |
|
73 emit objectAdded(object, position); |
|
74 } |
|
75 |
|
76 /* |
|
77 * Swaps one object with another, if they both are in this model. |
80 */ |
78 */ |
81 bool Model::swapObjects(LDObject* one, LDObject* other) |
79 bool Model::swapObjects(LDObject* one, LDObject* other) |
82 { |
80 { |
83 int a = _objects.indexOf(one); |
81 int a = _objects.indexOf(one); |
84 int b = _objects.indexOf(other); |
82 int b = _objects.indexOf(other); |
85 |
83 |
86 if (a != b and a != -1 and b != -1) |
84 if (a != b and a != -1 and b != -1) |
87 { |
85 { |
88 _objects[b] = one; |
86 _objects.swap(a, b); |
89 _objects[a] = other; |
87 emit objectsSwapped(one, other); |
90 return true; |
88 return true; |
91 } |
89 } |
92 else |
90 else |
93 { |
91 { |
94 return false; |
92 return false; |
95 } |
|
96 } |
|
97 |
|
98 /* |
|
99 * Assigns a new object to the specified position in the model. The object that already is in the position is deleted in the process. |
|
100 */ |
|
101 bool Model::setObjectAt(int idx, LDObject* obj) |
|
102 { |
|
103 if (idx < 0 or idx >= countof(_objects)) |
|
104 { |
|
105 return false; |
|
106 } |
|
107 else |
|
108 { |
|
109 removeAt(idx); |
|
110 insertObject(idx, obj); |
|
111 return true; |
|
112 } |
93 } |
113 } |
94 } |
114 |
95 |
115 /* |
96 /* |
116 * Returns the object at the specified position, or null if not found. |
97 * Returns the object at the specified position, or null if not found. |
136 /* |
117 /* |
137 * Removes the object at the provided position. |
118 * Removes the object at the provided position. |
138 */ |
119 */ |
139 void Model::removeAt(int position) |
120 void Model::removeAt(int position) |
140 { |
121 { |
141 LDObject* object = withdrawAt(position); |
122 emit aboutToRemoveObject(_objects[position], position); |
142 delete object; |
123 _objects.removeAt(position); |
143 } |
124 } |
144 |
125 |
145 /* |
126 /* |
146 * Replaces the given object with the contents of a model. |
127 * Replaces the given object with the contents of a model. |
147 */ |
128 */ |
148 void Model::replace(LDObject *object, Model &model) |
129 void Model::replace(LDObject *object, Model &model) |
149 { |
130 { |
150 if (object->model() == this) |
131 if (object->model() == this) |
151 { |
132 { |
152 int position = object->lineNumber(); |
133 int position = object->lineNumber(); |
153 |
134 merge(model, position); |
154 for (int i = countof(model.objects()) - 1; i >= 1; i -= 1) |
135 remove(object); |
155 insertObject(position + i, model.objects()[i]); |
|
156 |
|
157 setObjectAt(position, model.objects()[0]); |
|
158 } |
136 } |
159 } |
137 } |
160 |
138 |
161 /* |
139 /* |
162 * Signals the model to recount its triangles. |
140 * Signals the model to recount its triangles. |
184 return _triangleCount; |
162 return _triangleCount; |
185 } |
163 } |
186 |
164 |
187 /* |
165 /* |
188 * Merges the given model into this model, starting at the given position. The other model is emptied in the process. |
166 * Merges the given model into this model, starting at the given position. The other model is emptied in the process. |
189 */ |
167 * If select is true, the new objects are added to the selection. |
190 void Model::merge(Model& other, int position) |
168 */ |
|
169 void Model::merge(Model& other, int position, Filter filter, Callback callback) |
191 { |
170 { |
192 if (position < 0) |
171 if (position < 0) |
193 position = countof(_objects); |
172 position = countof(_objects); |
194 |
173 |
195 // Copy the vector of objects to copy, so that we don't change the vector while iterating over it. |
174 if (filter == nullptr) |
196 QVector<LDObject*> objectsCopy = other._objects; |
175 filter = [](LDObject*){return true;}; |
197 |
176 |
198 // Inform the contents of their new owner |
177 _objects.merge(other.mutableObjects(), position, [&](LDObject* object, int objectPosition) |
199 for (LDObject* object : objectsCopy) |
178 { |
200 { |
179 if (filter(object)) |
201 insertObject(position, object); |
180 { |
202 position += 1; |
181 emit other.aboutToRemoveObject(object, objectPosition); |
203 } |
182 return true; |
204 |
183 } |
205 other.clear(); |
184 else |
|
185 { |
|
186 return false; |
|
187 } |
|
188 }, [&](LDObject* object, int objectPosition) |
|
189 { |
|
190 _needsTriangleRecount = true; |
|
191 object->setDocument(this); |
|
192 emit objectAdded(object, objectPosition); |
|
193 |
|
194 if (callback) |
|
195 callback(object, position); |
|
196 }); |
206 } |
197 } |
207 |
198 |
208 /* |
199 /* |
209 * Returns the begin-iterator into this model, so that models can be used in foreach-loops. |
200 * Returns the begin-iterator into this model, so that models can be used in foreach-loops. |
210 */ |
201 */ |
211 QVector<LDObject*>::iterator Model::begin() |
202 LDObject* const* Model::begin() |
212 { |
203 { |
213 return _objects.begin(); |
204 return _objects.begin(); |
214 } |
205 } |
215 |
206 |
216 /* |
207 /* |
217 * Returns the end-iterator into this mode, so that models can be used in foreach-loops. |
208 * Returns the end-iterator into this mode, so that models can be used in foreach-loops. |
218 */ |
209 */ |
219 QVector<LDObject*>::iterator Model::end() |
210 LDObject* const* Model::end() |
220 { |
211 { |
221 return _objects.end(); |
212 return _objects.end(); |
222 } |
213 } |
223 |
214 |
224 /* |
215 /* |
229 for (int i = _objects.size() - 1; i >= 0; i -= 1) |
220 for (int i = _objects.size() - 1; i >= 0; i -= 1) |
230 removeAt(i); |
221 removeAt(i); |
231 |
222 |
232 _triangleCount = 0; |
223 _triangleCount = 0; |
233 _needsTriangleRecount = false; |
224 _needsTriangleRecount = false; |
234 } |
|
235 |
|
236 /* |
|
237 * Drops the object from the model. The object becomes a free object as a result (thus violating the invariant that every object |
|
238 * has a model!). The caller must immediately add the withdrawn object to another model. |
|
239 * |
|
240 * This private method is only used to implement public API. |
|
241 */ |
|
242 void Model::withdraw(LDObject* object) |
|
243 { |
|
244 if (object->model() == this) |
|
245 { |
|
246 int position = object->lineNumber(); |
|
247 |
|
248 if (_objects[position] == object) |
|
249 withdrawAt(position); |
|
250 } |
|
251 } |
|
252 |
|
253 /* |
|
254 * Drops an object from the model at the provided position. The caller must immediately put the result value object into a new model. |
|
255 */ |
|
256 LDObject* Model::withdrawAt(int position) |
|
257 { |
|
258 LDObject* object = _objects[position]; |
|
259 emit aboutToRemoveObject(object); |
|
260 _objects.removeAt(position); |
|
261 _needsTriangleRecount = true; |
|
262 return object; |
|
263 } |
225 } |
264 |
226 |
265 /* |
227 /* |
266 * Returns whether or not this model is empty. |
228 * Returns whether or not this model is empty. |
267 */ |
229 */ |