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),
26 hit(false)//, needUpdate(false)
34 Container::Container(const Container & copy): Object(copy.position, copy.parent)
36 // Use overloaded assignment operator
43 Container::~Container()
49 // Assignment operator
50 Container & Container::operator=(const Container & from)
52 // Take care of self-assignment
57 std::vector<Object *>::const_iterator i;
59 for(i=from.objects.begin(); i!=from.objects.end(); i++)
61 printf("Container: Copying object $%08X...\n", *i);
62 Object * object = (*i)->Copy();
63 objects.push_back(object);
70 /*virtual*/ void Container::Draw(Painter * painter)
75 for(std::vector<Object *>::iterator i=objects.begin(); i!=objects.end(); i++)
77 //printf("Containter::Draw item #%i [%X]...\n", a++, *i);
79 boundary = boundary.united((*i)->Extents());
82 if ((state == OSSelected) || hit)
85 painter->SetPen(QPen(Qt::magenta, 1.0, Qt::DashLine));
87 painter->SetPen(QPen(Qt::blue, 2.0, Qt::DashLine));
89 painter->SetBrush(QBrush(Qt::NoBrush));
90 painter->DrawPaddedRect(boundary);
95 /*virtual*/ Vector Container::Center(void)
102 We need at least *three* handles for this object:
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?"
111 Also: should put the snap logic into the Object base class (as a static method)...
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).
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).
123 /*virtual*/ bool Container::Collided(Vector point)
125 objectWasDragged = false;
126 bool collision = false;
127 lastObjectClicked = NULL;
129 // NOTE that this deletes the object on mouse down instead of mouse up.
130 // Have to check to see how it feels to do it that way...
131 // N.B.: This only works because the toolAction is not set, &
132 // Object::ignoreClicks isn't set either...
135 for(std::vector<Object *>::iterator i=objects.begin(); i!=objects.end();)
137 if ((*i)->Collided(point))
139 printf("Container::Collided: Deleting object ($%X)\n", *i);
140 Object * objectToDelete = *i;
142 delete objectToDelete;
150 for(std::vector<Object *>::iterator i=objects.begin(); i!=objects.end(); i++)
152 if ((*i)->Collided(point))
155 lastObjectClicked = *i;
156 //printf("Container::Collided: lastObjectClicked = %X\n", lastObjectClicked);
162 point = SnapPointToGrid(point);
164 // We check to see if the container we're trying to access is the
165 // DrawingView's document. If so, we ignore the state of the container.
166 // Otherwise, we care about the state of the container. :-)
167 if (isTopLevelContainer)
171 state = (collision ? OSSelected : OSInactive);
173 if (state == OSSelected)
185 What we need to do is check for whether or not we're a top level container,
186 and override the passing of stuff into the objects held. So here, if we're *NOT*
187 a top level container, instead of passing PointerMoved to our contained objects,
188 we check to see if our bounds are met (for selection rectangle, e.g.).
190 Also, for things like being able to split point's hot spots we need to be able
191 to check for that crap in the top level container. Which means that objects can
192 still know how to move themselves, but they can also defer to their container
193 as well. Which also means that things like HitTest() need to be in the Object
194 class so that we can leverage that stuff here as well.
197 // The TLC is passing all mouse movement here, so we're doing the same here.
198 // Need to adjust all other objects to handle things correctly.
200 // One optimization that will need to be done eventually is to subdivide the screen
201 // into parts and keep subdividing until an acceptable number of objects lie within
202 // the slice. This way, the GUI will still be responsive and *not* have to test
203 // every object for collision.
204 /*virtual*/ bool Container::PointerMoved(Vector point)
206 std::vector<Object *>::iterator i;
207 lastObjectHovered = penultimateObjectHovered = NULL;
209 if (!isTopLevelContainer)
211 // check for selection rectangle too
212 if (selectionInProgress)
214 if (selection.contains(Extents()))
222 // No need to do any checking if we're already selected...
223 // if (state == OSSelected)
227 // needUpdate = true;
232 for(i=objects.begin(); i!=objects.end(); i++)
234 if ((*i)->HitTest(point))
236 // state = OSSelected;
243 needUpdate = (oldHit != hit ? true : false);
248 Vector delta = point - oldPoint;
250 for(i=objects.begin(); i!=objects.end(); i++)
251 (*i)->Translate(delta);
260 for(std::vector<Object *>::iterator i=objects.begin(); i!=objects.end(); i++)
262 // if (objects[i]->GetState() == OSSelected)
263 if ((*i)->PointerMoved(point))
265 penultimateObjectHovered = lastObjectHovered;
266 lastObjectHovered = *i;
270 // Generic container doesn't need this???
271 // needUpdate = false;
272 return (lastObjectHovered == NULL ? false : true);
276 /*virtual*/ void Container::PointerReleased(void)
278 if (!isTopLevelContainer)
285 draggingHandle1 = false;
286 draggingHandle2 = false;
288 // Here we check for just a click: If object was clicked and dragged, then
289 // revert to the old state (OSInactive). Otherwise, keep the new state that
292 Maybe it would be better to just check for "object was dragged" state and not
293 have to worry about keeping track of old states...
295 if (objectWasDragged)
297 //Note that the preceeding is unnecessary for a generic container!
300 // for(int i=0; i<(int)objects.size(); i++)
301 // objects[i]->PointerReleased();
302 for(std::vector<Object *>::iterator i=objects.begin(); i!=objects.end(); i++)
303 (*i)->PointerReleased();
307 /*virtual*/ bool Container::NeedsUpdate(void)
309 // If this is *not* a top level container, then we treat it as an
311 if (!isTopLevelContainer)
316 // Search through objects for one that needs an update; if one is found,
317 // return immediately.
318 for(std::vector<Object *>::iterator i=objects.begin(); i!=objects.end(); i++)
320 if ((*i)->NeedsUpdate())
328 /*virtual*/ void Container::Add(Object * object)
330 objects.push_back(object);
331 printf("Container: Added object (=$%X). size = %li\n", object, objects.size());
335 /*virtual*/ QRectF Container::Extents(void)
339 for(std::vector<Object *>::iterator i=objects.begin(); i!=objects.end(); i++)
340 bounds = bounds.united((*i)->Extents());
346 void Container::Delete(Object * objectToDelete)
348 std::vector<Object *>::iterator i = objects.begin();
350 while (i != objects.end())
352 if (*i == objectToDelete)
355 delete objectToDelete;
364 void Container::DeleteSelectedItems(void)
366 std::vector<Object *>::iterator i = objects.begin();
368 while (i != objects.end())
370 if ((*i)->state == OSSelected)
381 void Container::Clear(void)
383 std::vector<Object *>::iterator i = objects.begin();
385 while (i != objects.end())
387 printf("Container: Deleting object ($%X)...\n", *i);
394 void Container::SelectAll(void)
396 for(unsigned int i=0; i<objects.size(); i++)
397 objects[i]->state = OSSelected;
401 void Container::DeselectAll(void)
403 for(unsigned int i=0; i<objects.size(); i++)
404 objects[i]->state = OSInactive;
408 int Container::ItemsSelected(void)
412 for(uint i=0; i<objects.size(); i++)
413 if (objects[i]->state == OSSelected)
420 Object * Container::SelectedItem(unsigned int index)
422 unsigned int selectedIndex = 0;
424 for(std::vector<Object *>::iterator i=objects.begin(); i!=objects.end(); i++)
426 if ((*i)->state == OSSelected)
428 if (selectedIndex == index)
439 void Container::MoveContentsTo(Container * newContainer)
442 if (newContainer == NULL)
445 // Shuffle the contents of this container to the new one
446 for(std::vector<Object *>::iterator i=objects.begin(); i!=objects.end(); i++)
448 newContainer->Add(*i);
449 (*i)->Reparent(newContainer);
452 // & clear our vector
457 void Container::CopyContentsTo(Container * newContainer)
460 if (newContainer == NULL)
463 // Shuffle the contents of this container to the new one
464 for(std::vector<Object *>::iterator i=objects.begin(); i!=objects.end(); i++)
465 newContainer->Add((*i)->Copy());
469 void Container::MoveSelectedContentsTo(Container * newContainer)
472 if (newContainer == NULL)
475 // Shuffle the contents of this container to the new one
476 for(std::vector<Object *>::iterator i=objects.begin(); i!=objects.end();)
478 if ((*i)->state != OSSelected)
484 newContainer->Add(*i);
485 (*i)->Reparent(newContainer);
491 void Container::CopySelectedContentsTo(Container * newContainer)
494 if (newContainer == NULL)
497 // Copy the contents of this container to the new one
498 for(std::vector<Object *>::iterator i=objects.begin(); i!=objects.end(); i++)
500 if ((*i)->state == OSSelected)
501 newContainer->Add((*i)->Copy());
506 void Container::ResizeAllDimensions(double newSize)
508 for(std::vector<Object *>::iterator i=objects.begin(); i!=objects.end(); i++)
510 if ((*i)->type == OTDimension)
511 ((Dimension *)(*i))->size = newSize;
512 if ((*i)->type == OTContainer)
513 ((Container *)(*i))->ResizeAllDimensions(newSize);
518 /*virtual*/ void Container::Enumerate(FILE * file)
520 // Only put "CONTAINER" markers if *not* the top level container
521 // if (parent != NULL)
522 if (!isTopLevelContainer)
523 fprintf(file, "CONTAINER %i\n", layer);
525 for(uint i=0; i<objects.size(); i++)
526 objects[i]->Enumerate(file);
528 // if (parent != NULL)
529 if (!isTopLevelContainer)
530 fprintf(file, "ENDCONTAINER\n");
534 /*virtual*/ Object * Container::Copy(void)
536 #warning "!!! This doesn't take care of attached Dimensions !!!"
538 This is a real problem. While having a pointer in the Dimension to this line's points
539 is fast & easy, it creates a huge problem when trying to replicate an object like this.
541 Maybe a way to fix that then, is to have reference numbers instead of pointers. That
542 way, if you copy them, ... you might still have problems. Because you can't be sure if
543 a copy will be persistant or not, you then *definitely* do not want them to have the
544 same reference number.
546 Container * c = new Container(position, parent);
552 /*virtual*/ void Container::Rotate(Point point, double angle)
554 for(std::vector<Object *>::iterator i=objects.begin(); i!=objects.end(); i++)
555 (*i)->Rotate(point, angle);
559 /*virtual*/ void Container::RotateSelected(Point point, double angle)
561 for(std::vector<Object *>::iterator i=objects.begin(); i!=objects.end(); i++)
563 if ((*i)->state == OSSelected)
564 (*i)->Rotate(point, angle);
569 /*virtual*/ void Container::Mirror(Point p1, Point p2)
571 for(std::vector<Object *>::iterator i=objects.begin(); i!=objects.end(); i++)
572 (*i)->Mirror(p1, p2);
576 /*virtual*/ void Container::MirrorSelected(Point p1, Point p2)
578 for(std::vector<Object *>::iterator i=objects.begin(); i!=objects.end(); i++)
580 if ((*i)->state == OSSelected)
581 (*i)->Mirror(p1, p2);
586 /*virtual*/ void Container::Save(void)
590 for(std::vector<Object *>::iterator i=objects.begin(); i!=objects.end(); i++)
595 /*virtual*/ void Container::Restore(void)
599 for(std::vector<Object *>::iterator i=objects.begin(); i!=objects.end(); i++)