X-Git-Url: http://shamusworld.gotdns.org/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=src%2Fcontainer.cpp;h=9c3d403bf74b8864af6cfea0b0a4a0f2e036c520;hb=7f3a6b11585376eecd80979ec3da2346c5314d88;hp=727c14360450cc498cf809b119bb08f3967021fb;hpb=9f6ad3fe0b9cb30115a5d38e8af3aebed0d70c08;p=architektonas diff --git a/src/container.cpp b/src/container.cpp index 727c143..9c3d403 100644 --- a/src/container.cpp +++ b/src/container.cpp @@ -4,38 +4,100 @@ // (C) 2011 Underground Software // See the README and GPLv3 files for licensing and warranty information // -// JLH = James L. Hammons +// JLH = James Hammons // // 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 +#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(); +} + + +// Assignment operator +Container & Container::operator=(const Container & from) +{ + // Take care of self-assignment + if (this == &from) + return *this; + + Clear(); + std::vector::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(QPainter * painter) + +/*virtual*/ void Container::Draw(Painter * painter) { - for(int i=0; i<(int)objects.size(); i++) - objects[i]->Draw(painter); + QRectF boundary; + +//int a=1; + for(std::vector::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,118 +111,176 @@ 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)) + // 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) + { + for(std::vector::iterator i=objects.begin(); i!=objects.end();) { - oldState = state; - state = OSSelected; - 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 if (state == OSSelected) + else { - // 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::iterator i=objects.begin(); i!=objects.end(); i++) { - oldState = state; -// state = OSInactive; - oldPoint = point; - dragging = true; - return true; + if ((*i)->Collided(point)) + { + collision = true; + lastObjectClicked = *i; +//printf("Container::Collided: lastObjectClicked = %X\n", lastObjectClicked); + } } } -#else - bool collision = false; - for(int i=0; i<(int)objects.size(); i++) + 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 { - if (objects[i]->Collided(point)) - collision = true; + state = (collision ? OSSelected : OSInactive); + + if (state == OSSelected) + { + DeselectAll(); + dragging = true; + oldPoint = point; + } } -#endif - // Do we decouple the state of the generic container from the objects inside??? - 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::iterator i; + lastObjectHovered = penultimateObjectHovered = 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::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)) + { + penultimateObjectHovered = lastObjectHovered; + 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; @@ -168,31 +288,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::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::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::iterator i=objects.begin(); i!=objects.end(); i++) + bounds = bounds.united((*i)->Extents()); + + return bounds; } + + +void Container::Delete(Object * objectToDelete) +{ + std::vector::iterator i = objects.begin(); + + while (i != objects.end()) + { + if (*i == objectToDelete) + { + objects.erase(i); + delete objectToDelete; + return; + } + + i++; + } +} + + +void Container::DeleteSelectedItems(void) +{ + std::vector::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::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; istate = OSSelected; +} + + +void Container::DeselectAll(void) +{ + for(unsigned int i=0; istate = OSInactive; +} + + +int Container::ItemsSelected(void) +{ + int selected = 0; + + for(uint i=0; istate == OSSelected) + selected++; + + return selected; +} + + +Object * Container::SelectedItem(unsigned int index) +{ + unsigned int selectedIndex = 0; + + for(std::vector::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::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::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::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::iterator i=objects.begin(); i!=objects.end(); i++) + { + if ((*i)->state == OSSelected) + newContainer->Add((*i)->Copy()); + } +} + + +void Container::ResizeAllDimensions(double newSize) +{ + for(std::vector::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; iEnumerate(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::iterator i=objects.begin(); i!=objects.end(); i++) + (*i)->Rotate(point, angle); +} + + +/*virtual*/ void Container::RotateSelected(Point point, double angle) +{ + for(std::vector::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::iterator i=objects.begin(); i!=objects.end(); i++) + (*i)->Mirror(p1, p2); +} + + +/*virtual*/ void Container::MirrorSelected(Point p1, Point p2) +{ + for(std::vector::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::iterator i=objects.begin(); i!=objects.end(); i++) + (*i)->Save(); +} + + +/*virtual*/ void Container::Restore(void) +{ + Object::Restore(); + + for(std::vector::iterator i=objects.begin(); i!=objects.end(); i++) + (*i)->Restore(); +} +