2 // container.cpp: Container object
4 // Part of the Architektonas Project
5 // (C) 2011 Underground Software
6 // See the README and GPLv3 files for licensing and warranty information
8 // JLH = James Hammons <jlhamm@acm.org>
11 // --- ---------- ------------------------------------------------------------
12 // JLH 03/30/2011 Created this file
13 // JLH 06/02/2011 Added code to delete objects in this container when they go
17 #include "container.h"
20 #include "dimension.h"
23 Container::Container(Vector p1, Object * p/*= NULL*/): Object(p1, p),
24 isTopLevelContainer(false),
25 dragging(false), draggingHandle1(false), draggingHandle2(false),
26 hit(false)//, needUpdate(false)
33 Container::Container(const Container & copy): Object(copy.position, copy.parent)
35 // Use overloaded assignment operator
41 Container::~Container()
46 // Assignment operator
47 Container & Container::operator=(const Container & from)
49 // Take care of self-assignment
54 std::vector<Object *>::const_iterator i;
56 for(i=from.objects.begin(); i!=from.objects.end(); i++)
58 printf("Container: Copying object $%08X...\n", *i);
59 Object * object = (*i)->Copy();
60 objects.push_back(object);
66 /*virtual*/ void Container::Draw(Painter * painter)
71 for(std::vector<Object *>::iterator i=objects.begin(); i!=objects.end(); i++)
73 //printf("Containter::Draw item #%i [%X]...\n", a++, *i);
75 boundary = boundary.united((*i)->Extents());
78 if ((state == OSSelected) || hit)
81 painter->SetPen(QPen(Qt::magenta, 1.0, Qt::DashLine));
83 painter->SetPen(QPen(Qt::blue, 2.0, Qt::DashLine));
85 painter->SetBrush(QBrush(Qt::NoBrush));
86 painter->DrawPaddedRect(boundary);
90 /*virtual*/ Vector Container::Center(void)
96 We need at least *three* handles for this object:
101 We need to think about the intuitive way (if there is any) to grab and
102 manipulate a complex object like this... Need to think, "What should happen when
103 I click here and drag there?"
105 Also: should put the snap logic into the Object base class (as a static method)...
108 // Need to add checking here for clicking on a member of a group (Container),
109 // and checking for if it's a top level container (the DrawingView's document).
111 One approach is to check for the parent of the container: If it's NULL, then it's
112 the DrawingView's document. It might be better, though, to set a boolean like
113 isTopLevelContainer so that we can do things like edit members of a group without
114 having to ungroup them first (like Inkscape).
116 /*virtual*/ bool Container::Collided(Vector point)
118 objectWasDragged = false;
119 bool collision = false;
120 lastObjectClicked = NULL;
122 // NOTE that this deletes the object on mouse down instead of mouse up.
123 // Have to check to see how it feels to do it that way...
124 // N.B.: This only works because the toolAction is not set, &
125 // Object::ignoreClicks isn't set either...
128 for(std::vector<Object *>::iterator i=objects.begin(); i!=objects.end();)
130 if ((*i)->Collided(point))
132 printf("Container::Collided: Deleting object ($%X)\n", *i);
133 Object * objectToDelete = *i;
135 delete objectToDelete;
143 for(std::vector<Object *>::iterator i=objects.begin(); i!=objects.end(); i++)
145 if ((*i)->Collided(point))
148 lastObjectClicked = *i;
149 //printf("Container::Collided: lastObjectClicked = %X\n", lastObjectClicked);
155 point = SnapPointToGrid(point);
157 // We check to see if the container we're trying to access is the
158 // DrawingView's document. If so, we ignore the state of the container.
159 // Otherwise, we care about the state of the container. :-)
160 if (isTopLevelContainer)
164 state = (collision ? OSSelected : OSInactive);
166 if (state == OSSelected)
178 What we need to do is check for whether or not we're a top level container,
179 and override the passing of stuff into the objects held. So here, if we're *NOT*
180 a top level container, instead of passing PointerMoved to our contained objects,
181 we check to see if our bounds are met (for selection rectangle, e.g.).
183 Also, for things like being able to split point's hot spots we need to be able
184 to check for that crap in the top level container. Which means that objects can
185 still know how to move themselves, but they can also defer to their container
186 as well. Which also means that things like HitTest() need to be in the Object
187 class so that we can leverage that stuff here as well.
190 // The TLC is passing all mouse movement here, so we're doing the same here.
191 // Need to adjust all other objects to handle things correctly.
193 // One optimization that will need to be done eventually is to subdivide the screen
194 // into parts and keep subdividing until an acceptable number of objects lie within
195 // the slice. This way, the GUI will still be responsive and *not* have to test
196 // every object for collision.
197 /*virtual*/ bool Container::PointerMoved(Vector point)
199 std::vector<Object *>::iterator i;
200 lastObjectHovered = penultimateObjectHovered = NULL;
202 if (!isTopLevelContainer)
204 // check for selection rectangle too
205 if (selectionInProgress)
207 if (selection.contains(Extents()))
215 // No need to do any checking if we're already selected...
216 // if (state == OSSelected)
220 // needUpdate = true;
225 for(i=objects.begin(); i!=objects.end(); i++)
227 if ((*i)->HitTest(point))
229 // state = OSSelected;
236 needUpdate = (oldHit != hit ? true : false);
241 Vector delta = point - oldPoint;
243 for(i=objects.begin(); i!=objects.end(); i++)
244 (*i)->Translate(delta);
253 for(std::vector<Object *>::iterator i=objects.begin(); i!=objects.end(); i++)
255 // if (objects[i]->GetState() == OSSelected)
256 if ((*i)->PointerMoved(point))
258 penultimateObjectHovered = lastObjectHovered;
259 lastObjectHovered = *i;
263 // Generic container doesn't need this???
264 // needUpdate = false;
265 return (lastObjectHovered == NULL ? false : true);
268 /*virtual*/ void Container::PointerReleased(void)
270 if (!isTopLevelContainer)
277 draggingHandle1 = false;
278 draggingHandle2 = false;
280 // Here we check for just a click: If object was clicked and dragged, then
281 // revert to the old state (OSInactive). Otherwise, keep the new state that
284 Maybe it would be better to just check for "object was dragged" state and not
285 have to worry about keeping track of old states...
287 if (objectWasDragged)
289 //Note that the preceeding is unnecessary for a generic container!
292 // for(int i=0; i<(int)objects.size(); i++)
293 // objects[i]->PointerReleased();
294 for(std::vector<Object *>::iterator i=objects.begin(); i!=objects.end(); i++)
295 (*i)->PointerReleased();
298 /*virtual*/ bool Container::NeedsUpdate(void)
300 // If this is *not* a top level container, then we treat it as an
302 if (!isTopLevelContainer)
307 // Search through objects for one that needs an update; if one is found,
308 // return immediately.
309 for(std::vector<Object *>::iterator i=objects.begin(); i!=objects.end(); i++)
311 if ((*i)->NeedsUpdate())
318 /*virtual*/ void Container::Add(Object * object)
320 objects.push_back(object);
321 printf("Container: Added object (=$%X). size = %li\n", object, objects.size());
324 /*virtual*/ QRectF Container::Extents(void)
328 for(std::vector<Object *>::iterator i=objects.begin(); i!=objects.end(); i++)
329 bounds = bounds.united((*i)->Extents());
334 void Container::Delete(Object * objectToDelete)
336 std::vector<Object *>::iterator i = objects.begin();
338 while (i != objects.end())
340 if (*i == objectToDelete)
343 delete objectToDelete;
351 void Container::DeleteSelectedItems(void)
353 std::vector<Object *>::iterator i = objects.begin();
355 while (i != objects.end())
357 if ((*i)->state == OSSelected)
367 void Container::Clear(void)
369 std::vector<Object *>::iterator i = objects.begin();
371 while (i != objects.end())
373 printf("Container: Deleting object ($%X)...\n", *i);
379 void Container::SelectAll(void)
381 for(unsigned int i=0; i<objects.size(); i++)
382 objects[i]->state = OSSelected;
385 void Container::DeselectAll(void)
387 for(unsigned int i=0; i<objects.size(); i++)
388 objects[i]->state = OSInactive;
391 int Container::ItemsSelected(void)
395 for(uint i=0; i<objects.size(); i++)
396 if (objects[i]->state == OSSelected)
402 Object * Container::SelectedItem(unsigned int index)
404 unsigned int selectedIndex = 0;
406 for(std::vector<Object *>::iterator i=objects.begin(); i!=objects.end(); i++)
408 if ((*i)->state == OSSelected)
410 if (selectedIndex == index)
420 void Container::MoveContentsTo(Container * newContainer)
423 if (newContainer == NULL)
426 // Shuffle the contents of this container to the new one
427 for(std::vector<Object *>::iterator i=objects.begin(); i!=objects.end(); i++)
429 newContainer->Add(*i);
430 (*i)->Reparent(newContainer);
433 // & clear our vector
437 void Container::CopyContentsTo(Container * newContainer)
440 if (newContainer == NULL)
443 // Shuffle the contents of this container to the new one
444 for(std::vector<Object *>::iterator i=objects.begin(); i!=objects.end(); i++)
445 newContainer->Add((*i)->Copy());
448 void Container::MoveSelectedContentsTo(Container * newContainer)
451 if (newContainer == NULL)
454 // Shuffle the contents of this container to the new one
455 for(std::vector<Object *>::iterator i=objects.begin(); i!=objects.end();)
457 if ((*i)->state != OSSelected)
463 newContainer->Add(*i);
464 (*i)->Reparent(newContainer);
469 void Container::CopySelectedContentsTo(Container * newContainer)
472 if (newContainer == NULL)
475 // Copy the contents of this container to the new one
476 for(std::vector<Object *>::iterator i=objects.begin(); i!=objects.end(); i++)
478 if ((*i)->state == OSSelected)
479 newContainer->Add((*i)->Copy());
483 void Container::ResizeAllDimensions(double newSize)
485 for(std::vector<Object *>::iterator i=objects.begin(); i!=objects.end(); i++)
487 if ((*i)->type == OTDimension)
488 ((Dimension *)(*i))->size = newSize;
489 if ((*i)->type == OTContainer)
490 ((Container *)(*i))->ResizeAllDimensions(newSize);
494 /*virtual*/ void Container::Enumerate(FILE * file)
496 // Only put "CONTAINER" markers if *not* the top level container
497 // if (parent != NULL)
498 if (!isTopLevelContainer)
499 fprintf(file, "CONTAINER %i\n", layer);
501 for(uint i=0; i<objects.size(); i++)
502 objects[i]->Enumerate(file);
504 // if (parent != NULL)
505 if (!isTopLevelContainer)
506 fprintf(file, "ENDCONTAINER\n");
509 /*virtual*/ Object * Container::Copy(void)
511 #warning "!!! This doesn't take care of attached Dimensions !!!"
513 This is a real problem. While having a pointer in the Dimension to this line's points
514 is fast & easy, it creates a huge problem when trying to replicate an object like this.
516 Maybe a way to fix that then, is to have reference numbers instead of pointers. That
517 way, if you copy them, ... you might still have problems. Because you can't be sure if
518 a copy will be persistant or not, you then *definitely* do not want them to have the
519 same reference number.
521 Container * c = new Container(position, parent);
526 /*virtual*/ void Container::Rotate(Point point, double angle)
528 for(std::vector<Object *>::iterator i=objects.begin(); i!=objects.end(); i++)
529 (*i)->Rotate(point, angle);
532 /*virtual*/ void Container::RotateSelected(Point point, double angle)
534 for(std::vector<Object *>::iterator i=objects.begin(); i!=objects.end(); i++)
536 if ((*i)->state == OSSelected)
537 (*i)->Rotate(point, angle);
541 /*virtual*/ void Container::Mirror(Point p1, Point p2)
543 for(std::vector<Object *>::iterator i=objects.begin(); i!=objects.end(); i++)
544 (*i)->Mirror(p1, p2);
547 /*virtual*/ void Container::MirrorSelected(Point p1, Point p2)
549 for(std::vector<Object *>::iterator i=objects.begin(); i!=objects.end(); i++)
551 if ((*i)->state == OSSelected)
552 (*i)->Mirror(p1, p2);
556 /*virtual*/ void Container::Save(void)
560 for(std::vector<Object *>::iterator i=objects.begin(); i!=objects.end(); i++)
564 /*virtual*/ void Container::Restore(void)
568 for(std::vector<Object *>::iterator i=objects.begin(); i!=objects.end(); i++)