]> Shamusworld >> Repos - architektonas/blob - src/container.cpp
25079d972e9cb0fb3f6ba925a40a3282baa69af6
[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 What we need to do is check for whether or not we're a top level container,
174 and override the passing of stuff into the objects held. So here, if we're *NOT*
175 a top level container, instead of passing PointerMoved to our contained objects,
176 we check to see if our bounds are met (for selection rectangle, e.g.).
177
178 Also, for things like being able to split point's hot spots we need to be able
179 to check for that crap in the top level container. Which means that objects can
180 still know how to move themselves, but they can also defer to their container
181 as well. Which also means that things like HitTest() need to be in the Object
182 class so that we can leverage that stuff here as well.
183 */
184
185 // The TLC is passing all mouse movement here, so we're doing the same here.
186 // Need to adjust all other objects to handle things correctly.
187
188 // One optimization that will need to be done eventually is to subdivide the screen
189 // into parts and keep subdividing until an acceptable number of objects lie within
190 // the slice. This way, the GUI will still be responsive and *not* have to test
191 // every object for collision.
192 /*virtual*/ void Container::PointerMoved(Vector point)
193 {
194         if (!isTopLevelContainer)
195         {
196                 // check for selection rectangle too
197
198
199                 needUpdate = true;
200
201                 for(std::vector<Object *>::iterator i=objects.begin(); i!=objects.end(); i++)
202                 {
203                         if ((*i)->HitTest(point))
204                         {
205                                 state = OSSelected;
206                                 return;
207                         }
208                 }
209
210                 state = OSInactive;
211                 return;
212         }
213
214 //      objectWasDragged = true;
215 //printf("CONTAINER: PointerMoved()\n");
216
217         for(std::vector<Object *>::iterator i=objects.begin(); i!=objects.end(); i++)
218 //      for(int i=0; i<(int)objects.size(); i++)
219         {
220 ////            if (objects[i]->GetState() == OSSelected)
221 //                      objects[i]->PointerMoved(point);
222                 (*i)->PointerMoved(point);
223         }
224
225         // Generic container doesn't need this???
226 //      needUpdate = false;
227 }
228
229
230 /*virtual*/ void Container::PointerReleased(void)
231 {
232         dragging = false;
233         draggingHandle1 = false;
234         draggingHandle2 = false;
235
236         // Here we check for just a click: If object was clicked and dragged, then
237         // revert to the old state (OSInactive). Otherwise, keep the new state that
238         // we set.
239 /*Maybe it would be better to just check for "object was dragged" state and not have to worry
240 about keeping track of old states...
241 */
242         if (objectWasDragged)
243                 state = oldState;
244 //Note that the preceeding is unnecessary for a generic container!
245
246         for(int i=0; i<(int)objects.size(); i++)
247                 objects[i]->PointerReleased();
248 }
249
250
251 /*virtual*/ bool Container::NeedsUpdate(void)
252 {
253         // If this is *not* a top level container, then we treat it as an
254         // aggregate object.
255         if (!isTopLevelContainer)
256         {
257                 return needUpdate;
258         }
259
260         // Search through objects for one that needs an update; if one is found,
261         // return immediately.
262         for(std::vector<Object *>::iterator i=objects.begin(); i!=objects.end(); i++)
263         {
264                 if ((*i)->NeedsUpdate())
265                         return true;
266         }
267
268         return false;
269 }
270
271
272 /*virtual*/ void Container::Add(Object * object)
273 {
274         objects.push_back(object);
275 printf("Container: Added object (=$%X). size = %li\n", object, objects.size());
276 }
277
278
279 /*virtual*/ QRectF Container::Extents(void)
280 {
281         QRectF bounds;
282
283         for(std::vector<Object *>::iterator i=objects.begin(); i!=objects.end(); i++)
284                 bounds = bounds.united((*i)->Extents());
285
286         return bounds;
287 }
288
289
290 void Container::Delete(Object * objectToDelete)
291 {
292         std::vector<Object *>::iterator i = objects.begin();
293
294         while (i != objects.end())
295         {
296                 if (*i == objectToDelete)
297                 {
298                         objects.erase(i);
299                         delete objectToDelete;
300                         return;
301                 }
302
303                 i++;
304         }
305 }
306
307
308 void Container::DeleteSelectedItems(void)
309 {
310         std::vector<Object *>::iterator i = objects.begin();
311
312         while (i != objects.end())
313         {
314                 if ((*i)->state == OSSelected)
315                 {
316                         delete *i;
317                         objects.erase(i);
318                 }
319                 else
320                         i++;
321         }
322 }
323
324
325 void Container::Clear(void)
326 {
327         std::vector<Object *>::iterator i = objects.begin();
328
329         while (i != objects.end())
330         {
331 printf("Container: Deleting object ($%X)...\n", *i);
332                 delete (*i);
333                 objects.erase(i);
334         }
335 }
336
337
338 void Container::SelectAll(void)
339 {
340         for(unsigned int i=0; i<objects.size(); i++)
341                 objects[i]->state = OSSelected;
342 }
343
344
345 void Container::DeselectAll(void)
346 {
347         for(unsigned int i=0; i<objects.size(); i++)
348                 objects[i]->state = OSInactive;
349 }
350
351
352 int Container::ItemsSelected(void)
353 {
354         int selected = 0;
355
356         for(uint i=0; i<objects.size(); i++)
357                 if (objects[i]->state == OSSelected)
358                         selected++;
359
360         return selected;
361 }
362
363
364 Object * Container::SelectedItem(unsigned int index)
365 {
366         unsigned int selectedIndex = 0;
367
368         for(std::vector<Object *>::iterator i=objects.begin(); i!=objects.end(); i++)
369         {
370                 if ((*i)->state == OSSelected)
371                 {
372                         if (selectedIndex == index)
373                                 return *i;
374                         else
375                                 selectedIndex++;
376                 }
377         }
378
379         return NULL;
380 }
381
382
383 void Container::MoveContentsTo(Container * newContainer)
384 {
385         // Sanity check
386         if (newContainer == NULL)
387                 return;
388
389         // Shuffle the contents of this container to the new one
390 //      for(unsigned int i=0; i<objects.size(); i++)
391         for(std::vector<Object *>::iterator i=objects.begin(); i!=objects.end(); i++)
392         {
393                 newContainer->Add(*i);
394                 (*i)->Reparent(newContainer);
395         }
396
397         // & clear our vector
398         objects.clear();
399 }
400
401
402 void Container::MoveSelectedContentsTo(Container * newContainer)
403 {
404         // Sanity check
405         if (newContainer == NULL)
406                 return;
407
408         // Shuffle the contents of this container to the new one
409         for(std::vector<Object *>::iterator i=objects.begin(); i!=objects.end();)
410         {
411                 if ((*i)->state != OSSelected)
412                 {
413                         i++;
414                         continue;
415                 }
416
417                 newContainer->Add(*i);
418                 (*i)->Reparent(newContainer);
419                 objects.erase(i);
420         }
421 }
422
423
424 /*virtual*/ void Container::Enumerate(FILE * file)
425 {
426         // Only put "CONTAINER" markers if *not* the top level container
427         if (parent != NULL)
428                 fprintf(file, "CONTAINER\n");
429
430         for(uint i=0; i<objects.size(); i++)
431                 objects[i]->Enumerate(file);
432
433         if (parent != NULL)
434                 fprintf(file, "ENDCONTAINER\n");
435 }
436