]> Shamusworld >> Repos - architektonas/blobdiff - src/container.cpp
Trim tool now works for Lines, but inaccurate.
[architektonas] / src / container.cpp
index 4fceefdac106829e6d0a939f5e89fc27b66f437e..2eca9f4573473c0c772a4efd96060836e6bc15ec 100644 (file)
 // (C) 2011 Underground Software
 // See the README and GPLv3 files for licensing and warranty information
 //
-// JLH = James L. Hammons <jlhamm@acm.org>
+// JLH = James Hammons <jlhamm@acm.org>
 //
 // WHO  WHEN        WHAT
 // ---  ----------  ------------------------------------------------------------
 // JLH  03/30/2011  Created this file
+// JLH  06/02/2011  Added code to delete objects in this container when they go
+//                  out of scope
 //
 
 #include "container.h"
 
 #include <QtGui>
+#include "dimension.h"
+#include "painter.h"
 
 
 Container::Container(Vector p1, Object * p/*= NULL*/): Object(p1, p),
-       dragging(false), draggingHandle1(false), draggingHandle2(false)//, needUpdate(false)
+       isTopLevelContainer(false),
+       dragging(false), draggingHandle1(false), draggingHandle2(false),
+       hit(false)//, needUpdate(false)
 {
+       type = OTContainer;
+       state = OSInactive;
+}
+
+
+// Copy constructor
+Container::Container(const Container & copy): Object(copy.position, copy.parent)
+{
+       // Use overloaded assignment operator
+       *this = copy;
+       type = OTContainer;
+       state = OSInactive;
 }
 
+
 Container::~Container()
 {
+       Clear();
 }
 
-/*virtual*/ void Container::Draw(QPainter * painter)
+
+// Assignment operator
+Container & Container::operator=(const Container & from)
 {
-       for(int i=0; i<(int)objects.size(); i++)
-               objects[i]->Draw(painter);
+       // Take care of self-assignment
+       if (this == &from)
+               return *this;
+
+       Clear();
+       std::vector<Object *>::const_iterator i;
+
+       for(i=from.objects.begin(); i!=from.objects.end(); i++)
+       {
+printf("Container: Copying object $%08X...\n", *i);
+               Object * object = (*i)->Copy();
+               objects.push_back(object);
+       }
+
+       return *this;
 }
 
+
+/*virtual*/ void Container::Draw(Painter * painter)
+{
+       QRectF boundary;
+
+//int a=1;
+       for(std::vector<Object *>::iterator i=objects.begin(); i!=objects.end(); i++)
+       {
+//printf("Containter::Draw item #%i [%X]...\n", a++, *i);
+               (*i)->Draw(painter);
+               boundary = boundary.united((*i)->Extents());
+       }
+
+       if ((state == OSSelected) || hit)
+       {
+               if (hit)
+                       painter->SetPen(QPen(Qt::magenta, 1.0, Qt::DashLine));
+               else
+                       painter->SetPen(QPen(Qt::blue, 2.0, Qt::DashLine));
+
+               painter->SetBrush(QBrush(Qt::NoBrush));
+               painter->DrawPaddedRect(boundary);
+       }
+}
+
+
 /*virtual*/ Vector Container::Center(void)
 {
        return position;
 }
 
+
 /*
  We need at least *three* handles for this object:
  - one for moving
@@ -49,134 +111,173 @@ I click here and drag there?"
 Also: should put the snap logic into the Object base class (as a static method)...
 */
 
+
+// Need to add checking here for clicking on a member of a group (Container),
+// and checking for if it's a top level container (the DrawingView's document).
+/*
+One approach is to check for the parent of the container: If it's NULL, then it's
+the DrawingView's document. It might be better, though, to set a boolean like
+isTopLevelContainer so that we can do things like edit members of a group without
+having to ungroup them first (like Inkscape).
+*/
 /*virtual*/ bool Container::Collided(Vector point)
 {
        objectWasDragged = false;
-       Vector v1 = position - point;
+       bool collision = false;
+       lastObjectClicked = NULL;
 
-#if 0
-       if (state == OSInactive)
-       {
-//printf("Circle: pp = %lf, length = %lf, distance = %lf\n", parameterizedPoint, lineSegment.Magnitude(), distance);
-//printf("      v1.Magnitude = %lf, v2.Magnitude = %lf\n", v1.Magnitude(), v2.Magnitude());
-//printf("      point = %lf,%lf,%lf; p1 = %lf,%lf,%lf; p2 = %lf,%lf,%lf\n", point.x, point.y, point.z, position.x, position.y, position.z, endpoint.x, endpoint.y, endpoint.z);
-//printf("      \n", );
-//How to translate this into pixels from Document space???
-//Maybe we need to pass a scaling factor in here from the caller? That would make sense, as
-//the caller knows about the zoom factor and all that good kinda crap
-               if (v1.Magnitude() < 10.0)
-               {
-                       oldState = state;
-                       state = OSSelected;
-                       oldPoint = position; //maybe "position"?
-                       draggingHandle1 = true;
-                       return true;
-               }
-               else if ((v1.Magnitude() < radius + 2.0) && (v1.Magnitude() > radius - 2.0))
-               {
-                       oldState = state;
-                       state = OSSelected;
-                       oldPoint = point;
-                       dragging = true;
-                       return true;
-               }
-       }
-       else if (state == OSSelected)
+       // NOTE that this deletes the object on mouse down instead of mouse up.
+       // Have to check to see how it feels to do it that way...
+       // N.B.: This only works because the toolAction is not set, &
+       //       Object::ignoreClicks isn't set either...
+       if (deleteActive)
        {
-               // Here we test for collision with handles as well! (SOON!)
-/*
-Like so:
-               if (v1.Magnitude() < 2.0) // Handle #1
-               else if (v2.Magnitude() < 2.0) // Handle #2
-*/
-               if ((v1.Magnitude() < radius + 2.0) && (v1.Magnitude() > radius - 2.0))
+               for(std::vector<Object *>::iterator i=objects.begin(); i!=objects.end();)
                {
-                       oldState = state;
-//                     state = OSInactive;
-                       oldPoint = point;
-                       dragging = true;
-                       return true;
+                       if ((*i)->Collided(point))
+                       {
+printf("Container::Collided: Deleting object ($%X)\n", *i);
+                               Object * objectToDelete = *i;
+                               objects.erase(i);
+                               delete objectToDelete;
+                       }
+                       else
+                               i++;
                }
        }
-#else
-       bool collision = false;
-
-       // NOTE that this deletes the object on mouse down instead of mouse up. Have to
-       // check to see how it feels to do it that way...
-       if (deleteActive)
+       else
        {
-               for(int i=0; i<(int)objects.size(); i++)
+               for(std::vector<Object *>::iterator i=objects.begin(); i!=objects.end(); i++)
                {
-                       if (objects[i]->Collided(point))
+                       if ((*i)->Collided(point))
                        {
-                               objects.erase(objects.begin() + i);     // Calls the destructor, (deletes the object, I presume... O_o)
-                               break;
+                               collision = true;
+                               lastObjectClicked = *i;
+//printf("Container::Collided: lastObjectClicked = %X\n", lastObjectClicked);
                        }
                }
        }
+
+       if (snapToGrid)
+               point = SnapPointToGrid(point);
+
+       // We check to see if the container we're trying to access is the
+       // DrawingView's document. If so, we ignore the state of the container.
+       // Otherwise, we care about the state of the container. :-)
+       if (isTopLevelContainer)
+               state = OSInactive;
        else
        {
-               for(int i=0; i<(int)objects.size(); i++)
+               state = (collision ? OSSelected : OSInactive);
+
+               if (state == OSSelected)
                {
-                       if (objects[i]->Collided(point))
-                               collision = true;
+                       DeselectAll();
+                       dragging = true;
+                       oldPoint = point;
                }
        }
-#endif
 
-       // Do we decouple the state of the generic container from the objects inside??? Mebbe.
-       state = OSInactive;
-//     return false;
        return collision;
 }
 
-/*virtual*/ void Container::PointerMoved(Vector point)
+/*
+What we need to do is check for whether or not we're a top level container,
+and override the passing of stuff into the objects held. So here, if we're *NOT*
+a top level container, instead of passing PointerMoved to our contained objects,
+we check to see if our bounds are met (for selection rectangle, e.g.).
+
+Also, for things like being able to split point's hot spots we need to be able
+to check for that crap in the top level container. Which means that objects can
+still know how to move themselves, but they can also defer to their container
+as well. Which also means that things like HitTest() need to be in the Object
+class so that we can leverage that stuff here as well.
+*/
+
+// The TLC is passing all mouse movement here, so we're doing the same here.
+// Need to adjust all other objects to handle things correctly.
+
+// One optimization that will need to be done eventually is to subdivide the screen
+// into parts and keep subdividing until an acceptable number of objects lie within
+// the slice. This way, the GUI will still be responsive and *not* have to test
+// every object for collision.
+/*virtual*/ bool Container::PointerMoved(Vector point)
 {
-       objectWasDragged = true;
-#if 0
-       if (dragging)
+       std::vector<Object *>::iterator i;
+       lastObjectHovered = NULL;
+
+       if (!isTopLevelContainer)
        {
-               // Here we need to check whether or not we're dragging a handle or the object itself...
-//             Vector delta = point - oldPoint;
+               // check for selection rectangle too
+               if (selectionInProgress)
+               {
+                       if (selection.contains(Extents()))
+                               state = OSSelected;
+                       else
+                               state = OSInactive;
 
-//             position += delta;
-//             endpoint += delta;
-               radius = Vector(point - position).Magnitude();
+                       return false;
+               }
 
-               oldPoint = point;
-               needUpdate = true;
-       }
-       else if (draggingHandle1)
-       {
-               Vector delta = point - oldPoint;
-               position += delta;
-               oldPoint = point;
-               needUpdate = true;
-       }
-/*     else if (draggingHandle2)
-       {
-               Vector delta = point - oldPoint;
+               // No need to do any checking if we're already selected...
+//             if (state == OSSelected)
+//                     return;
 
-               endpoint += delta;
+//             oldState = state;
+//             needUpdate = true;
+//             if (dragging &&
+               bool oldHit = hit;
+               hit = false;
 
-               oldPoint = point;
-               needUpdate = true;
-       }*/
-       else
-               needUpdate = false;
-#else
-       for(int i=0; i<(int)objects.size(); i++)
+               for(i=objects.begin(); i!=objects.end(); i++)
+               {
+                       if ((*i)->HitTest(point))
+                       {
+//                             state = OSSelected;
+//                             return;
+                               hit = true;
+                               break;
+                       }
+               }
+
+               needUpdate = (oldHit != hit ? true : false);
+//             state = oldState;
+
+               if (dragging)
+               {
+                       Vector delta = point - oldPoint;
+
+                       for(i=objects.begin(); i!=objects.end(); i++)
+                               (*i)->Translate(delta);
+
+                       oldPoint = point;
+                       needUpdate = true;
+               }
+
+               return false;
+       }
+
+       for(std::vector<Object *>::iterator i=objects.begin(); i!=objects.end(); i++)
        {
-               if (objects[i]->GetState() == OSSelected)
-                       objects[i]->PointerMoved(point);
+//             if (objects[i]->GetState() == OSSelected)
+               if ((*i)->PointerMoved(point))
+                       lastObjectHovered = *i;
        }
-#endif
+
        // Generic container doesn't need this???
 //     needUpdate = false;
+       return (lastObjectHovered == NULL ? false : true);
 }
 
+
 /*virtual*/ void Container::PointerReleased(void)
 {
+       if (!isTopLevelContainer)
+       {
+               dragging = false;
+               return;
+       }
+#if 0
        dragging = false;
        draggingHandle1 = false;
        draggingHandle2 = false;
@@ -184,31 +285,315 @@ Like so:
        // Here we check for just a click: If object was clicked and dragged, then
        // revert to the old state (OSInactive). Otherwise, keep the new state that
        // we set.
-/*Maybe it would be better to just check for "object was dragged" state and not have to worry
-about keeping track of old states...
+/*
+Maybe it would be better to just check for "object was dragged" state and not
+have to worry about keeping track of old states...
 */
        if (objectWasDragged)
                state = oldState;
 //Note that the preceeding is unnecessary for a generic container!
+#endif
 
-       for(int i=0; i<(int)objects.size(); i++)
-               objects[i]->PointerReleased();
+//     for(int i=0; i<(int)objects.size(); i++)
+//             objects[i]->PointerReleased();
+       for(std::vector<Object *>::iterator i=objects.begin(); i!=objects.end(); i++)
+               (*i)->PointerReleased();
 }
 
+
 /*virtual*/ bool Container::NeedsUpdate(void)
 {
-       needUpdate = false;
+       // If this is *not* a top level container, then we treat it as an
+       // aggregate object.
+       if (!isTopLevelContainer)
+       {
+               return needUpdate;
+       }
 
-       for(int i=0; i<(int)objects.size(); i++)
+       // Search through objects for one that needs an update; if one is found,
+       // return immediately.
+       for(std::vector<Object *>::iterator i=objects.begin(); i!=objects.end(); i++)
        {
-               if (objects[i]->NeedsUpdate())
-                       needUpdate = true;
+               if ((*i)->NeedsUpdate())
+                       return true;
        }
 
-       return needUpdate;
+       return false;
 }
 
-void Container::Add(Object * object)
+
+/*virtual*/ void Container::Add(Object * object)
 {
        objects.push_back(object);
+printf("Container: Added object (=$%X). size = %li\n", object, objects.size());
+}
+
+
+/*virtual*/ QRectF Container::Extents(void)
+{
+       QRectF bounds;
+
+       for(std::vector<Object *>::iterator i=objects.begin(); i!=objects.end(); i++)
+               bounds = bounds.united((*i)->Extents());
+
+       return bounds;
+}
+
+
+void Container::Delete(Object * objectToDelete)
+{
+       std::vector<Object *>::iterator i = objects.begin();
+
+       while (i != objects.end())
+       {
+               if (*i == objectToDelete)
+               {
+                       objects.erase(i);
+                       delete objectToDelete;
+                       return;
+               }
+
+               i++;
+       }
+}
+
+
+void Container::DeleteSelectedItems(void)
+{
+       std::vector<Object *>::iterator i = objects.begin();
+
+       while (i != objects.end())
+       {
+               if ((*i)->state == OSSelected)
+               {
+                       delete *i;
+                       objects.erase(i);
+               }
+               else
+                       i++;
+       }
+}
+
+
+void Container::Clear(void)
+{
+       std::vector<Object *>::iterator i = objects.begin();
+
+       while (i != objects.end())
+       {
+printf("Container: Deleting object ($%X)...\n", *i);
+               delete (*i);
+               objects.erase(i);
+       }
+}
+
+
+void Container::SelectAll(void)
+{
+       for(unsigned int i=0; i<objects.size(); i++)
+               objects[i]->state = OSSelected;
+}
+
+
+void Container::DeselectAll(void)
+{
+       for(unsigned int i=0; i<objects.size(); i++)
+               objects[i]->state = OSInactive;
+}
+
+
+int Container::ItemsSelected(void)
+{
+       int selected = 0;
+
+       for(uint i=0; i<objects.size(); i++)
+               if (objects[i]->state == OSSelected)
+                       selected++;
+
+       return selected;
 }
+
+
+Object * Container::SelectedItem(unsigned int index)
+{
+       unsigned int selectedIndex = 0;
+
+       for(std::vector<Object *>::iterator i=objects.begin(); i!=objects.end(); i++)
+       {
+               if ((*i)->state == OSSelected)
+               {
+                       if (selectedIndex == index)
+                               return *i;
+                       else
+                               selectedIndex++;
+               }
+       }
+
+       return NULL;
+}
+
+
+void Container::MoveContentsTo(Container * newContainer)
+{
+       // Sanity check
+       if (newContainer == NULL)
+               return;
+
+       // Shuffle the contents of this container to the new one
+       for(std::vector<Object *>::iterator i=objects.begin(); i!=objects.end(); i++)
+       {
+               newContainer->Add(*i);
+               (*i)->Reparent(newContainer);
+       }
+
+       // & clear our vector
+       objects.clear();
+}
+
+
+void Container::CopyContentsTo(Container * newContainer)
+{
+       // Sanity check
+       if (newContainer == NULL)
+               return;
+
+       // Shuffle the contents of this container to the new one
+       for(std::vector<Object *>::iterator i=objects.begin(); i!=objects.end(); i++)
+               newContainer->Add((*i)->Copy());
+}
+
+
+void Container::MoveSelectedContentsTo(Container * newContainer)
+{
+       // Sanity check
+       if (newContainer == NULL)
+               return;
+
+       // Shuffle the contents of this container to the new one
+       for(std::vector<Object *>::iterator i=objects.begin(); i!=objects.end();)
+       {
+               if ((*i)->state != OSSelected)
+               {
+                       i++;
+                       continue;
+               }
+
+               newContainer->Add(*i);
+               (*i)->Reparent(newContainer);
+               objects.erase(i);
+       }
+}
+
+
+void Container::CopySelectedContentsTo(Container * newContainer)
+{
+       // Sanity check
+       if (newContainer == NULL)
+               return;
+
+       // Copy the contents of this container to the new one
+       for(std::vector<Object *>::iterator i=objects.begin(); i!=objects.end(); i++)
+       {
+               if ((*i)->state == OSSelected)
+                       newContainer->Add((*i)->Copy());
+       }
+}
+
+
+void Container::ResizeAllDimensions(double newSize)
+{
+       for(std::vector<Object *>::iterator i=objects.begin(); i!=objects.end(); i++)
+       {
+               if ((*i)->type == OTDimension)
+                       ((Dimension *)(*i))->size = newSize;
+               if ((*i)->type == OTContainer)
+                       ((Container *)(*i))->ResizeAllDimensions(newSize);
+       }
+}
+
+
+/*virtual*/ void Container::Enumerate(FILE * file)
+{
+       // Only put "CONTAINER" markers if *not* the top level container
+//     if (parent != NULL)
+       if (!isTopLevelContainer)
+               fprintf(file, "CONTAINER %i\n", layer);
+
+       for(uint i=0; i<objects.size(); i++)
+               objects[i]->Enumerate(file);
+
+//     if (parent != NULL)
+       if (!isTopLevelContainer)
+               fprintf(file, "ENDCONTAINER\n");
+}
+
+
+/*virtual*/ Object * Container::Copy(void)
+{
+#warning "!!! This doesn't take care of attached Dimensions !!!"
+/*
+This is a real problem. While having a pointer in the Dimension to this line's points
+is fast & easy, it creates a huge problem when trying to replicate an object like this.
+
+Maybe a way to fix that then, is to have reference numbers instead of pointers. That
+way, if you copy them, ... you might still have problems. Because you can't be sure if
+a copy will be persistant or not, you then *definitely* do not want them to have the
+same reference number.
+*/
+       Container * c = new Container(position, parent);
+       *c = *this;
+       return c;
+}
+
+
+/*virtual*/ void Container::Rotate(Point point, double angle)
+{
+       for(std::vector<Object *>::iterator i=objects.begin(); i!=objects.end(); i++)
+               (*i)->Rotate(point, angle);
+}
+
+
+/*virtual*/ void Container::RotateSelected(Point point, double angle)
+{
+       for(std::vector<Object *>::iterator i=objects.begin(); i!=objects.end(); i++)
+       {
+               if ((*i)->state == OSSelected)
+                       (*i)->Rotate(point, angle);
+       }
+}
+
+
+/*virtual*/ void Container::Mirror(Point p1, Point p2)
+{
+       for(std::vector<Object *>::iterator i=objects.begin(); i!=objects.end(); i++)
+               (*i)->Mirror(p1, p2);
+}
+
+
+/*virtual*/ void Container::MirrorSelected(Point p1, Point p2)
+{
+       for(std::vector<Object *>::iterator i=objects.begin(); i!=objects.end(); i++)
+       {
+               if ((*i)->state == OSSelected)
+                       (*i)->Mirror(p1, p2);
+       }
+}
+
+
+/*virtual*/ void Container::Save(void)
+{
+       Object::Save();
+
+       for(std::vector<Object *>::iterator i=objects.begin(); i!=objects.end(); i++)
+               (*i)->Save();
+}
+
+
+/*virtual*/ void Container::Restore(void)
+{
+       Object::Restore();
+
+       for(std::vector<Object *>::iterator i=objects.begin(); i!=objects.end(); i++)
+               (*i)->Restore();
+}
+