]> Shamusworld >> Repos - architektonas/blob - src/container.cpp
bd37b7738610a1e4c54586699b2932cf57cec417
[architektonas] / src / container.cpp
1 // container.cpp: Container object
2 //
3 // Part of the Architektonas Project
4 // (C) 2011 Underground Software
5 // See the README and GPLv3 files for licensing and warranty information
6 //
7 // JLH = James Hammons <jlhamm@acm.org>
8 //
9 // WHO  WHEN        WHAT
10 // ---  ----------  ------------------------------------------------------------
11 // JLH  03/30/2011  Created this file
12 // JLH  06/02/2011  Added code to delete objects in this container when they go
13 //                  out of scope
14 //
15
16 #include "container.h"
17
18 #include <QtGui>
19 #include "dimension.h"
20 #include "painter.h"
21
22
23 Container::Container(Vector p1, Object * p/*= NULL*/): Object(p1, p),
24         isTopLevelContainer(false),
25         dragging(false), draggingHandle1(false), draggingHandle2(false)//, needUpdate(false)
26 {
27         type = OTContainer;
28 }
29
30
31 // Copy constructor
32 Container::Container(const Container & copy): Object(copy.position, copy.parent)
33 {
34         // Use overloaded assignment operator
35         *this = copy;
36         type = OTContainer;
37 }
38
39
40 Container::~Container()
41 {
42         Clear();
43 }
44
45
46 // Assignment operator
47 Container & Container::operator=(const Container & from)
48 {
49         // Take care of self-assignment
50         if (this == &from)
51                 return *this;
52
53         Clear();
54
55         // Small problem with this approach: if the copied object goes out of scope,
56         // all of the objects we copied in here will be deleted. D'oh!
57         for(uint i=0; i<from.objects.size(); i++)
58         {
59                 Object * object = from.objects[i];
60
61                 // Need to copy the object here...
62
63                 objects.push_back(object);
64         }
65
66         return *this;
67 }
68
69
70 /*virtual*/ void Container::Draw(Painter * painter)
71 {
72         QRectF boundary;
73
74         for(std::vector<Object *>::iterator i=objects.begin(); i!=objects.end(); i++)
75 //      for(int i=0; i<(int)objects.size(); i++)
76         {
77 #if 0
78 //printf("Container: About to draw (object = $%X)\n", objects[i]);
79                 objects[i]->Draw(painter);
80                 bounds = bounds.united(objects[i].RectangularExtents());
81 #else
82                 (*i)->Draw(painter);
83                 boundary = boundary.united((*i)->Extents());
84 #endif
85         }
86
87         if (state == OSSelected)
88         {
89                 painter->SetPen(QPen(Qt::magenta, 2.0, Qt::DashLine));
90                 painter->SetBrush(QBrush(Qt::NoBrush));
91                 painter->DrawRect(boundary);
92         }
93 }
94
95
96 /*virtual*/ Vector Container::Center(void)
97 {
98         return position;
99 }
100
101 /*
102  We need at least *three* handles for this object:
103  - one for moving
104  - one for resizing
105  - one for rotation
106
107 We need to think about the intuitive way (if there is any) to grab and
108 manipulate a complex object like this... Need to think, "What should happen when
109 I click here and drag there?"
110
111 Also: should put the snap logic into the Object base class (as a static method)...
112 */
113
114
115 // Need to add checking here for clicking on a member of a group (Container),
116 // and checking for if it's a top level container (the DrawingView's document).
117 /*
118 One approach is to check for the parent of the container: If it's NULL, then it's
119 the DrawingView's document. It might be better, though, to set a boolean like
120 isTopLevelContainer so that we can do things like edit members of a group without
121 having to ungroup them first (like Inkscape).
122 */
123 /*virtual*/ bool Container::Collided(Vector point)
124 {
125         objectWasDragged = false;
126 //      Vector v1 = position - point;
127
128         bool collision = false;
129
130         // NOTE that this deletes the object on mouse down instead of mouse up. Have to
131         // check to see how it feels to do it that way...
132         if (deleteActive)
133         {
134                 for(std::vector<Object *>::iterator i=objects.begin(); i!=objects.end();)
135                 {
136                         if ((*i)->Collided(point))
137                         {
138 printf("Container::Collided: Deleting object ($%X)\n", *i);
139                                 Object * objectToDelete = *i;
140                                 objects.erase(i);
141                                 delete objectToDelete;
142                         }
143                         else
144                                 i++;
145                 }
146         }
147         else
148         {
149                 for(std::vector<Object *>::iterator i=objects.begin(); i!=objects.end(); i++)
150                 {
151                         if ((*i)->Collided(point))
152                                 collision = true;
153                 }
154         }
155
156         // We check to see if the container we're trying to access is the
157         // DrawingView's document. If so, we ignore the state of the container.
158         // Otherwise, we care about the state of the container. :-)
159         if (isTopLevelContainer)
160                 state = OSInactive;
161         else
162         {
163                 state = (collision ? OSSelected : OSInactive);
164
165                 if (state == OSSelected)
166                         DeselectAll();
167         }
168
169         return collision;
170 }
171
172
173 // The TLC is passing all mouse movement here, so we're doing the same here.
174 // Need to adjust all other objects to handle things correctly.
175
176 // One optimization that will need to be done eventually is to subdivide the screen
177 // into parts and keep subdividing until an acceptable number of objects lie within
178 // the slice. This way, the GUI will still be responsive and *not* have to test
179 // every object for collision.
180 /*virtual*/ void Container::PointerMoved(Vector point)
181 {
182 //      objectWasDragged = true;
183 //printf("CONTAINER: PointerMoved()\n");
184
185         for(std::vector<Object *>::iterator i=objects.begin(); i!=objects.end(); i++)
186 //      for(int i=0; i<(int)objects.size(); i++)
187         {
188 ////            if (objects[i]->GetState() == OSSelected)
189 //                      objects[i]->PointerMoved(point);
190                 (*i)->PointerMoved(point);
191         }
192
193         // Generic container doesn't need this???
194 //      needUpdate = false;
195 }
196
197
198 /*virtual*/ void Container::PointerReleased(void)
199 {
200         dragging = false;
201         draggingHandle1 = false;
202         draggingHandle2 = false;
203
204         // Here we check for just a click: If object was clicked and dragged, then
205         // revert to the old state (OSInactive). Otherwise, keep the new state that
206         // we set.
207 /*Maybe it would be better to just check for "object was dragged" state and not have to worry
208 about keeping track of old states...
209 */
210         if (objectWasDragged)
211                 state = oldState;
212 //Note that the preceeding is unnecessary for a generic container!
213
214         for(int i=0; i<(int)objects.size(); i++)
215                 objects[i]->PointerReleased();
216 }
217
218
219 /*virtual*/ bool Container::NeedsUpdate(void)
220 {
221         // Search through objects for one that needs an update; if one is found,
222         // return immediately.
223         for(std::vector<Object *>::iterator i=objects.begin(); i!=objects.end(); i++)
224         {
225                 if ((*i)->NeedsUpdate())
226                         return true;
227         }
228
229         return false;
230 }
231
232
233 /*virtual*/ void Container::Add(Object * object)
234 {
235         objects.push_back(object);
236 printf("Container: Added object (=$%X). size = %li\n", object, objects.size());
237 }
238
239
240 void Container::Delete(Object * objectToDelete)
241 {
242         std::vector<Object *>::iterator i = objects.begin();
243
244         while (i != objects.end())
245         {
246                 if (*i == objectToDelete)
247                 {
248                         objects.erase(i);
249                         delete objectToDelete;
250                         return;
251                 }
252
253                 i++;
254         }
255 }
256
257
258 void Container::DeleteSelectedItems(void)
259 {
260         std::vector<Object *>::iterator i = objects.begin();
261
262         while (i != objects.end())
263         {
264                 if ((*i)->state == OSSelected)
265                 {
266                         delete *i;
267                         objects.erase(i);
268                 }
269                 else
270                         i++;
271         }
272 }
273
274
275 void Container::Clear(void)
276 {
277         std::vector<Object *>::iterator i = objects.begin();
278
279         while (i != objects.end())
280         {
281 printf("Container: Deleting object ($%X)...\n", *i);
282                 delete (*i);
283                 objects.erase(i);
284         }
285 }
286
287
288 void Container::SelectAll(void)
289 {
290         for(unsigned int i=0; i<objects.size(); i++)
291                 objects[i]->state = OSSelected;
292 }
293
294
295 void Container::DeselectAll(void)
296 {
297         for(unsigned int i=0; i<objects.size(); i++)
298                 objects[i]->state = OSInactive;
299 }
300
301
302 int Container::ItemsSelected(void)
303 {
304         int selected = 0;
305
306         for(uint i=0; i<objects.size(); i++)
307                 if (objects[i]->state == OSSelected)
308                         selected++;
309
310         return selected;
311 }
312
313
314 Object * Container::SelectedItem(unsigned int index)
315 {
316         unsigned int selectedIndex = 0;
317
318         for(std::vector<Object *>::iterator i=objects.begin(); i!=objects.end(); i++)
319         {
320                 if ((*i)->state == OSSelected)
321                 {
322                         if (selectedIndex == index)
323                                 return *i;
324                         else
325                                 selectedIndex++;
326                 }
327         }
328
329         return NULL;
330 }
331
332
333 void Container::MoveContentsTo(Container * newContainer)
334 {
335         // Sanity check
336         if (newContainer == NULL)
337                 return;
338
339         // Shuffle the contents of this container to the new one
340         for(unsigned int i=0; i<objects.size(); i++)
341                 newContainer->Add(objects[i]);
342
343         // & clear our vector
344         objects.clear();
345 }
346
347
348 void Container::MoveSelectedContentsTo(Container * newContainer)
349 {
350         // Sanity check
351         if (newContainer == NULL)
352                 return;
353
354         // Shuffle the contents of this container to the new one
355         for(std::vector<Object *>::iterator i=objects.begin(); i!=objects.end();)
356         {
357                 if ((*i)->state != OSSelected)
358                 {
359                         i++;
360                         continue;
361                 }
362
363                 newContainer->Add(*i);
364                 objects.erase(i);
365         }
366 }
367
368
369 /*virtual*/ void Container::Enumerate(FILE * file)
370 {
371         // Only put "CONTAINER" markers if *not* the top level container
372         if (parent != NULL)
373                 fprintf(file, "CONTAINER\n");
374
375         for(uint i=0; i<objects.size(); i++)
376                 objects[i]->Enumerate(file);
377
378         if (parent != NULL)
379                 fprintf(file, "ENDCONTAINER\n");
380 }
381