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 // Vector v1 = position - point;
128 bool collision = false;
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...
134 for(std::vector<Object *>::iterator i=objects.begin(); i!=objects.end();)
136 if ((*i)->Collided(point))
138 printf("Container::Collided: Deleting object ($%X)\n", *i);
139 Object * objectToDelete = *i;
141 delete objectToDelete;
149 for(std::vector<Object *>::iterator i=objects.begin(); i!=objects.end(); i++)
151 if ((*i)->Collided(point))
157 point = SnapPointToGrid(point);
159 // We check to see if the container we're trying to access is the
160 // DrawingView's document. If so, we ignore the state of the container.
161 // Otherwise, we care about the state of the container. :-)
162 if (isTopLevelContainer)
166 state = (collision ? OSSelected : OSInactive);
168 if (state == OSSelected)
180 What we need to do is check for whether or not we're a top level container,
181 and override the passing of stuff into the objects held. So here, if we're *NOT*
182 a top level container, instead of passing PointerMoved to our contained objects,
183 we check to see if our bounds are met (for selection rectangle, e.g.).
185 Also, for things like being able to split point's hot spots we need to be able
186 to check for that crap in the top level container. Which means that objects can
187 still know how to move themselves, but they can also defer to their container
188 as well. Which also means that things like HitTest() need to be in the Object
189 class so that we can leverage that stuff here as well.
192 // The TLC is passing all mouse movement here, so we're doing the same here.
193 // Need to adjust all other objects to handle things correctly.
195 // One optimization that will need to be done eventually is to subdivide the screen
196 // into parts and keep subdividing until an acceptable number of objects lie within
197 // the slice. This way, the GUI will still be responsive and *not* have to test
198 // every object for collision.
199 /*virtual*/ void Container::PointerMoved(Vector point)
201 std::vector<Object *>::iterator i;
203 if (!isTopLevelContainer)
205 // check for selection rectangle too
206 if (selectionInProgress)
208 if (selection.contains(Extents()))
216 // No need to do any checking if we're already selected...
217 // if (state == OSSelected)
221 // needUpdate = true;
226 for(i=objects.begin(); i!=objects.end(); i++)
228 if ((*i)->HitTest(point))
230 // state = OSSelected;
237 needUpdate = (oldHit != hit ? true : false);
242 Vector delta = point - oldPoint;
244 for(i=objects.begin(); i!=objects.end(); i++)
245 (*i)->Translate(delta);
254 for(std::vector<Object *>::iterator i=objects.begin(); i!=objects.end(); i++)
256 // if (objects[i]->GetState() == OSSelected)
257 (*i)->PointerMoved(point);
260 // Generic container doesn't need this???
261 // needUpdate = false;
265 /*virtual*/ void Container::PointerReleased(void)
267 if (!isTopLevelContainer)
274 draggingHandle1 = false;
275 draggingHandle2 = false;
277 // Here we check for just a click: If object was clicked and dragged, then
278 // revert to the old state (OSInactive). Otherwise, keep the new state that
281 Maybe it would be better to just check for "object was dragged" state and not
282 have to worry about keeping track of old states...
284 if (objectWasDragged)
286 //Note that the preceeding is unnecessary for a generic container!
289 // for(int i=0; i<(int)objects.size(); i++)
290 // objects[i]->PointerReleased();
291 for(std::vector<Object *>::iterator i=objects.begin(); i!=objects.end(); i++)
292 (*i)->PointerReleased();
296 /*virtual*/ bool Container::NeedsUpdate(void)
298 // If this is *not* a top level container, then we treat it as an
300 if (!isTopLevelContainer)
305 // Search through objects for one that needs an update; if one is found,
306 // return immediately.
307 for(std::vector<Object *>::iterator i=objects.begin(); i!=objects.end(); i++)
309 if ((*i)->NeedsUpdate())
317 /*virtual*/ void Container::Add(Object * object)
319 objects.push_back(object);
320 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());
335 void Container::Delete(Object * objectToDelete)
337 std::vector<Object *>::iterator i = objects.begin();
339 while (i != objects.end())
341 if (*i == objectToDelete)
344 delete objectToDelete;
353 void Container::DeleteSelectedItems(void)
355 std::vector<Object *>::iterator i = objects.begin();
357 while (i != objects.end())
359 if ((*i)->state == OSSelected)
370 void Container::Clear(void)
372 std::vector<Object *>::iterator i = objects.begin();
374 while (i != objects.end())
376 printf("Container: Deleting object ($%X)...\n", *i);
383 void Container::SelectAll(void)
385 for(unsigned int i=0; i<objects.size(); i++)
386 objects[i]->state = OSSelected;
390 void Container::DeselectAll(void)
392 for(unsigned int i=0; i<objects.size(); i++)
393 objects[i]->state = OSInactive;
397 int Container::ItemsSelected(void)
401 for(uint i=0; i<objects.size(); i++)
402 if (objects[i]->state == OSSelected)
409 Object * Container::SelectedItem(unsigned int index)
411 unsigned int selectedIndex = 0;
413 for(std::vector<Object *>::iterator i=objects.begin(); i!=objects.end(); i++)
415 if ((*i)->state == OSSelected)
417 if (selectedIndex == index)
428 void Container::MoveContentsTo(Container * newContainer)
431 if (newContainer == NULL)
434 // Shuffle the contents of this container to the new one
435 for(std::vector<Object *>::iterator i=objects.begin(); i!=objects.end(); i++)
437 newContainer->Add(*i);
438 (*i)->Reparent(newContainer);
441 // & clear our vector
446 void Container::CopyContentsTo(Container * newContainer)
449 if (newContainer == NULL)
452 // Shuffle the contents of this container to the new one
453 for(std::vector<Object *>::iterator i=objects.begin(); i!=objects.end(); i++)
454 newContainer->Add((*i)->Copy());
458 void Container::MoveSelectedContentsTo(Container * newContainer)
461 if (newContainer == NULL)
464 // Shuffle the contents of this container to the new one
465 for(std::vector<Object *>::iterator i=objects.begin(); i!=objects.end();)
467 if ((*i)->state != OSSelected)
473 newContainer->Add(*i);
474 (*i)->Reparent(newContainer);
480 void Container::CopySelectedContentsTo(Container * newContainer)
483 if (newContainer == NULL)
486 // Copy the contents of this container to the new one
487 for(std::vector<Object *>::iterator i=objects.begin(); i!=objects.end(); i++)
489 if ((*i)->state == OSSelected)
490 newContainer->Add((*i)->Copy());
495 void Container::ResizeAllDimensions(double newSize)
497 for(std::vector<Object *>::iterator i=objects.begin(); i!=objects.end(); i++)
499 if ((*i)->type == OTDimension)
500 ((Dimension *)(*i))->size = newSize;
501 if ((*i)->type == OTContainer)
502 ((Container *)(*i))->ResizeAllDimensions(newSize);
507 /*virtual*/ void Container::Enumerate(FILE * file)
509 // Only put "CONTAINER" markers if *not* the top level container
510 // if (parent != NULL)
511 if (!isTopLevelContainer)
512 fprintf(file, "CONTAINER %i\n", layer);
514 for(uint i=0; i<objects.size(); i++)
515 objects[i]->Enumerate(file);
517 // if (parent != NULL)
518 if (!isTopLevelContainer)
519 fprintf(file, "ENDCONTAINER\n");
523 /*virtual*/ Object * Container::Copy(void)
525 #warning "!!! This doesn't take care of attached Dimensions !!!"
527 This is a real problem. While having a pointer in the Dimension to this line's points
528 is fast & easy, it creates a huge problem when trying to replicate an object like this.
530 Maybe a way to fix that then, is to have reference numbers instead of pointers. That
531 way, if you copy them, ... you might still have problems. Because you can't be sure if
532 a copy will be persistant or not, you then *definitely* do not want them to have the
533 same reference number.
535 Container * c = new Container(position, parent);
541 /*virtual*/ void Container::Rotate(Point point, double angle)
543 for(std::vector<Object *>::iterator i=objects.begin(); i!=objects.end(); i++)
544 (*i)->Rotate(point, angle);
548 /*virtual*/ void Container::RotateSelected(Point point, double angle)
550 for(std::vector<Object *>::iterator i=objects.begin(); i!=objects.end(); i++)
552 if ((*i)->state == OSSelected)
553 (*i)->Rotate(point, angle);
558 /*virtual*/ void Container::Mirror(Point p1, Point p2)
560 for(std::vector<Object *>::iterator i=objects.begin(); i!=objects.end(); i++)
561 (*i)->Mirror(p1, p2);
565 /*virtual*/ void Container::MirrorSelected(Point p1, Point p2)
567 for(std::vector<Object *>::iterator i=objects.begin(); i!=objects.end(); i++)
569 if ((*i)->state == OSSelected)
570 (*i)->Mirror(p1, p2);
575 /*virtual*/ void Container::Save(void)
579 for(std::vector<Object *>::iterator i=objects.begin(); i!=objects.end(); i++)
584 /*virtual*/ void Container::Restore(void)
588 for(std::vector<Object *>::iterator i=objects.begin(); i!=objects.end(); i++)