1 // container.cpp: Container object
3 // Part of the Architektonas Project
4 // (C) 2011 Underground Software
5 // See the README and GPLv3 files for licensing and warranty information
7 // JLH = James Hammons <jlhamm@acm.org>
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
16 #include "container.h"
19 #include "dimension.h"
23 Container::Container(Vector p1, Object * p/*= NULL*/): Object(p1, p),
24 isTopLevelContainer(false),
25 dragging(false), draggingHandle1(false), draggingHandle2(false)//, needUpdate(false)
33 Container::Container(const Container & copy): Object(copy.position, copy.parent)
35 // Use overloaded assignment operator
42 Container::~Container()
48 // Assignment operator
49 Container & Container::operator=(const Container & from)
51 // Take care of self-assignment
57 // Small problem with this approach: if the copied object goes out of scope,
58 // all of the objects we copied in here will be deleted. D'oh!
59 // For this COPY constructor to be meaningful, we have to actually COPY the
60 // objects in this Container, not just MOVE a copy of the POINTER! D-:
61 std::vector<Object *>::const_iterator i;
63 // for(uint i=0; i<from.objects.size(); i++)
64 for(i=from.objects.begin(); i!=from.objects.end(); i++)
66 // Object * object = from.objects[i];
67 Object * object = (*i)->Copy();
69 // Need to actually COPY the object here, not copy the pointer only!!
70 // (which we do now, above :-P)
72 objects.push_back(object);
79 /*virtual*/ void Container::Draw(Painter * painter)
84 for(std::vector<Object *>::iterator i=objects.begin(); i!=objects.end(); i++)
86 //printf("Containter::Draw item #%i [%X]...\n", a++, *i);
88 boundary = boundary.united((*i)->Extents());
91 if ((state == OSSelected) || hit)
94 painter->SetPen(QPen(Qt::magenta, 1.0, Qt::DashLine));
96 painter->SetPen(QPen(Qt::blue, 2.0, Qt::DashLine));
98 painter->SetBrush(QBrush(Qt::NoBrush));
99 painter->DrawPaddedRect(boundary);
104 /*virtual*/ Vector Container::Center(void)
111 We need at least *three* handles for this object:
116 We need to think about the intuitive way (if there is any) to grab and
117 manipulate a complex object like this... Need to think, "What should happen when
118 I click here and drag there?"
120 Also: should put the snap logic into the Object base class (as a static method)...
124 // Need to add checking here for clicking on a member of a group (Container),
125 // and checking for if it's a top level container (the DrawingView's document).
127 One approach is to check for the parent of the container: If it's NULL, then it's
128 the DrawingView's document. It might be better, though, to set a boolean like
129 isTopLevelContainer so that we can do things like edit members of a group without
130 having to ungroup them first (like Inkscape).
132 /*virtual*/ bool Container::Collided(Vector point)
134 objectWasDragged = false;
135 // Vector v1 = position - point;
137 bool collision = false;
139 // NOTE that this deletes the object on mouse down instead of mouse up. Have to
140 // check to see how it feels to do it that way...
143 for(std::vector<Object *>::iterator i=objects.begin(); i!=objects.end();)
145 if ((*i)->Collided(point))
147 printf("Container::Collided: Deleting object ($%X)\n", *i);
148 Object * objectToDelete = *i;
150 delete objectToDelete;
158 for(std::vector<Object *>::iterator i=objects.begin(); i!=objects.end(); i++)
160 if ((*i)->Collided(point))
166 point = SnapPointToGrid(point);
168 // We check to see if the container we're trying to access is the
169 // DrawingView's document. If so, we ignore the state of the container.
170 // Otherwise, we care about the state of the container. :-)
171 if (isTopLevelContainer)
175 state = (collision ? OSSelected : OSInactive);
177 if (state == OSSelected)
189 What we need to do is check for whether or not we're a top level container,
190 and override the passing of stuff into the objects held. So here, if we're *NOT*
191 a top level container, instead of passing PointerMoved to our contained objects,
192 we check to see if our bounds are met (for selection rectangle, e.g.).
194 Also, for things like being able to split point's hot spots we need to be able
195 to check for that crap in the top level container. Which means that objects can
196 still know how to move themselves, but they can also defer to their container
197 as well. Which also means that things like HitTest() need to be in the Object
198 class so that we can leverage that stuff here as well.
201 // The TLC is passing all mouse movement here, so we're doing the same here.
202 // Need to adjust all other objects to handle things correctly.
204 // One optimization that will need to be done eventually is to subdivide the screen
205 // into parts and keep subdividing until an acceptable number of objects lie within
206 // the slice. This way, the GUI will still be responsive and *not* have to test
207 // every object for collision.
208 /*virtual*/ void Container::PointerMoved(Vector point)
210 std::vector<Object *>::iterator i;
212 if (!isTopLevelContainer)
214 // check for selection rectangle too
215 if (selectionInProgress)
217 if (selection.contains(Extents()))
225 // No need to do any checking if we're already selected...
226 // if (state == OSSelected)
230 // needUpdate = true;
235 for(i=objects.begin(); i!=objects.end(); i++)
237 if ((*i)->HitTest(point))
239 // state = OSSelected;
246 needUpdate = (oldHit != hit ? true : false);
251 Vector delta = point - oldPoint;
253 for(i=objects.begin(); i!=objects.end(); i++)
254 (*i)->Translate(delta);
263 for(std::vector<Object *>::iterator i=objects.begin(); i!=objects.end(); i++)
265 // if (objects[i]->GetState() == OSSelected)
266 (*i)->PointerMoved(point);
269 // Generic container doesn't need this???
270 // needUpdate = false;
274 /*virtual*/ void Container::PointerReleased(void)
276 if (!isTopLevelContainer)
283 draggingHandle1 = false;
284 draggingHandle2 = false;
286 // Here we check for just a click: If object was clicked and dragged, then
287 // revert to the old state (OSInactive). Otherwise, keep the new state that
290 Maybe it would be better to just check for "object was dragged" state and not
291 have to worry about keeping track of old states...
293 if (objectWasDragged)
295 //Note that the preceeding is unnecessary for a generic container!
298 // for(int i=0; i<(int)objects.size(); i++)
299 // objects[i]->PointerReleased();
300 for(std::vector<Object *>::iterator i=objects.begin(); i!=objects.end(); i++)
301 (*i)->PointerReleased();
305 /*virtual*/ bool Container::NeedsUpdate(void)
307 // If this is *not* a top level container, then we treat it as an
309 if (!isTopLevelContainer)
314 // Search through objects for one that needs an update; if one is found,
315 // return immediately.
316 for(std::vector<Object *>::iterator i=objects.begin(); i!=objects.end(); i++)
318 if ((*i)->NeedsUpdate())
326 /*virtual*/ void Container::Add(Object * object)
328 objects.push_back(object);
329 printf("Container: Added object (=$%X). size = %li\n", object, objects.size());
333 /*virtual*/ QRectF Container::Extents(void)
337 for(std::vector<Object *>::iterator i=objects.begin(); i!=objects.end(); i++)
338 bounds = bounds.united((*i)->Extents());
344 void Container::Delete(Object * objectToDelete)
346 std::vector<Object *>::iterator i = objects.begin();
348 while (i != objects.end())
350 if (*i == objectToDelete)
353 delete objectToDelete;
362 void Container::DeleteSelectedItems(void)
364 std::vector<Object *>::iterator i = objects.begin();
366 while (i != objects.end())
368 if ((*i)->state == OSSelected)
379 void Container::Clear(void)
381 std::vector<Object *>::iterator i = objects.begin();
383 while (i != objects.end())
385 printf("Container: Deleting object ($%X)...\n", *i);
392 void Container::SelectAll(void)
394 for(unsigned int i=0; i<objects.size(); i++)
395 objects[i]->state = OSSelected;
399 void Container::DeselectAll(void)
401 for(unsigned int i=0; i<objects.size(); i++)
402 objects[i]->state = OSInactive;
406 int Container::ItemsSelected(void)
410 for(uint i=0; i<objects.size(); i++)
411 if (objects[i]->state == OSSelected)
418 Object * Container::SelectedItem(unsigned int index)
420 unsigned int selectedIndex = 0;
422 for(std::vector<Object *>::iterator i=objects.begin(); i!=objects.end(); i++)
424 if ((*i)->state == OSSelected)
426 if (selectedIndex == index)
437 void Container::MoveContentsTo(Container * newContainer)
440 if (newContainer == NULL)
443 // Shuffle the contents of this container to the new one
444 // for(unsigned int i=0; i<objects.size(); i++)
445 for(std::vector<Object *>::iterator i=objects.begin(); i!=objects.end(); i++)
447 newContainer->Add(*i);
448 (*i)->Reparent(newContainer);
451 // & clear our vector
456 void Container::MoveSelectedContentsTo(Container * newContainer)
459 if (newContainer == NULL)
462 // Shuffle the contents of this container to the new one
463 for(std::vector<Object *>::iterator i=objects.begin(); i!=objects.end();)
465 if ((*i)->state != OSSelected)
471 newContainer->Add(*i);
472 (*i)->Reparent(newContainer);
478 void Container::ResizeAllDimensions(double newSize)
480 for(std::vector<Object *>::iterator i=objects.begin(); i!=objects.end(); i++)
482 if ((*i)->type == OTDimension)
483 ((Dimension *)(*i))->size = newSize;
484 if ((*i)->type == OTContainer)
485 ((Container *)(*i))->ResizeAllDimensions(newSize);
490 /*virtual*/ void Container::Enumerate(FILE * file)
492 // Only put "CONTAINER" markers if *not* the top level container
493 // if (parent != NULL)
494 if (!isTopLevelContainer)
495 fprintf(file, "CONTAINER\n");
497 for(uint i=0; i<objects.size(); i++)
498 objects[i]->Enumerate(file);
500 // if (parent != NULL)
501 if (!isTopLevelContainer)
502 fprintf(file, "ENDCONTAINER\n");
506 /*virtual*/ Object * Container::Copy(void)
508 #warning "!!! This doesn't take care of attached Dimensions !!!"
510 This is a real problem. While having a pointer in the Dimension to this line's points
511 is fast & easy, it creates a huge problem when trying to replicate an object like this.
513 Maybe a way to fix that then, is to have reference numbers instead of pointers. That
514 way, if you copy them, ... you might still have problems. Because you can't be sure if
515 a copy will be persistant or not, you then *definitely* do not want them to have the
516 same reference number.
518 Container * c = new Container(position, parent);