]> Shamusworld >> Repos - architektonas/blobdiff - src/drawingview.cpp
Added object pane, grouping, load/save functionality.
[architektonas] / src / drawingview.cpp
index 015431c88e3fece1f53954e22d489a460bd5e1bf..1fd0482a83d266c0e313081f878ded884655b649 100644 (file)
@@ -7,7 +7,7 @@
 // JLH = James Hammons <jlhamm@acm.org>
 //
 // Who  When        What
-// ---  ----------  -------------------------------------------------------------
+// ---  ----------  ------------------------------------------------------------
 // JLH  03/22/2011  Created this file
 // JLH  09/29/2011  Added middle mouse button panning
 //
@@ -20,6 +20,7 @@
 // STILL TO BE DONE:
 //
 // - Lots of stuff
+// - Layer locking (hiding works)
 //
 
 // Uncomment this for debugging...
 #include "mathconstants.h"
 #include "painter.h"
 #include "structs.h"
+#include "utils.h"
 
 
 #define BACKGROUND_MAX_SIZE    512
 
-// Class variable
-//Container DrawingView::document(Vector(0, 0));
-
 
 DrawingView::DrawingView(QWidget * parent/*= NULL*/): QWidget(parent),
        // The value in the settings file will override this.
-       useAntialiasing(true), numSelected(0), numHovered(0), shiftDown(false),
+       useAntialiasing(true), /*numSelected(0),*/ numHovered(0), shiftDown(false),
        ctrlDown(false),
        gridBackground(BACKGROUND_MAX_SIZE, BACKGROUND_MAX_SIZE),
-       scale(1.0), offsetX(-10), offsetY(-10),// document(Vector(0, 0)),
-       gridPixels(0), collided(false)//, toolAction(NULL)
+       scale(1.0), offsetX(-10), offsetY(-10), document(true),
+       gridPixels(0), collided(false), hoveringIntersection(false)
 {
-//     document.isTopLevelContainer = true;
 //wtf? doesn't work except in c++11??? document = { 0 };
        setBackgroundRole(QPalette::Base);
        setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
@@ -77,19 +75,20 @@ DrawingView::DrawingView(QWidget * parent/*= NULL*/): QWidget(parent),
 #endif
 #else
        Line * line = new Line;//(Vector(5, 5), Vector(50, 40), &document);
-       line->p1 = Vector(5, 5);
-       line->p2 = Vector(50, 40);
+       line->p[0] = Vector(5, 5);
+       line->p[1] = Vector(50, 40);
        line->type = OTLine;
        line->thickness = 2.0;
        line->style = LSDash;
        line->color = 0xFF7F00;
+       line->layer = 0;
        document.objects.push_back(line);
        document.objects.push_back(new Line(Vector(50, 40), Vector(10, 83)));
        document.objects.push_back(new Line(Vector(10, 83), Vector(17, 2)));
        document.objects.push_back(new Circle(Vector(100, 100), 36));
        document.objects.push_back(new Circle(Vector(50, 150), 49));
-       document.objects.push_back(new Arc(Vector(300, 300), 32, PI / 4.0, PI * 1.3)),
-       document.objects.push_back(new Arc(Vector(200, 200), 60, PI / 2.0, PI * 1.5));
+       document.objects.push_back(new Arc(Vector(300, 300), 32, TAU / 8.0, TAU * 0.65)),
+       document.objects.push_back(new Arc(Vector(200, 200), 60, TAU / 4.0, TAU * 0.75));
        document.objects.push_back(new Dimension(Vector(50, 40), Vector(5, 5)));
        document.objects.push_back(new Text(Vector(10, 83), "Here is some awesome text!"));
 #endif
@@ -144,20 +143,6 @@ we do! :-)
 }
 
 
-#if 0
-void DrawingView::SetToolActive(Action * action)
-{
-       if (action != NULL)
-       {
-               toolAction = action;
-               connect(toolAction, SIGNAL(ObjectReady(Object *)), this,
-                       SLOT(AddNewObjectToDocument(Object *)));
-               connect(toolAction, SIGNAL(NeedRefresh()), this, SLOT(HandleActionUpdate()));
-       }
-}
-#endif
-
-
 void DrawingView::SetGridSize(uint32_t size)
 {
        // Sanity check
@@ -179,7 +164,6 @@ void DrawingView::SetGridSize(uint32_t size)
        pmp.end();
 
        // Set up new BG brush & zoom level (pixels per base unit)
-//     Painter::zoom = gridPixels / gridSpacing;
        Global::zoom = gridPixels / Global::gridSpacing;
        UpdateGridBackground();
 }
@@ -264,36 +248,74 @@ zero; so we do another modulus operation on the result to achieve this.
 }
 
 
-void DrawingView::AddNewObjectToDocument(Object * object)
+//
+// Basically, we just make a single pass through the Container. If the layer #
+// is less than the layer # being deleted, then do nothing. If the layer # is
+// equal to the layer # being deleted, then delete the object. If the layer #
+// is greater than the layer # being deleted, then set the layer # to its layer
+// # - 1.
+//
+void DrawingView::DeleteCurrentLayer(int layer)
 {
-       if (object)
+//printf("DrawingView::DeleteCurrentLayer(): currentLayer = %i\n", layer);
+       std::vector<void *>::iterator i = document.objects.begin();
+
+       while (i != document.objects.end())
        {
-//             object->Reparent(&document);
-//             document.Add(object);
-               update();
+               Object * obj = (Object *)(*i);
+
+               if (obj->layer < layer)
+                       i++;
+               else if (obj->layer == layer)
+               {
+                       document.objects.erase(i);
+                       delete obj;
+               }
+               else
+               {
+                       obj->layer--;
+                       i++;
+               }
        }
-//printf("DrawingView::AddNewObjectToDocument(). object=%08X\n", object);
+
+       // We've just done a destructive action, so update the screen!
+       update();
 }
 
 
-void DrawingView::HandleActionUpdate(void)
+void DrawingView::HandleLayerToggle(void)
 {
+       // A layer's visibility was toggled, so update the screen...
        update();
 }
 
 
-void DrawingView::SetCurrentLayer(int layer)
+//
+// A layer was moved up or down in the layer list, so we have to swap the
+// document's object's layer numbers in the layers that were swapped.
+//
+void DrawingView::HandleLayerSwap(int layer1, int layer2)
 {
-       Global::currentLayer = layer;
-//printf("DrawingView::CurrentLayer = %i\n", layer);
+//printf("DrawingView: Swapping layers %i and %i.\n", layer1, layer2);
+       std::vector<void *>::iterator i;
+
+       for(i=document.objects.begin(); i!=document.objects.end(); i++)
+       {
+               Object * obj = (Object *)(*i);
+
+               if (obj->layer == layer1)
+                       obj->layer = layer2;
+               else if (obj->layer == layer2)
+                       obj->layer = layer1;
+       }
 }
 
 
 QPoint DrawingView::GetAdjustedMousePosition(QMouseEvent * event)
 {
-       // This is undoing the transform, e.g. going from client coords to local coords.
-       // In essence, the height - y is height + (y * -1), the (y * -1) term doing the
-       // conversion of the y-axis from increasing bottom to top.
+       // This is undoing the transform, e.g. going from client coords to local
+       // coords. In essence, the height - y is height + (y * -1), the (y * -1)
+       // term doing the conversion of the y-axis from increasing bottom to top.
        return QPoint(offsetX + event->x(), offsetY + (size().height() - event->y()));
 }
 
@@ -302,7 +324,7 @@ QPoint DrawingView::GetAdjustedClientPosition(int x, int y)
 {
        // VOODOO ALERT (ON Y COMPONENT!!!!) (eh?)
        // No voodoo here, it's just grouped wrong to see it. It should be:
-       // -offsetY + (size.height() + (y * -1.0)) <-- this is wrong, offsetY should be positive
+       // -offsetY + (size.height() + (y * -1.0)) <-- this is wrong, offsetY should be positive [why? we use -offsetX after all]
        return QPoint(-offsetX + x, (size().height() - (-offsetY + y)) * +1.0);
 }
 
@@ -322,123 +344,149 @@ void DrawingView::paintEvent(QPaintEvent * /*event*/)
        painter.DrawLine(0, -16384, 0, 16384);
        painter.DrawLine(-16384, 0, 16384, 0);
 
-       // The top level document takes care of rendering for us...
-//     document.Draw(&painter);
-       // Not any more it doesn't...
-       RenderObjects(&painter, &document);
-
-#if 0
-       if (toolAction)
+       // Do object rendering...
+       for(int i=0; i<Global::numLayers; i++)
        {
-               painter.SetPen(QPen(QColor(200, 100, 0, 255), 1.0, Qt::DashLine));
-               painter.DrawCrosshair(oldPoint);
-               toolAction->Draw(&painter);
+               if (Global::layerHidden[i] == false)
+                       RenderObjects(&painter, document.objects, i);
        }
-#else
+
+       // Do tool rendering, if any...
        if (Global::tool)
        {
                painter.SetPen(QPen(QColor(200, 100, 0, 255), 1.0, Qt::DashLine));
                painter.DrawCrosshair(oldPoint);
                ToolDraw(&painter);
        }
-#endif
 
-#if 1
+       // Do selection rectangle rendering, if any
        if (Global::selectionInProgress)
        {
                painter.SetPen(QPen(QColor(255, 127, 0, 255)));
                painter.SetBrush(QBrush(QColor(255, 127, 0, 100)));
                painter.DrawRect(Global::selection);
        }
-#endif
+
+       if (hoveringIntersection)
+               painter.DrawHandle(intersectionPoint);
+
+       if (!informativeText.isEmpty())
+               painter.DrawInformativeText(informativeText);
 }
 
 
-void DrawingView::RenderObjects(Painter * painter, Container * c)
+//
+// Renders objects in the passed in vector
+//
+void DrawingView::RenderObjects(Painter * painter, std::vector<void *> & v, int layer, bool ignoreLayer/*= false*/)
 {
        std::vector<void *>::iterator i;
 
-       for(i=c->objects.begin(); i!=c->objects.end(); i++)
+       for(i=v.begin(); i!=v.end(); i++)
        {
                Object * obj = (Object *)(*i);
                float scaledThickness = Global::scale * obj->thickness;
-               painter->SetPen(obj->color, Global::zoom * scaledThickness, obj->style);
-               painter->SetBrush(obj->color);
 
-               if (obj->selected || obj->hovered)
-                       painter->SetPen(0xFF0000, Global::zoom * scaledThickness, LSDash);
+               // If the object isn't on the current layer being drawn, skip it
+               if (!ignoreLayer && (obj->layer != layer))
+                       continue;
+
+               if ((Global::tool == TTRotate) && ctrlDown && obj->selected)
+               {
+                       painter->SetPen(0x00FF00, 2.0, LSSolid);
+               }
+               else
+               {
+                       painter->SetPen(obj->color, Global::zoom * scaledThickness, obj->style);
+                       painter->SetBrush(obj->color);
+
+                       if (obj->selected || obj->hitObject)
+                               painter->SetPen(0xFF0000, Global::zoom * scaledThickness, LSDash);
+               }
 
                switch (obj->type)
                {
                case OTLine:
-               {
-                       Line * l = (Line *)obj;
-                       painter->DrawLine(l->p1, l->p2);
+                       painter->DrawLine(obj->p[0], obj->p[1]);
+
+                       if (obj->hitPoint[0])
+                               painter->DrawHandle(obj->p[0]);
+
+                       if (obj->hitPoint[1])
+                               painter->DrawHandle(obj->p[1]);
+
                        break;
-               }
                case OTCircle:
-               {
-                       Circle * ci = (Circle *)obj;
                        painter->SetBrush(QBrush(Qt::NoBrush));
-                       painter->DrawEllipse(ci->p1, ci->radius, ci->radius);
+                       painter->DrawEllipse(obj->p[0], obj->radius[0], obj->radius[0]);
+
+                       if (obj->hitPoint[0])
+                               painter->DrawHandle(obj->p[0]);
+
                        break;
-               }
                case OTArc:
-               {
-                       Arc * a = (Arc *)obj;
-                       painter->DrawArc(a->p1, a->radius, a->angle1, a->angle2);
+                       painter->DrawArc(obj->p[0], obj->radius[0], obj->angle[0], obj->angle[1]);
+
+                       if (obj->hitPoint[0])
+                               painter->DrawHandle(obj->p[0]);
+
+                       if (obj->hitPoint[1])
+                               painter->DrawHandle(obj->p[0] + (Vector(cos(obj->angle[0]), sin(obj->angle[0])) * obj->radius[0]));
+
+                       if (obj->hitPoint[2])
+                               painter->DrawHandle(obj->p[0] + (Vector(cos(obj->angle[0] + obj->angle[1]), sin(obj->angle[0] + obj->angle[1])) * obj->radius[0]));
+
                        break;
-               }
                case OTDimension:
                {
                        Dimension * d = (Dimension *)obj;
 
-                       Vector v(d->p1, d->p2);
+                       Vector v(d->p[0], d->p[1]);
                        double angle = v.Angle();
                        Vector unit = v.Unit();
-                       Vector linePt1 = d->p1, linePt2 = d->p2;
+                       Vector linePt1 = d->p[0], linePt2 = d->p[1];
                        Vector ortho;
                        double x1, y1, length;
 
                        if (d->subtype == DTLinearVert)
                        {
-                               if ((angle < 0) || (angle > PI))
+                               if ((angle < 0) || (angle > HALF_TAU))
                                {
-                                       x1 = (d->p1.x > d->p2.x ? d->p1.x : d->p2.x);
-                                       y1 = (d->p1.y > d->p2.y ? d->p1.y : d->p2.y);
+                                       x1 = (d->p[0].x > d->p[1].x ? d->p[0].x : d->p[1].x);
+                                       y1 = (d->p[0].y > d->p[1].y ? d->p[0].y : d->p[1].y);
                                        ortho = Vector(1.0, 0);
-                                       angle = PI3_OVER_2;
+                                       angle = THREE_QTR_TAU;
                                }
                                else
                                {
-                                       x1 = (d->p1.x > d->p2.x ? d->p2.x : d->p1.x);
-                                       y1 = (d->p1.y > d->p2.y ? d->p2.y : d->p1.y);
+                                       x1 = (d->p[0].x > d->p[1].x ? d->p[1].x : d->p[0].x);
+                                       y1 = (d->p[0].y > d->p[1].y ? d->p[1].y : d->p[0].y);
                                        ortho = Vector(-1.0, 0);
-                                       angle = PI_OVER_2;
+                                       angle = QTR_TAU;
                                }
 
                                linePt1.x = linePt2.x = x1;
-                               length = fabs(d->p1.y - d->p2.y);
+                               length = fabs(d->p[0].y - d->p[1].y);
                        }
                        else if (d->subtype == DTLinearHorz)
                        {
-                               if ((angle < PI_OVER_2) || (angle > PI3_OVER_2))
+                               if ((angle < QTR_TAU) || (angle > THREE_QTR_TAU))
                                {
-                                       x1 = (d->p1.x > d->p2.x ? d->p1.x : d->p2.x);
-                                       y1 = (d->p1.y > d->p2.y ? d->p1.y : d->p2.y);
+                                       x1 = (d->p[0].x > d->p[1].x ? d->p[0].x : d->p[1].x);
+                                       y1 = (d->p[0].y > d->p[1].y ? d->p[0].y : d->p[1].y);
                                        ortho = Vector(0, 1.0);
                                        angle = 0;
                                }
                                else
                                {
-                                       x1 = (d->p1.x > d->p2.x ? d->p2.x : d->p1.x);
-                                       y1 = (d->p1.y > d->p2.y ? d->p2.y : d->p1.y);
+                                       x1 = (d->p[0].x > d->p[1].x ? d->p[1].x : d->p[0].x);
+                                       y1 = (d->p[0].y > d->p[1].y ? d->p[1].y : d->p[0].y);
                                        ortho = Vector(0, -1.0);
-                                       angle = PI;
+                                       angle = HALF_TAU;
                                }
 
                                linePt1.y = linePt2.y = y1;
-                               length = fabs(d->p1.x - d->p2.x);
+                               length = fabs(d->p[0].x - d->p[1].x);
                        }
                        else if (d->subtype == DTLinear)
                        {
@@ -453,8 +501,8 @@ void DrawingView::RenderObjects(Painter * painter, Container * c)
                        Point p2 = linePt2 + (ortho * 10.0 * scaledThickness);
                        Point p3 = linePt1 + (ortho * 16.0 * scaledThickness);
                        Point p4 = linePt2 + (ortho * 16.0 * scaledThickness);
-                       Point p5 = d->p1 + (ortho * 4.0 * scaledThickness);
-                       Point p6 = d->p2 + (ortho * 4.0 * scaledThickness);
+                       Point p5 = d->p[0] + (ortho * 4.0 * scaledThickness);
+                       Point p6 = d->p[1] + (ortho * 4.0 * scaledThickness);
 
                /*
                The numbers hardcoded into here, what are they?
@@ -464,13 +512,12 @@ void DrawingView::RenderObjects(Painter * painter, Container * c)
                        painter->DrawLine(p3, p5);
                        painter->DrawLine(p4, p6);
 
-                       // Calculate whether or not the arrowheads are too crowded to put inside
-                       // the extension lines. 9.0 is the length of the arrowhead.
+                       // Calculate whether or not the arrowheads are too crowded to put
+                       // inside the extension lines. 9.0 is the length of the arrowhead.
                        double t = Geometry::ParameterOfLineAndPoint(linePt1, linePt2, linePt2 - (unit * 9.0 * scaledThickness));
-               //printf("Dimension::Draw(): t = %lf\n", t);
 
-               // On the screen, it's acting like this is actually 58%...
-               // This is correct, we want it to happen at > 50%
+                       // On the screen, it's acting like this is actually 58%...
+                       // This is correct, we want it to happen at > 50%
                        if (t > 0.58)
                        {
                                // Draw main dimension line + arrowheads
@@ -493,9 +540,6 @@ void DrawingView::RenderObjects(Painter * painter, Container * c)
                        painter->SetFont(QFont("Arial", 8.0 * Global::zoom * scaledThickness));
                        Point ctr = p2 + (Vector(p2, p1) / 2.0);
 
-               #if 0
-                       QString dimText = QString("%1\"").arg(Vector(endpoint - position).Magnitude());
-               #else
                        QString dimText;
 
                        if (length < 12.0)
@@ -510,7 +554,6 @@ void DrawingView::RenderObjects(Painter * painter, Container * c)
                                else
                                        dimText = QString("%1' %2\"").arg(feet).arg(inches);
                        }
-               #endif
 
                        painter->DrawAngledText(ctr, angle, dimText, scaledThickness);
 
@@ -519,41 +562,37 @@ void DrawingView::RenderObjects(Painter * painter, Container * c)
                case OTText:
                {
                        Text * t = (Text *)obj;
-                       painter->DrawTextObject(t->p1, t->s.c_str(), scaledThickness);
+                       painter->DrawTextObject(t->p[0], t->s.c_str(), scaledThickness);
                        break;
                }
-               default:
+               case OTSpline:
+               {
                        break;
                }
-       }
-}
-
-
-void DrawingView::DeleteSelectedItems(void)
-{
-       std::vector<void *>::iterator i = document.objects.begin();
-
-       while (i != document.objects.end())
-       {
-               Object * obj = (Object *)(*i);
-
-               if (obj->selected)
+               case OTPolygon:
                {
-                       delete obj;
-                       document.objects.erase(i);
+                       break;
                }
-               else
-                       i++;
-       }
-}
-
+               case OTContainer:
+               {
+                       // Containers require recursive rendering...
+                       Container * c = (Container *)obj;
+                       RenderObjects(painter, (*c).objects, layer);
 
-void DrawingView::ClearSelection(void)
-{
-       std::vector<void *>::iterator i;
+//printf("Container extents: <%lf, %lf>, <%lf, %lf>\nsize: %i\n", r.l, r.t, r.r, r.b, c->objects.size());
+                       // Containers also have special indicators showing they are selected
+                       if (c->selected || c->hitObject)
+                       {
+                               Rect r = GetObjectExtents(obj);
+                               painter->DrawRectCorners(r);
+                       }
 
-       for(i=document.objects.begin(); i!=document.objects.end(); i++)
-               ((Object *)(*i))->selected = false;
+                       break;
+               }
+               default:
+                       break;
+               }
+       }
 }
 
 
@@ -571,10 +610,10 @@ void DrawingView::AddHoveredToSelection(void)
 
 void DrawingView::GetSelection(std::vector<void *> & v)
 {
-       v.empty();
+       v.clear();
        std::vector<void *>::iterator i;
 
-       for(i=v.begin(); i!=v.end(); i++)
+       for(i=document.objects.begin(); i!=document.objects.end(); i++)
        {
                if (((Object *)(*i))->selected)
                        v.push_back(*i);
@@ -584,13 +623,16 @@ void DrawingView::GetSelection(std::vector<void *> & v)
 
 void DrawingView::GetHovered(std::vector<void *> & v)
 {
-       v.empty();
+       v.clear();
        std::vector<void *>::iterator i;
 
-       for(i=v.begin(); i!=v.end(); i++)
+       for(i=document.objects.begin(); i!=document.objects.end(); i++)
        {
                if (((Object *)(*i))->hovered)
+//             {
+//printf("GetHovered: adding object (%X) to hover... hp1=%s, hp2=%s, hl=%s\n", (*i), (((Line *)(*i))->hitPoint[0] ? "true" : "false"), (((Line *)(*i))->hitPoint[1] ? "true" : "false"), (((Line *)(*i))->hitObject ? "true" : "false"));
                        v.push_back(*i);
+//             }
        }
 }
 
@@ -602,49 +644,194 @@ void DrawingView::resizeEvent(QResizeEvent * /*event*/)
 }
 
 
-void DrawingView::ToolMouseDown(Point p)
+void DrawingView::ToolHandler(int mode, Point p)
+{
+       if (Global::tool == TTLine)
+               LineHandler(mode, p);
+       else if (Global::tool == TTCircle)
+               CircleHandler(mode, p);
+       else if (Global::tool == TTArc)
+               ArcHandler(mode, p);
+       else if (Global::tool == TTRotate)
+               RotateHandler(mode, p);
+       else if (Global::tool == TTMirror)
+               MirrorHandler(mode, p);
+}
+
+
+void DrawingView::ToolDraw(Painter * painter)
 {
        if (Global::tool == TTLine)
        {
                if (Global::toolState == TSNone)
                {
-                       toolPoint[0] = p;
-//                     toolState = TSPoint1;
-//                     toolObject = (Object *)(new Line(p, Vector(0, 0)));
+                       painter->DrawHandle(toolPoint[0]);
                }
-               else //if (Global::toolState == TSPoint1)
+               else if ((Global::toolState == TSPoint2) && shiftDown)
                {
-                       toolPoint[1] = p;
+                       painter->DrawHandle(toolPoint[1]);
+               }
+               else
+               {
+                       painter->DrawLine(toolPoint[0], toolPoint[1]);
+                       painter->DrawHandle(toolPoint[1]);
+
+                       Vector v(toolPoint[0], toolPoint[1]);
+                       double absAngle = v.Angle() * RADIANS_TO_DEGREES;
+                       double absLength = v.Magnitude();
+                       QString text = tr("Length: %1 in.\n") + QChar(0x2221) + tr(": %2");
+                       informativeText = text.arg(absLength).arg(absAngle);
+               }
+       }
+       else if (Global::tool == TTCircle)
+       {
+               if (Global::toolState == TSNone)
+               {
+                       painter->DrawHandle(toolPoint[0]);
+               }
+               else if ((Global::toolState == TSPoint2) && shiftDown)
+               {
+                       painter->DrawHandle(toolPoint[1]);
+               }
+               else
+               {
+                       painter->DrawCross(toolPoint[0]);
+                       double length = Vector::Magnitude(toolPoint[0], toolPoint[1]);
+                       painter->SetBrush(QBrush(Qt::NoBrush));
+                       painter->DrawEllipse(toolPoint[0], length, length);
+                       QString text = tr("Radius: %1 in.");
+                       informativeText = text.arg(length);
+               }
+       }
+       else if (Global::tool == TTArc)
+       {
+               if (Global::toolState == TSNone)
+               {
+                       painter->DrawHandle(toolPoint[0]);
+               }
+               else if (Global::toolState == TSPoint2)
+               {
+                       double length = Vector::Magnitude(toolPoint[0], toolPoint[1]);
+                       painter->SetBrush(QBrush(Qt::NoBrush));
+                       painter->DrawEllipse(toolPoint[0], length, length);
+                       painter->DrawLine(toolPoint[0], toolPoint[1]);
+                       painter->DrawHandle(toolPoint[1]);
+                       QString text = tr("Radius: %1 in.");
+                       informativeText = text.arg(length);
+               }
+               else if (Global::toolState == TSPoint3)
+               {
+                       double angle = Vector::Angle(toolPoint[0], toolPoint[2]);
+                       painter->DrawLine(toolPoint[0], toolPoint[2]);
+                       painter->SetBrush(QBrush(Qt::NoBrush));
+                       painter->DrawEllipse(toolPoint[0], toolPoint[1].x, toolPoint[1].x);
+                       painter->DrawHandle(toolPoint[0] + (Vector(cos(angle), sin(angle)) * toolPoint[1].x));
+                       QString text = tr("Angle start: %1") + QChar(0x00B0);
+                       informativeText = text.arg(RADIANS_TO_DEGREES * angle);
+               }
+               else
+               {
+                       double angle = Vector::Angle(toolPoint[0], toolPoint[3]);
+                       double span = angle - toolPoint[2].x;
+
+                       if (span < 0)
+                               span += TAU;
+
+                       painter->DrawLine(toolPoint[0], toolPoint[3]);
+                       painter->SetBrush(QBrush(Qt::NoBrush));
+                       painter->DrawEllipse(toolPoint[0], toolPoint[1].x, toolPoint[1].x);
+                       painter->SetPen(0xFF00FF, 2.0, LSSolid);
+                       painter->DrawArc(toolPoint[0], toolPoint[1].x, toolPoint[2].x, span);
+                       painter->DrawHandle(toolPoint[0] + (Vector(cos(angle), sin(angle)) * toolPoint[1].x));
+                       QString text = tr("Arc span: %1") + QChar(0x00B0);
+                       informativeText = text.arg(RADIANS_TO_DEGREES * span);
+               }
+       }
+       else if (Global::tool == TTRotate)
+       {
+               if ((Global::toolState == TSNone) || (Global::toolState == TSPoint1))
+                       painter->DrawHandle(toolPoint[0]);
+               else if ((Global::toolState == TSPoint2) && shiftDown)
+                       painter->DrawHandle(toolPoint[1]);
+               else
+               {
+                       if (toolPoint[0] == toolPoint[1])
+                               return;
+
+                       painter->DrawLine(toolPoint[0], toolPoint[1]);
+
+                       double absAngle = (Vector(toolPoint[1] - toolPoint[0]).Angle()) * RADIANS_TO_DEGREES;
+                       QString text = QChar(0x2221) + QObject::tr(": %1");
+                       informativeText = text.arg(absAngle);
+
+                       if (ctrlDown)
+                               informativeText += " (Copy)";
+               }
+       }
+       else if (Global::tool == TTMirror)
+       {
+               if ((Global::toolState == TSNone) || (Global::toolState == TSPoint1))
+                       painter->DrawHandle(toolPoint[0]);
+               else if ((Global::toolState == TSPoint2) && shiftDown)
+                       painter->DrawHandle(toolPoint[1]);
+               else
+               {
+                       if (toolPoint[0] == toolPoint[1])
+                               return;
+                       
+                       Point mirrorPoint = toolPoint[0] + Vector(toolPoint[1], toolPoint[0]);
+                       painter->DrawLine(mirrorPoint, toolPoint[1]);
+
+                       double absAngle = (Vector(toolPoint[1] - toolPoint[0]).Angle()) * RADIANS_TO_DEGREES;
+
+                       if (absAngle > 180.0)
+                               absAngle -= 180.0;
+
+                       QString text = QChar(0x2221) + QObject::tr(": %1");
+                       informativeText = text.arg(absAngle);
+
+                       if (ctrlDown)
+                               informativeText += " (Copy)";
                }
        }
 }
 
 
-void DrawingView::ToolMouseMove(Point p)
+void DrawingView::LineHandler(int mode, Point p)
 {
-       if (Global::tool == TTLine)
+       switch (mode)
        {
+       case ToolMouseDown:
                if (Global::toolState == TSNone)
                        toolPoint[0] = p;
                else
                        toolPoint[1] = p;
-       }
-}
 
+               break;
+       case ToolMouseMove:
+               if (Global::toolState == TSNone)
+                       toolPoint[0] = p;
+               else
+                       toolPoint[1] = p;
 
-void DrawingView::ToolMouseUp(Point p)
-{
-       if (Global::tool == TTLine)
-       {
+               break;
+       case ToolMouseUp:
                if (Global::toolState == TSNone)
                {
                        Global::toolState = TSPoint2;
                        // Prevent spurious line from drawing...
                        toolPoint[1] = toolPoint[0];
                }
+               else if ((Global::toolState == TSPoint2) && shiftDown)
+               {
+                       // Key override is telling us to make a new line, not continue the
+                       // previous one.
+                       toolPoint[0] = toolPoint[1];
+               }
                else
                {
                        Line * l = new Line(toolPoint[0], toolPoint[1]);
+                       l->layer = Global::activeLayer;
                        document.objects.push_back(l);
                        toolPoint[0] = toolPoint[1];
                }
@@ -652,82 +839,376 @@ void DrawingView::ToolMouseUp(Point p)
 }
 
 
-void DrawingView::ToolDraw(Painter * painter)
+void DrawingView::CircleHandler(int mode, Point p)
 {
-       if (Global::tool == TTLine)
+       switch (mode)
        {
+       case ToolMouseDown:
+               if (Global::toolState == TSNone)
+                       toolPoint[0] = p;
+               else
+                       toolPoint[1] = p;
+
+               break;
+       case ToolMouseMove:
+               if (Global::toolState == TSNone)
+                       toolPoint[0] = p;
+               else
+                       toolPoint[1] = p;
+
+               break;
+       case ToolMouseUp:
                if (Global::toolState == TSNone)
                {
-                       painter->DrawHandle(toolPoint[0]);
+                       Global::toolState = TSPoint2;
+                       // Prevent spurious line from drawing...
+                       toolPoint[1] = toolPoint[0];
+               }
+               else if ((Global::toolState == TSPoint2) && shiftDown)
+               {
+                       // Key override is telling us to make a new line, not continue the
+                       // previous one.
+                       toolPoint[0] = toolPoint[1];
                }
                else
                {
-                       painter->DrawLine(toolPoint[0], toolPoint[1]);
-                       painter->DrawHandle(toolPoint[1]);
+                       double length = Vector::Magnitude(toolPoint[0], toolPoint[1]);
+                       Circle * c = new Circle(toolPoint[0], length);
+                       c->layer = Global::activeLayer;
+                       document.objects.push_back(c);
+                       toolPoint[0] = toolPoint[1];
+                       Global::toolState = TSNone;
                }
        }
 }
 
 
-void DrawingView::mousePressEvent(QMouseEvent * event)
+void DrawingView::ArcHandler(int mode, Point p)
 {
-       if (event->button() == Qt::LeftButton)
+       switch (mode)
        {
-               Vector point = Painter::QtToCartesianCoords(Vector(event->x(), event->y()));
-//             collided = document.Collided(point);
-               collided = false;
+       case ToolMouseDown:
+               if (Global::toolState == TSNone)
+                       toolPoint[0] = p;
+               else if (Global::toolState == TSPoint2)
+                       toolPoint[1] = p;
+               else if (Global::toolState == TSPoint3)
+                       toolPoint[2] = p;
+               else
+                       toolPoint[3] = p;
 
-               // Do an update if collided with at least *one* object in the document
-//             if (collided)
-//                     update();
-               // Actually, we already know what we're going to click on as all the collision
-               // detection already happened in the mouse move function...!
-//printf("MouseDown: ctrl=%s, numHovered=%i\n", (ctrlDown ? "DOWN" : "up"), numHovered);
+               break;
+       case ToolMouseMove:
+               if (Global::toolState == TSNone)
+                       toolPoint[0] = p;
+               else if (Global::toolState == TSPoint2)
+                       toolPoint[1] = p;
+               else if (Global::toolState == TSPoint3)
+                       toolPoint[2] = p;
+               else
+                       toolPoint[3] = p;
 
-#if 0
-               if (toolAction)
+               break;
+       case ToolMouseUp:
+               if (Global::toolState == TSNone)
                {
-                       if (Global::snapToGrid)
-                               point = Global::SnapPointToGrid(point);
-
-                       // We always snap to object points, and they take precendence over
-                       // grid points...
-                       if (Global::snapPointIsValid)
-                               point = Global::snapPoint;
+                       // Prevent spurious line from drawing...
+                       toolPoint[1] = toolPoint[0];
+                       Global::toolState = TSPoint2;
+               }
+               else if (Global::toolState == TSPoint2)
+               {
+                       if (shiftDown)
+                       {
+                               // Key override is telling us to start circle at new center, not
+                               // continue the current one.
+                               toolPoint[0] = toolPoint[1];
+                               return;
+                       }
 
-                       toolAction->MouseDown(point);
+                       // Set the radius in toolPoint[1].x
+                       toolPoint[1].x = Vector::Magnitude(toolPoint[0], toolPoint[1]);
+                       Global::toolState = TSPoint3;
                }
-#else
-               if (Global::tool)
+               else if (Global::toolState == TSPoint3)
                {
-                       if (Global::snapToGrid)
-                               point = SnapPointToGrid(point);
+                       // Set the angle in toolPoint[2].x
+                       toolPoint[2].x = Vector::Angle(toolPoint[0], toolPoint[2]);
+                       Global::toolState = TSPoint4;
+               }
+               else
+               {
+                       double endAngle = Vector::Angle(toolPoint[0], toolPoint[3]);
+                       double span = endAngle - toolPoint[2].x;
 
-                       // Snap to object point if valid...
-                       
+                       if (span < 0)
+                               span += TAU;
 
-                       ToolMouseDown(point);
-                       //Also, may want to figure out if hovering over a snap point on an object,
-                       //snap to grid if not.
-                       return;
+                       Arc * arc = new Arc(toolPoint[0], toolPoint[1].x, toolPoint[2].x, span);
+                       arc->layer = Global::activeLayer;
+                       document.objects.push_back(arc);
+                       Global::toolState = TSNone;
                }
-#endif
+       }
+}
+
+
+void DrawingView::RotateHandler(int mode, Point p)
+{
+       switch (mode)
+       {
+       case ToolMouseDown:
+               if (Global::toolState == TSNone)
+               {
+                       toolPoint[0] = p;
+                       SavePointsFrom(select, toolScratch);
+                       Global::toolState = TSPoint1;
+               }
+               else if (Global::toolState == TSPoint1)
+                       toolPoint[0] = p;
+               else
+                       toolPoint[1] = p;
+
+               break;
+       case ToolMouseMove:
+               if ((Global::toolState == TSPoint1) || (Global::toolState == TSNone))
+                       toolPoint[0] = p;
+               else if (Global::toolState == TSPoint2)
+               {
+                       toolPoint[1] = p;
+
+                       if (shiftDown)
+                               return;
+
+                       double angle = Vector(toolPoint[0], toolPoint[1]).Angle();
+                       std::vector<void *>::iterator j = select.begin();
+                       std::vector<Object>::iterator i = toolScratch.begin();
+
+                       for(; i!=toolScratch.end(); i++, j++)
+                       {
+                               Object obj = *i;
+                               Point p1 = Geometry::RotatePointAroundPoint(obj.p[0], toolPoint[0], angle);
+                               Point p2 = Geometry::RotatePointAroundPoint(obj.p[1], toolPoint[0], angle);
+                               Object * obj2 = (Object *)(*j);
+                               obj2->p[0] = p1;
+                               obj2->p[1] = p2;
+
+                               if (obj.type == OTArc)
+                               {
+                                       obj2->angle[0] = obj.angle[0] + angle;
+
+                                       if (obj2->angle[0] > TAU)
+                                               obj2->angle[0] -= TAU;
+                               }
+                       }
+               }
+
+               break;
+       case ToolMouseUp:
+               if (Global::toolState == TSPoint1)
+               {
+                       Global::toolState = TSPoint2;
+                       // Prevent spurious line from drawing...
+                       toolPoint[1] = toolPoint[0];
+               }
+               else if ((Global::toolState == TSPoint2) && shiftDown)
+               {
+                       // Key override is telling us to make a new line, not continue the
+                       // previous one.
+                       toolPoint[0] = toolPoint[1];
+               }
+               else
+               {
+                       // Either we're finished with our rotate, or we're stamping a copy.
+                       if (ctrlDown)
+                       {
+                               // Stamp a copy of the selection at the current rotation & bail
+                               std::vector<void *> temp;
+                               CopyObjects(select, temp);
+                               ClearSelected(temp);
+                               AddObjectsTo(document.objects, temp);
+                               RestorePointsTo(select, toolScratch);
+                               return;
+                       }
+
+                       toolPoint[0] = p;
+                       Global::toolState = TSPoint1;
+                       SavePointsFrom(select, toolScratch);
+               }
+
+               break;
+       case ToolKeyDown:
+               // Reset the selection if shift held down...
+               if (shiftDown)
+                       RestorePointsTo(select, toolScratch);
+
+               break;
+       case ToolKeyUp:
+               // Reset selection when key is let up
+               if (!shiftDown)
+                       RotateHandler(ToolMouseMove, toolPoint[1]);
+
+               break;
+       case ToolCleanup:
+               RestorePointsTo(select, toolScratch);
+       }
+}
+
+
+void DrawingView::MirrorHandler(int mode, Point p)
+{
+       switch (mode)
+       {
+       case ToolMouseDown:
+               if (Global::toolState == TSNone)
+               {
+                       toolPoint[0] = p;
+                       SavePointsFrom(select, toolScratch);
+                       Global::toolState = TSPoint1;
+               }
+               else if (Global::toolState == TSPoint1)
+                       toolPoint[0] = p;
+               else
+                       toolPoint[1] = p;
+
+               break;
+       case ToolMouseMove:
+               if ((Global::toolState == TSPoint1) || (Global::toolState == TSNone))
+                       toolPoint[0] = p;
+               else if (Global::toolState == TSPoint2)
+               {
+                       toolPoint[1] = p;
+
+                       if (shiftDown)
+                               return;
 
+                       double angle = Vector(toolPoint[0], toolPoint[1]).Angle();
+                       std::vector<void *>::iterator j = select.begin();
+                       std::vector<Object>::iterator i = toolScratch.begin();
+
+                       for(; i!=toolScratch.end(); i++, j++)
+                       {
+                               Object obj = *i;
+                               Point p1 = Geometry::MirrorPointAroundLine(obj.p[0], toolPoint[0], toolPoint[1]);
+                               Point p2 = Geometry::MirrorPointAroundLine(obj.p[1], toolPoint[0], toolPoint[1]);
+                               Object * obj2 = (Object *)(*j);
+                               obj2->p[0] = p1;
+                               obj2->p[1] = p2;
+
+                               if (obj.type == OTArc)
+                               {
+                                       // This is 2*mirror angle - obj angle - obj span
+                                       obj2->angle[0] = (2.0 * angle) - obj.angle[0] - obj.angle[1];
+
+                                       if (obj2->angle[0] > TAU)
+                                               obj2->angle[0] -= TAU;
+                               }
+                       }
+               }
+
+               break;
+       case ToolMouseUp:
+               if (Global::toolState == TSPoint1)
+               {
+                       Global::toolState = TSPoint2;
+                       // Prevent spurious line from drawing...
+                       toolPoint[1] = toolPoint[0];
+               }
+               else if ((Global::toolState == TSPoint2) && shiftDown)
+               {
+                       // Key override is telling us to make a new line, not continue the
+                       // previous one.
+                       toolPoint[0] = toolPoint[1];
+               }
+               else
+               {
+                       // Either we're finished with our rotate, or we're stamping a copy.
+                       if (ctrlDown)
+                       {
+                               // Stamp a copy of the selection at the current rotation & bail
+                               std::vector<void *> temp;
+                               CopyObjects(select, temp);
+                               ClearSelected(temp);
+                               AddObjectsTo(document.objects, temp);
+                               RestorePointsTo(select, toolScratch);
+                               return;
+                       }
+
+                       toolPoint[0] = p;
+                       Global::toolState = TSPoint1;
+                       SavePointsFrom(select, toolScratch);
+               }
+
+               break;
+       case ToolKeyDown:
+               // Reset the selection if shift held down...
+               if (shiftDown)
+                       RestorePointsTo(select, toolScratch);
+
+               break;
+       case ToolKeyUp:
+               // Reset selection when key is let up
+               if (!shiftDown)
+                       MirrorHandler(ToolMouseMove, toolPoint[1]);
+
+               break;
+       case ToolCleanup:
+               RestorePointsTo(select, toolScratch);
+       }
+}
+
+
+void DrawingView::mousePressEvent(QMouseEvent * event)
+{
+       if (event->button() == Qt::LeftButton)
+       {
+               Vector point = Painter::QtToCartesianCoords(Vector(event->x(), event->y()));
+
+               // Handle tool processing, if any
+               if (Global::tool)
+               {
+                       if (hoveringIntersection)
+                               point = intersectionPoint;
+                       else if (Global::snapToGrid)
+                               point = SnapPointToGrid(point);
+
+                       //Also, may want to figure out if hovering over a snap point on an object,
+                       //snap to grid if not.
+                       // Snap to object point if valid...
+//                     if (Global::snapPointIsValid)
+//                             point = Global::snapPoint;
+                       
+                       ToolHandler(ToolMouseDown, point);
+                       return;
+               }
+
+               // Clear the selection only if CTRL isn't being held on click
                if (!ctrlDown)
-                       ClearSelection();
+                       ClearSelected(document.objects);
+//                     ClearSelection();
 
+               // If any objects are being hovered on click, add them to the selection
+               // & return
                if (numHovered > 0)
+               {
                        AddHoveredToSelection();
+                       update();       // needed??
+                       GetHovered(hover);      // prolly needed
 
-#if 1
-               // Didn't hit any object and not using a tool, so do a selection rectangle
-               if (!(numHovered || Global::tool))
-               {
-                       Global::selectionInProgress = true;
-                       Global::selection.setTopLeft(QPointF(point.x, point.y));
-                       Global::selection.setBottomRight(QPointF(point.x, point.y));
+                       // Needed for grab & moving objects
+                       // We do it *after*... why? (doesn't seem to confer any advantage...)
+                       if (hoveringIntersection)
+                               oldPoint = intersectionPoint;
+                       else if (Global::snapToGrid)
+                               oldPoint = SnapPointToGrid(point);
+
+                       return;
                }
-#endif
+
+               // Didn't hit any object and not using a tool, so do a selection rectangle
+               Global::selectionInProgress = true;
+               Global::selection.setTopLeft(QPointF(point.x, point.y));
+               Global::selection.setBottomRight(QPointF(point.x, point.y));
        }
        else if (event->button() == Qt::MiddleButton)
        {
@@ -745,13 +1226,15 @@ void DrawingView::mouseMoveEvent(QMouseEvent * event)
        Global::selection.setBottomRight(QPointF(point.x, point.y));
        // Only needs to be done here, as mouse down is always preceded by movement
        Global::snapPointIsValid = false;
+       hoveringIntersection = false;
 
        // Scrolling...
        if (event->buttons() & Qt::MiddleButton)
        {
                point = Vector(event->x(), event->y());
-               // Since we're using Qt coords for scrolling, we have to adjust them here to
-               // conform to Cartesian coords, since the origin is using Cartesian. :-)
+               // Since we're using Qt coords for scrolling, we have to adjust them
+               // here to conform to Cartesian coords, since the origin is using
+               // Cartesian. :-)
                Vector delta(oldPoint, point);
                delta /= Global::zoom;
                delta.y = -delta.y;
@@ -763,308 +1246,106 @@ void DrawingView::mouseMoveEvent(QMouseEvent * event)
                return;
        }
 
-#if 1
-       // Grid processing... (only snap here is left button is down)
-       // Umm, WHY??
-       if (/*(event->buttons() & Qt::LeftButton) &&*/ Global::snapToGrid)
-       {
-               point = SnapPointToGrid(point);
-       }
-
-       // Snap points on objects always take precedence over the grid, whether
-       // dragging an object or not...
-//thisnowok
-       if (Global::snapPointIsValid)
-       {
-// Uncommenting this causes the cursor to become unresponsive after the first
-// object is added.
-//             point = Global::snapPoint;
-       }
-#endif
-
-       // Do checking here to see if object can be selected or not
+       // If we're doing a selection rect, see if any objects are engulfed by it
+       // (implies left mouse button held down)
        if (Global::selectionInProgress)
        {
-               std::vector<void *>::iterator i;
-//             QRectF bounds;
-               numSelected = 0;
-
-               for(i=document.objects.begin(); i!=document.objects.end(); i++)
-               {
-                       Object * obj = (Object *)(*i);
-                       obj->selected = false;
-//                     QRectF extents;
-
-                       switch (obj->type)
-                       {
-                       case OTLine:
-                       {
-                               Line * l = (Line *)obj;
-
-                               if (Global::selection.contains(l->p1.x, l->p1.y) && Global::selection.contains(l->p2.x, l->p2.y))
-                                       l->selected = true;
-
-                               break;
-                       }
-                       case OTCircle:
-                       {
-                               Circle * c = (Circle *)obj;
-
-                               if (Global::selection.contains(c->p1.x - c->radius, c->p1.y - c->radius) && Global::selection.contains(c->p1.x + c->radius, c->p1.y + c->radius))
-                                       c->selected = true;
-
-                               break;
-                       }
-                       case OTArc:
-                       {
-                               Arc * a = (Arc *)obj;
-
-       double start = a->angle1;
-       double end = start + a->angle2;
-       QPointF p1(cos(start), sin(start));
-       QPointF p2(cos(end), sin(end));
-       QRectF bounds(p1, p2);
-
-#if 1
-       // Swap X/Y coordinates if they're backwards...
-       if (bounds.left() > bounds.right())
-       {
-               double temp = bounds.left();
-               bounds.setLeft(bounds.right());
-               bounds.setRight(temp);
+               CheckObjectBounds();
+               update();
+               return;
        }
 
-       if (bounds.bottom() > bounds.top())
+       // Handle object movement (left button down & over an object)
+       if ((event->buttons() & Qt::LeftButton) && numHovered && !Global::tool)
        {
-               double temp = bounds.bottom();
-               bounds.setBottom(bounds.top());
-               bounds.setTop(temp);
-       }
-#else
-       // Doesn't work as advertised! For shame!
-       bounds = bounds.normalized();
-#endif
-
-       // If the end of the arc is before the beginning, add 360 degrees to it
-       if (end < start)
-               end += 2.0 * PI;
-
-       // Adjust the bounds depending on which axes are crossed
-       if ((start < PI_OVER_2) && (end > PI_OVER_2))
-               bounds.setTop(1.0);
-
-       if ((start < PI) && (end > PI))
-               bounds.setLeft(-1.0);
-
-       if ((start < (PI + PI_OVER_2)) && (end > (PI + PI_OVER_2)))
-               bounds.setBottom(-1.0);
+               if (hoveringIntersection)
+                       point = intersectionPoint;
+               else if (Global::snapToGrid)
+                       point = SnapPointToGrid(point);
 
-       if ((start < (2.0 * PI)) && (end > (2.0 * PI)))
-               bounds.setRight(1.0);
-
-       if ((start < ((2.0 * PI) + PI_OVER_2)) && (end > ((2.0 * PI) + PI_OVER_2)))
-               bounds.setTop(1.0);
-
-       if ((start < (3.0 * PI)) && (end > (3.0 * PI)))
-               bounds.setLeft(-1.0);
-
-       if ((start < ((3.0 * PI) + PI_OVER_2)) && (end > ((3.0 * PI) + PI_OVER_2)))
-               bounds.setBottom(-1.0);
-
-       bounds.setTopLeft(QPointF(bounds.left() * a->radius, bounds.top() * a->radius));
-       bounds.setBottomRight(QPointF(bounds.right() * a->radius, bounds.bottom() * a->radius));
-       bounds.translate(a->p1.x, a->p1.y);
-
-                               if (Global::selection.contains(bounds))
-                                       a->selected = true;
-
-                               break;
-                       }
-                       default:
-                               break;
-                       }
-
-                       if (obj->selected)
-                               numSelected++;
-               }
+               HandleObjectMovement(point);
+               update();
+               oldPoint = point;
+               return;
        }
 
-//     oldPoint = point;
-//we should keep track of the last point here and only pass this down *if* the point
-//changed...
+       // Do object hit testing...
+       bool needUpdate = HitTestObjects(point);
 
-#if 0
-       // This returns true if we've moved over an object...
-       if (document.PointerMoved(point)) // <-- This
-       // This is where the object would do automagic dragging & shit. Since we don't
-       // do that anymore, we need a strategy to handle it.
+       // Check for multi-hover...
+       if (numHovered > 1)
        {
+               GetHovered(hover);
+               Geometry::Intersects((Object *)hover[0], (Object *)hover[1]);
+               int numIntersecting = Global::numIntersectParams;
+               double t = Global::intersectParam[0];
+               double u = Global::intersectParam[1];
 
-/*
-Now objects handle mouse move snapping as well. The code below mainly works only
-for tools; we need to fix it so that objects work as well...
+               if (numIntersecting > 0)
+               {
+                       Vector v1 = Geometry::GetPointForParameter((Object *)hover[0], t);
+                       Vector v2 = Geometry::GetPointForParameter((Object *)hover[1], u);
+                       QString text = tr("Intersection t=%1 (%3, %4), u=%2 (%5, %6)");
+                       informativeText = text.arg(t).arg(u).arg(v1.x).arg(v1.y).arg(v2.x).arg(v2.y);
 
-There's a problem with the object point snapping in that it's dependent on the
-order of the objects in the document. Most likely this is because it counts the
-selected object last and thus fucks up the algorithm. Need to fix this...
+                       hoveringIntersection = true;
+                       intersectionPoint = v1;
+               }
 
+               numIntersecting = Global::numIntersectPoints;
 
-*/
-               // Do object snapping here. Grid snapping on mouse down is done in the
-               // objects themselves, only because we have to hit test the raw point,
-               // not the snapped point. There has to be a better way...!
-               if (document.penultimateObjectHovered)
+               if (numIntersecting > 0)
                {
-                       // Two objects are hovered, see if we have an intersection point
-                       if ((document.lastObjectHovered->type == OTLine) && (document.penultimateObjectHovered->type == OTLine))
-                       {
-                               double t;
-                               int n = Geometry::Intersects((Line *)document.lastObjectHovered, (Line *)document.penultimateObjectHovered, &t);
+                       Vector v1 = Global::intersectPoint[0];
 
-                               if (n == 1)
-                               {
-                                       Global::snapPoint = document.lastObjectHovered->GetPointAtParameter(t);
-                                       Global::snapPointIsValid = true;
-                               }
-                       }
-                       else if ((document.lastObjectHovered->type == OTCircle) && (document.penultimateObjectHovered->type == OTCircle))
+                       if (numIntersecting == 2)
                        {
-                               Point p1, p2;
-                               int n = Geometry::Intersects((Circle *)document.lastObjectHovered, (Circle *)document.penultimateObjectHovered, 0, 0, 0, 0, &p1, &p2);
+                               Vector v2 = Global::intersectPoint[1];
 
-                               if (n == 1)
-                               {
-                                       Global::snapPoint = p1;
-                                       Global::snapPointIsValid = true;
-                               }
-                               else if (n == 2)
-                               {
-                                       double d1 = Vector(point, p1).Magnitude();
-                                       double d2 = Vector(point, p2).Magnitude();
-
-                                       if (d1 < d2)
-                                               Global::snapPoint = p1;
-                                       else
-                                               Global::snapPoint = p2;
-
-                                       Global::snapPointIsValid = true;
-                               }
+                               if (Vector::Magnitude(v2, point) < Vector::Magnitude(v1, point))
+                                       v1 = v2;
                        }
+
+                       QString text = tr("Intersection <%1, %2>");
+                       informativeText = text.arg(v1.x).arg(v1.y);
+                       hoveringIntersection = true;
+                       intersectionPoint = v1;
                }
-//             else
-//             {
-                       // Otherwise, it was a single object hovered...
-//             }
        }
 
-       if (toolAction)
+//this doesn't work down here for some reason... :-P
+//could be because the object being moved is part of the intersection, and this is screwing things up. In which case, we need to exclude the moving object somehow from the hit test function...
+#if 0
+       // Handle object movement (left button down & over an object)
+       if ((event->buttons() & Qt::LeftButton) && numHovered && !Global::tool)
        {
-               if (Global::snapToGrid)
-                       point = Global::SnapPointToGrid(point);
+               if (hoveringIntersection)
+                       point = intersectionPoint;
+               else if (Global::snapToGrid)
+                       point = SnapPointToGrid(point);
 
-               // We always snap to object points, and they take precendence over
-               // grid points...
-               if (Global::snapPointIsValid)
-                       point = Global::snapPoint;
-
-               toolAction->MouseMoved(point);
+               HandleObjectMovement(point);
+               update();
+               oldPoint = point;
+               return;
        }
-#else
 #endif
 
-       bool needUpdate = false;
-
-       // Don't do this kind of checking unless we're not doing a selection rectangle!
-       // Hmm, lines don't stay selected if globally selected... !!! FIX !!! [DONE]
-       // it's because there were extra state variables, the hit* vars...
-       if (!Global::selectionInProgress)
-       {
-       std::vector<void *>::iterator i;
-       numHovered = 0;
-
-       for(i=document.objects.begin(); i!=document.objects.end(); i++)
-       {
-               Object * obj = (Object *)(*i);
-//             obj->selected = false;
-
-               switch (obj->type)
-               {
-               case OTLine:
-               {
-                       Line * l = (Line *)obj;
-
-//     bool hitPoint1, hitPoint2, hitLine;
-       l->hitPoint[0] = l->hitPoint[1] = l->hitObject = false;
-       Vector lineSegment = l->p2 - l->p1;
-       Vector v1 = point - l->p1;
-       Vector v2 = point - l->p2;
-       double t = Geometry::ParameterOfLineAndPoint(l->p1, l->p2, point);
-       double distance;
-
-       if (t < 0.0)
-               distance = v1.Magnitude();
-       else if (t > 1.0)
-               distance = v2.Magnitude();
-       else
-               // distance = ?Det?(ls, v1) / |ls|
-               distance = fabs((lineSegment.x * v1.y - v1.x * lineSegment.y)
-                       / lineSegment.Magnitude());
-
-       if ((v1.Magnitude() * Global::zoom) < 8.0)
-       {
-               l->hitPoint[0] = true;
-//             snapPoint = l->p1;
-//             snapPointIsValid = true;
-       }
-       else if ((v2.Magnitude() * Global::zoom) < 8.0)
-       {
-               l->hitPoint[1] = true;
-//             snapPoint = l->p2;
-//             snapPointIsValid = true;
-       }
-       else if ((distance * Global::zoom) < 5.0)
-               l->hitObject = true;
-
-       bool oldHovered = l->hovered;
-       l->hovered = (l->hitPoint[0] || l->hitPoint[1] || l->hitObject ? true : false);
-
-       if (oldHovered != l->hovered)
-               needUpdate = true;
-
-                       break;
-               }
-               case OTCircle:
-               {
-                       Circle * c = (Circle *)obj;
-
-
-                       break;
-               }
-               default:
-                       break;
-               }
-
-               if (obj->hovered)
-               {
-                       numHovered++;
-//printf("MouseMove: OBJECT HOVERED (numHovered = %i)\n", numHovered);
-               }
-       }
-       }
-//printf("MouseMove: numHovered = %i\n", numHovered);
-
-       // Tool handling...
+       // Do tool handling, if any are active...
        if (Global::tool)
        {
-               // Need to do snapping, etc. as well
-               ToolMouseMove(point);
+               if (hoveringIntersection)
+                       point = intersectionPoint;
+               else if (Global::snapToGrid)
+                       point = SnapPointToGrid(point);
+
+               ToolHandler(ToolMouseMove, point);
        }
 
        // This is used to draw the tool crosshair...
        oldPoint = point;
 
-//     if (/*document.NeedsUpdate() ||*/ Global::selectionInProgress /*|| toolAction*/)
-       if (needUpdate || Global::selectionInProgress || Global::tool)
+       if (needUpdate || Global::tool)
                update();
 }
 
@@ -1073,50 +1354,41 @@ void DrawingView::mouseReleaseEvent(QMouseEvent * event)
 {
        if (event->button() == Qt::LeftButton)
        {
-#if 0
-               document.PointerReleased();
-#endif
-
 //We need to update especially if nothing collided and the state needs to change. !!! FIX !!!
-//could set it up to use the document's update function (assumes that all object updates
-//are being reported correctly:
+//could set it up to use the document's update function (assumes that all object
+//updates are being reported correctly:
 //             if (document.NeedsUpdate())
+               // Do an update if collided with at least *one* object in the document
 //             if (collided)
-                       update();       // Do an update if collided with at least *one* object in the document
+                       update();
 
-#if 0
-               if (toolAction)
-                       toolAction->MouseReleased();
-#else
                if (Global::tool)
                {
                        Vector point = Painter::QtToCartesianCoords(Vector(event->x(), event->y()));
-                       ToolMouseUp(point);
+                       ToolHandler(ToolMouseUp, point);
                        return;
                }
-#endif
 
                if (Global::selectionInProgress)
-               {
-                       // Select all the stuff inside of selection
                        Global::selectionInProgress = false;
 
-                       // Clear our vectors
-                       select.empty();
-                       hover.empty();
+               informativeText.clear();
+// Should we be doing this automagically? Hmm...
+               // Clear our vectors
+               select.clear();
+               hover.clear();
 
-                       // Scoop 'em up
-                       std::vector<void *>::iterator i;
+               // Scoop 'em up
+               std::vector<void *>::iterator i;
 
-                       for(i=document.objects.begin(); i!=document.objects.end(); i++)
-                       {
-                               if (((Object *)(*i))->selected)
-                                       select.push_back(*i);
+               for(i=document.objects.begin(); i!=document.objects.end(); i++)
+               {
+                       if (((Object *)(*i))->selected)
+                               select.push_back(*i);
 
 //hmm, this is no good, too late to do any good :-P
-//                             if ((*i)->hovered)
-//                                     hover.push_back(*i);
-                       }
+//                     if ((*i)->hovered)
+//                             hover.push_back(*i);
                }
        }
        else if (event->button() == Qt::MiddleButton)
@@ -1148,41 +1420,54 @@ void DrawingView::wheelEvent(QWheelEvent * event)
                Global::zoom /= zoomFactor;
        }
 
-#if 1
 //     Global::gridSpacing = gridPixels / Painter::zoom;
 //     UpdateGridBackground();
        SetGridSize(Global::gridSpacing * Global::zoom);
        update();
 //     zoomIndicator->setText(QString("Grid: %1\", BU: Inch").arg(Global::gridSpacing));
-#endif
 }
 
 
 void DrawingView::keyPressEvent(QKeyEvent * event)
 {
-#if 0
-       if (toolAction)
-               toolAction->KeyDown(event->key());
-#endif
+       bool oldShift = shiftDown;
+       bool oldCtrl = ctrlDown;
+
        if (event->key() == Qt::Key_Shift)
                shiftDown = true;
        else if (event->key() == Qt::Key_Control)
                ctrlDown = true;
+
+       if ((oldShift != shiftDown) || (oldCtrl != ctrlDown))
+       {
+               if (Global::tool)
+                       ToolHandler(ToolKeyDown, Point(0, 0));
+
+               update();
+       }
 }
 
 
 void DrawingView::keyReleaseEvent(QKeyEvent * event)
 {
-#if 0
-       if (toolAction)
-               toolAction->KeyReleased(event->key());
-#endif
+       bool oldShift = shiftDown;
+       bool oldCtrl = ctrlDown;
+
        if (event->key() == Qt::Key_Shift)
                shiftDown = false;
        else if (event->key() == Qt::Key_Control)
                ctrlDown = false;
+
+       if ((oldShift != shiftDown) || (oldCtrl != ctrlDown))
+       {
+               if (Global::tool)
+                       ToolHandler(ToolKeyUp, Point(0, 0));
+
+               update();
+       }
 }
 
+
 //
 // This looks strange, but it's really quite simple: We want a point that's
 // more than half-way to the next grid point to snap there while conversely we
@@ -1202,3 +1487,552 @@ Point DrawingView::SnapPointToGrid(Point point)
        return point;
 }
 
+
+Rect DrawingView::GetObjectExtents(Object * obj)
+{
+       // Default to empty rect, if object checks below fail for some reason
+       Rect rect;
+
+       switch (obj->type)
+       {
+       case OTLine:
+       {
+               rect = Rect(obj->p[0], obj->p[1]);
+               break;
+       }
+       case OTCircle:
+       {
+               rect = Rect(obj->p[0], obj->p[0]);
+               rect.Expand(obj->radius[0]);
+               break;
+       }
+       case OTArc:
+       {
+               Arc * a = (Arc *)obj;
+
+               double start = a->angle[0];
+               double end = start + a->angle[1];
+               rect = Rect(Point(cos(start), sin(start)), Point(cos(end), sin(end)));
+
+               // If the end of the arc is before the beginning, add 360 degrees to it
+               if (end < start)
+                       end += TAU;
+
+               // Adjust the bounds depending on which axes are crossed
+               if ((start < QTR_TAU) && (end > QTR_TAU))
+                       rect.t = 1.0;
+
+               if ((start < HALF_TAU) && (end > HALF_TAU))
+                       rect.l = -1.0;
+
+               if ((start < THREE_QTR_TAU) && (end > THREE_QTR_TAU))
+                       rect.b = -1.0;
+
+               if ((start < TAU) && (end > TAU))
+                       rect.r = 1.0;
+
+               if ((start < (TAU + QTR_TAU)) && (end > (TAU + QTR_TAU)))
+                       rect.t = 1.0;
+
+               if ((start < (TAU + HALF_TAU)) && (end > (TAU + HALF_TAU)))
+                       rect.l = -1.0;
+
+               if ((start < (TAU + THREE_QTR_TAU)) && (end > (TAU + THREE_QTR_TAU)))
+                       rect.b = -1.0;
+
+               rect *= a->radius[0];
+               rect.Translate(a->p[0]);
+
+               break;
+       }
+       case OTContainer:
+       {
+               Container * c = (Container *)obj;
+               std::vector<void *>::iterator i = c->objects.begin();
+               rect = GetObjectExtents((Object *)*i);
+               i++;
+
+               for(; i!=c->objects.end(); i++)
+                       rect |= GetObjectExtents((Object *)*i);
+       }
+       default:
+               break;
+       }
+
+       return rect;
+}
+
+
+void DrawingView::CheckObjectBounds(void)
+{
+       std::vector<void *>::iterator i;
+
+       for(i=document.objects.begin(); i!=document.objects.end(); i++)
+       {
+               Object * obj = (Object *)(*i);
+               obj->selected = false;
+
+               switch (obj->type)
+               {
+               case OTLine:
+               {
+                       Line * l = (Line *)obj;
+
+                       if (Global::selection.contains(l->p[0].x, l->p[0].y) && Global::selection.contains(l->p[1].x, l->p[1].y))
+                               l->selected = true;
+
+                       break;
+               }
+               case OTCircle:
+               {
+                       Circle * c = (Circle *)obj;
+
+                       if (Global::selection.contains(c->p[0].x - c->radius[0], c->p[0].y - c->radius[0]) && Global::selection.contains(c->p[0].x + c->radius[0], c->p[0].y + c->radius[0]))
+                               c->selected = true;
+
+                       break;
+               }
+               case OTArc:
+               {
+                       Arc * a = (Arc *)obj;
+
+                       double start = a->angle[0];
+                       double end = start + a->angle[1];
+                       QPointF p1(cos(start), sin(start));
+                       QPointF p2(cos(end), sin(end));
+                       QRectF bounds(p1, p2);
+
+#if 1
+                       // Swap X/Y coordinates if they're backwards...
+                       if (bounds.left() > bounds.right())
+                       {
+                               double temp = bounds.left();
+                               bounds.setLeft(bounds.right());
+                               bounds.setRight(temp);
+                       }
+
+                       if (bounds.bottom() > bounds.top())
+                       {
+                               double temp = bounds.bottom();
+                               bounds.setBottom(bounds.top());
+                               bounds.setTop(temp);
+                       }
+#else
+                       // Doesn't work as advertised! For shame!
+                       bounds = bounds.normalized();
+#endif
+
+                       // If the end of the arc is before the beginning, add 360 degrees to it
+                       if (end < start)
+                               end += TAU;
+
+                       // Adjust the bounds depending on which axes are crossed
+                       if ((start < QTR_TAU) && (end > QTR_TAU))
+                               bounds.setTop(1.0);
+
+                       if ((start < HALF_TAU) && (end > HALF_TAU))
+                               bounds.setLeft(-1.0);
+
+                       if ((start < THREE_QTR_TAU) && (end > THREE_QTR_TAU))
+                               bounds.setBottom(-1.0);
+
+                       if ((start < TAU) && (end > TAU))
+                               bounds.setRight(1.0);
+
+                       if ((start < (TAU + QTR_TAU)) && (end > (TAU + QTR_TAU)))
+                               bounds.setTop(1.0);
+
+                       if ((start < (TAU + HALF_TAU)) && (end > (TAU + HALF_TAU)))
+                               bounds.setLeft(-1.0);
+
+                       if ((start < (TAU + THREE_QTR_TAU)) && (end > (TAU + THREE_QTR_TAU)))
+                               bounds.setBottom(-1.0);
+
+                       bounds.setTopLeft(QPointF(bounds.left() * a->radius[0], bounds.top() * a->radius[0]));
+                       bounds.setBottomRight(QPointF(bounds.right() * a->radius[0], bounds.bottom() * a->radius[0]));
+                       bounds.translate(a->p[0].x, a->p[0].y);
+
+                       if (Global::selection.contains(bounds))
+                               a->selected = true;
+
+                       break;
+               }
+               default:
+                       break;
+               }
+       }
+}
+
+
+bool DrawingView::HitTestObjects(Point point)
+{
+       std::vector<void *>::iterator i;
+       numHovered = 0;
+       bool needUpdate = false;
+
+       for(i=document.objects.begin(); i!=document.objects.end(); i++)
+       {
+               Object * obj = (Object *)(*i);
+
+               if (HitTest(obj, point))
+                       needUpdate = true;
+#if 0
+               switch (obj->type)
+               {
+               case OTLine:
+               {
+                       bool oldHP0 = obj->hitPoint[0], oldHP1 = obj->hitPoint[1], oldHO = obj->hitObject;
+                       obj->hitPoint[0] = obj->hitPoint[1] = obj->hitObject = false;
+                       Vector lineSegment = obj->p[1] - obj->p[0];
+                       Vector v1 = point - obj->p[0];
+                       Vector v2 = point - obj->p[1];
+                       double t = Geometry::ParameterOfLineAndPoint(obj->p[0], obj->p[1], point);
+                       double distance;
+
+                       if (t < 0.0)
+                               distance = v1.Magnitude();
+                       else if (t > 1.0)
+                               distance = v2.Magnitude();
+                       else
+                               // distance = ?Det?(ls, v1) / |ls|
+                               distance = fabs((lineSegment.x * v1.y - v1.x * lineSegment.y)
+                                       / lineSegment.Magnitude());
+
+                       if ((v1.Magnitude() * Global::zoom) < 8.0)
+                               obj->hitPoint[0] = true;
+                       else if ((v2.Magnitude() * Global::zoom) < 8.0)
+                               obj->hitPoint[1] = true;
+                       else if ((distance * Global::zoom) < 5.0)
+                               obj->hitObject = true;
+
+                       obj->hovered = (obj->hitPoint[0] || obj->hitPoint[1] || obj->hitObject ? true : false);
+
+                       if ((oldHP0 != obj->hitPoint[0]) || (oldHP1 != obj->hitPoint[1]) || (oldHO != obj->hitObject))
+                               needUpdate = true;
+
+                       break;
+               }
+               case OTCircle:
+               {
+                       bool oldHP = obj->hitPoint[0], oldHO = obj->hitObject;
+                       obj->hitPoint[0] = obj->hitObject = false;
+                       double length = Vector::Magnitude(obj->p[0], point);
+
+                       if ((length * Global::zoom) < 8.0)
+                               obj->hitPoint[0] = true;
+                       else if ((fabs(length - obj->radius[0]) * Global::zoom) < 2.0)
+                               obj->hitObject = true;
+
+                       obj->hovered = (obj->hitPoint[0] || obj->hitObject ? true : false);
+
+                       if ((oldHP != obj->hitPoint[0]) || (oldHO != obj->hitObject))
+                               needUpdate = true;
+
+                       break;
+               }
+               case OTArc:
+               {
+                       bool oldHP0 = obj->hitPoint[0], oldHP1 = obj->hitPoint[1], oldHP2 = obj->hitPoint[2], oldHO = obj->hitObject;
+                       obj->hitPoint[0] = obj->hitPoint[1] = obj->hitPoint[2] = obj->hitObject = false;
+                       double length = Vector::Magnitude(obj->p[0], point);
+                       double angle = Vector::Angle(obj->p[0], point);
+
+                       // Make sure we get the angle in the correct spot
+                       if (angle < obj->angle[0])
+                               angle += TAU;
+
+                       // Get the span that we're pointing at...
+                       double span = angle - obj->angle[0];
+
+                       // N.B.: Still need to hit test the arc start & arc span handles...
+                       double spanAngle = obj->angle[0] + obj->angle[1];
+                       Point handle1 = obj->p[0] + (Vector(cos(obj->angle[0]), sin(obj->angle[0])) * obj->radius[0]);
+                       Point handle2 = obj->p[0] + (Vector(cos(spanAngle), sin(spanAngle)) * obj->radius[0]);
+                       double length2 = Vector::Magnitude(point, handle1);
+                       double length3 = Vector::Magnitude(point, handle2);
+
+                       if ((length * Global::zoom) < 8.0)
+                               obj->hitPoint[0] = true;
+                       else if ((length2 * Global::zoom) < 8.0)
+                               obj->hitPoint[1] = true;
+                       else if ((length3 * Global::zoom) < 8.0)
+                               obj->hitPoint[2] = true;
+                       else if (((fabs(length - obj->radius[0]) * Global::zoom) < 2.0) && (span < obj->angle[1]))
+                               obj->hitObject = true;
+
+                       obj->hovered = (obj->hitPoint[0] || obj->hitPoint[1] || obj->hitPoint[2] || obj->hitObject ? true : false);
+
+                       if ((oldHP0 != obj->hitPoint[0]) || (oldHP1 != obj->hitPoint[1]) || (oldHP2 != obj->hitPoint[2]) || (oldHO != obj->hitObject))
+                               needUpdate = true;
+
+                       break;
+               }
+               case OTContainer:
+               {
+                       // Containers must be recursively tested...
+                       Container * c = (Container *)obj;
+                       std::vector<void *>::iterator i;
+
+                       for(i=c->objects.begin(); i!=c->objects.end(); i++)
+                       {
+                               
+                       }
+               }
+               default:
+                       break;
+               }
+#endif
+
+               if (obj->hovered)
+               {
+                       numHovered++;
+//printf("MouseMove: OBJECT HOVERED (numHovered = %i)\n", numHovered);
+                       emit(ObjectHovered(obj));
+               }
+       }
+
+       return needUpdate;
+}
+
+
+bool DrawingView::HitTest(Object * obj, Point point)
+{
+       bool needUpdate = false;
+
+       switch (obj->type)
+       {
+       case OTLine:
+       {
+               bool oldHP0 = obj->hitPoint[0], oldHP1 = obj->hitPoint[1], oldHO = obj->hitObject;
+               obj->hitPoint[0] = obj->hitPoint[1] = obj->hitObject = false;
+               Vector lineSegment = obj->p[1] - obj->p[0];
+               Vector v1 = point - obj->p[0];
+               Vector v2 = point - obj->p[1];
+               double t = Geometry::ParameterOfLineAndPoint(obj->p[0], obj->p[1], point);
+               double distance;
+
+               if (t < 0.0)
+                       distance = v1.Magnitude();
+               else if (t > 1.0)
+                       distance = v2.Magnitude();
+               else
+                       // distance = ?Det?(ls, v1) / |ls|
+                       distance = fabs((lineSegment.x * v1.y - v1.x * lineSegment.y)
+                               / lineSegment.Magnitude());
+
+               if ((v1.Magnitude() * Global::zoom) < 8.0)
+                       obj->hitPoint[0] = true;
+               else if ((v2.Magnitude() * Global::zoom) < 8.0)
+                       obj->hitPoint[1] = true;
+               else if ((distance * Global::zoom) < 5.0)
+                       obj->hitObject = true;
+
+               obj->hovered = (obj->hitPoint[0] || obj->hitPoint[1] || obj->hitObject ? true : false);
+
+               if ((oldHP0 != obj->hitPoint[0]) || (oldHP1 != obj->hitPoint[1]) || (oldHO != obj->hitObject))
+                       needUpdate = true;
+
+               break;
+       }
+       case OTCircle:
+       {
+               bool oldHP = obj->hitPoint[0], oldHO = obj->hitObject;
+               obj->hitPoint[0] = obj->hitObject = false;
+               double length = Vector::Magnitude(obj->p[0], point);
+
+               if ((length * Global::zoom) < 8.0)
+                       obj->hitPoint[0] = true;
+               else if ((fabs(length - obj->radius[0]) * Global::zoom) < 2.0)
+                       obj->hitObject = true;
+
+               obj->hovered = (obj->hitPoint[0] || obj->hitObject ? true : false);
+
+               if ((oldHP != obj->hitPoint[0]) || (oldHO != obj->hitObject))
+                       needUpdate = true;
+
+               break;
+       }
+       case OTArc:
+       {
+               bool oldHP0 = obj->hitPoint[0], oldHP1 = obj->hitPoint[1], oldHP2 = obj->hitPoint[2], oldHO = obj->hitObject;
+               obj->hitPoint[0] = obj->hitPoint[1] = obj->hitPoint[2] = obj->hitObject = false;
+               double length = Vector::Magnitude(obj->p[0], point);
+               double angle = Vector::Angle(obj->p[0], point);
+
+               // Make sure we get the angle in the correct spot
+               if (angle < obj->angle[0])
+                       angle += TAU;
+
+               // Get the span that we're pointing at...
+               double span = angle - obj->angle[0];
+
+               // N.B.: Still need to hit test the arc start & arc span handles...
+               double spanAngle = obj->angle[0] + obj->angle[1];
+               Point handle1 = obj->p[0] + (Vector(cos(obj->angle[0]), sin(obj->angle[0])) * obj->radius[0]);
+               Point handle2 = obj->p[0] + (Vector(cos(spanAngle), sin(spanAngle)) * obj->radius[0]);
+               double length2 = Vector::Magnitude(point, handle1);
+               double length3 = Vector::Magnitude(point, handle2);
+
+               if ((length * Global::zoom) < 8.0)
+                       obj->hitPoint[0] = true;
+               else if ((length2 * Global::zoom) < 8.0)
+                       obj->hitPoint[1] = true;
+               else if ((length3 * Global::zoom) < 8.0)
+                       obj->hitPoint[2] = true;
+               else if (((fabs(length - obj->radius[0]) * Global::zoom) < 2.0) && (span < obj->angle[1]))
+                       obj->hitObject = true;
+
+               obj->hovered = (obj->hitPoint[0] || obj->hitPoint[1] || obj->hitPoint[2] || obj->hitObject ? true : false);
+
+               if ((oldHP0 != obj->hitPoint[0]) || (oldHP1 != obj->hitPoint[1]) || (oldHP2 != obj->hitPoint[2]) || (oldHO != obj->hitObject))
+                       needUpdate = true;
+
+               break;
+       }
+       case OTContainer:
+       {
+               // Containers must be recursively tested...
+               Container * c = (Container *)obj;
+               c->hitObject = false;
+               c->hovered = false;
+               std::vector<void *>::iterator i;
+
+               for(i=c->objects.begin(); i!=c->objects.end(); i++)
+               {
+                       Object * cObj = (Object *)*i;
+
+                       if (HitTest(cObj, point))
+                               needUpdate = true;
+
+                       if (cObj->hitObject == true)
+                               c->hitObject = true;
+
+                       if (cObj->hovered == true)
+                               c->hovered = true;
+               }
+
+               break;
+       }
+       default:
+               break;
+       }
+
+       return needUpdate;
+}
+
+
+void DrawingView::HandleObjectMovement(Point point)
+{
+       Point delta = point - oldPoint;
+//printf("HOM: old = (%f,%f), new = (%f, %f), delta = (%f, %f)\n", oldPoint.x, oldPoint.y, point.x, point.y, delta.x, delta.y);
+       Object * obj = (Object *)hover[0];
+//printf("Object type = %i (size=%i), ", obj->type, hover.size());
+//printf("Object (%X) move: hp1=%s, hp2=%s, hl=%s\n", obj, (obj->hitPoint[0] ? "true" : "false"), (obj->hitPoint[1] ? "true" : "false"), (obj->hitObject ? "true" : "false"));
+
+       switch (obj->type)
+       {
+       case OTLine:
+               if (obj->hitPoint[0])
+                       obj->p[0] = point;
+               else if (obj->hitPoint[1])
+                       obj->p[1] = point;
+               else if (obj->hitObject)
+               {
+                       obj->p[0] += delta;
+                       obj->p[1] += delta;
+               }
+
+               break;
+       case OTCircle:
+               if (obj->hitPoint[0])
+                       obj->p[0] = point;
+               else if (obj->hitObject)
+               {
+//this doesn't work. we need to save this on mouse down for this to work correctly!
+//                     double oldRadius = obj->radius[0];
+                       obj->radius[0] = Vector::Magnitude(obj->p[0], point);
+
+                       QString text = QObject::tr("Radius: %1");//\nScale: %2%");
+                       informativeText = text.arg(obj->radius[0], 0, 'd', 4);//.arg(obj->radius[0] / oldRadius * 100.0, 0, 'd', 0);
+               }
+
+               break;
+       case OTArc:
+               if (obj->hitPoint[0])
+                       obj->p[0] = point;
+               else if (obj->hitPoint[1])
+               {
+                       // Change the Arc's span (handle #1)
+                       if (shiftDown)
+                       {
+                               double angle = Vector::Angle(obj->p[0], point);
+                               double delta = angle - obj->angle[0];
+
+                               if (delta < 0)
+                                       delta += TAU;
+
+                               obj->angle[1] -= delta;
+                               obj->angle[0] = angle;
+
+                               if (obj->angle[1] < 0)
+                                       obj->angle[1] += TAU;
+
+                               QString text = QObject::tr("Span: %1") + QChar(0x00B0) + QObject::tr("\n%2") + QChar(0x00B0) + QObject::tr(" - %3") + QChar(0x00B0);
+                               informativeText = text.arg(obj->angle[1] * RADIANS_TO_DEGREES, 0, 'd', 4).arg(obj->angle[0] * RADIANS_TO_DEGREES, 0, 'd', 2).arg((obj->angle[0] + obj->angle[1]) * RADIANS_TO_DEGREES, 0, 'd', 2);
+                               return;
+                       }
+
+                       double angle = Vector::Angle(obj->p[0], point);
+                       obj->angle[0] = angle;
+                       QString text = QObject::tr("Start angle: %1") + QChar(0x00B0);
+                       informativeText = text.arg(obj->angle[0] * RADIANS_TO_DEGREES, 0, 'd', 4);
+               }
+               else if (obj->hitPoint[2])
+               {
+                       // Change the Arc's span (handle #2)
+                       if (shiftDown)
+                       {
+                               double angle = Vector::Angle(obj->p[0], point);
+                               obj->angle[1] = angle - obj->angle[0];
+
+                               if (obj->angle[1] < 0)
+                                       obj->angle[1] += TAU;
+
+                               QString text = QObject::tr("Span: %1") + QChar(0x00B0) + QObject::tr("\n%2") + QChar(0x00B0) + QObject::tr(" - %3") + QChar(0x00B0);
+                               informativeText = text.arg(obj->angle[1] * RADIANS_TO_DEGREES, 0, 'd', 4).arg(obj->angle[0] * RADIANS_TO_DEGREES, 0, 'd', 2).arg((obj->angle[0] + obj->angle[1]) * RADIANS_TO_DEGREES, 0, 'd', 2);
+                               return;
+                       }
+
+                       double angle = Vector::Angle(obj->p[0], point);
+                       obj->angle[0] = angle - obj->angle[1];
+
+                       if (obj->angle[0] < 0)
+                               obj->angle[0] += TAU;
+
+                       QString text = QObject::tr("End angle: %1") + QChar(0x00B0);
+                       informativeText = text.arg((obj->angle[0] + obj->angle[1]) * RADIANS_TO_DEGREES, 0, 'd', 4);
+               }
+               else if (obj->hitObject)
+               {
+                       if (shiftDown)
+                       {
+                               return;
+                       }
+
+                       obj->radius[0] = Vector::Magnitude(obj->p[0], point);
+                       QString text = QObject::tr("Radius: %1");
+                       informativeText = text.arg(obj->radius[0], 0, 'd', 4);
+               }
+
+               break;
+       case OTContainer:
+               // This is shitty, but works for now until I can code up something
+               // nicer :-)
+               TranslateObject(obj, delta);
+
+               break;
+       default:
+               break;
+       }
+}
+