]> Shamusworld >> Repos - architektonas/blobdiff - src/drawingview.cpp
Added Mirror tool
[architektonas] / src / drawingview.cpp
index 21c85dd48236cacb7d1cac4904ab5403af51ff77..fff56da8ef77bbd2e78a0f28a0278d82339fad4e 100644 (file)
 #include "mathconstants.h"
 #include "painter.h"
 #include "structs.h"
+#include "utils.h"
 
 
 #define BACKGROUND_MAX_SIZE    512
 
-enum { ToolMouseDown, ToolMouseMove, ToolMouseUp };
-
 // Class variable
 //Container DrawingView::document(Vector(0, 0));
 
@@ -79,8 +78,8 @@ 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;
@@ -266,24 +265,6 @@ zero; so we do another modulus operation on the result to achieve this.
 }
 
 
-void DrawingView::AddNewObjectToDocument(Object * object)
-{
-       if (object)
-       {
-//             object->Reparent(&document);
-//             document.Add(object);
-               update();
-       }
-//printf("DrawingView::AddNewObjectToDocument(). object=%08X\n", object);
-}
-
-
-void DrawingView::HandleActionUpdate(void)
-{
-       update();
-}
-
-
 void DrawingView::SetCurrentLayer(int layer)
 {
        Global::currentLayer = layer;
@@ -324,88 +305,96 @@ 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);
+       // Do object rendering...
+       RenderObjects(&painter, document.objects);
 
-#if 0
-       if (toolAction)
-       {
-               painter.SetPen(QPen(QColor(200, 100, 0, 255), 1.0, Qt::DashLine));
-               painter.DrawCrosshair(oldPoint);
-               toolAction->Draw(&painter);
-       }
-#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 (!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)
 {
        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->hitObject)
-                       painter->SetPen(0xFF0000, Global::zoom * scaledThickness, LSDash);
+               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 (l->hitPoint[0])
-                               painter->DrawHandle(l->p1);
+                       if (obj->hitPoint[0])
+                               painter->DrawHandle(obj->p[0]);
 
-                       if (l->hitPoint[1])
-                               painter->DrawHandle(l->p2);
+                       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;
 
@@ -413,41 +402,41 @@ void DrawingView::RenderObjects(Painter * painter, Container * c)
                        {
                                if ((angle < 0) || (angle > PI))
                                {
-                                       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;
                                }
                                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;
                                }
 
                                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))
                                {
-                                       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;
                                }
 
                                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)
                        {
@@ -462,8 +451,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?
@@ -478,8 +467,8 @@ void DrawingView::RenderObjects(Painter * painter, Container * c)
                        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
@@ -528,7 +517,7 @@ 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:
@@ -538,34 +527,6 @@ void DrawingView::RenderObjects(Painter * painter, Container * c)
 }
 
 
-void DrawingView::DeleteSelectedItems(void)
-{
-       std::vector<void *>::iterator i = document.objects.begin();
-
-       while (i != document.objects.end())
-       {
-               Object * obj = (Object *)(*i);
-
-               if (obj->selected)
-               {
-                       delete obj;
-                       document.objects.erase(i);
-               }
-               else
-                       i++;
-       }
-}
-
-
-void DrawingView::ClearSelection(void)
-{
-       std::vector<void *>::iterator i;
-
-       for(i=document.objects.begin(); i!=document.objects.end(); i++)
-               ((Object *)(*i))->selected = false;
-}
-
-
 void DrawingView::AddHoveredToSelection(void)
 {
        std::vector<void *>::iterator i;
@@ -614,10 +575,18 @@ void DrawingView::resizeEvent(QResizeEvent * /*event*/)
 }
 
 
-void DrawingView::ToolMouse(int mode, 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);
 }
 
 
@@ -642,8 +611,138 @@ void DrawingView::ToolDraw(Painter * painter)
                        double absAngle = v.Angle() * RADIANS_TO_DEGREES;
                        double absLength = v.Magnitude();
                        QString text = tr("Length: %1 in.\n") + QChar(0x2221) + tr(": %2");
-                       text = text.arg(absLength).arg(absAngle);
-                       painter->DrawInformativeText(text);
+                       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
+               {
+                       double length = Vector::Magnitude(toolPoint[0], toolPoint[1]);
+//                     painter->DrawLine(toolPoint[0], toolPoint[1]);
+//                     painter->DrawHandle(toolPoint[1]);
+                       painter->SetBrush(QBrush(Qt::NoBrush));
+                       painter->DrawEllipse(toolPoint[0], length, length);
+                       QString text = tr("Radius: %1 in.");//\n") + QChar(0x2221) + tr(": %2");
+                       informativeText = text.arg(length);//.arg(absAngle);
+               }
+       }
+       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 += PI_TIMES_2;
+
+                       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]);
+                       // Likely we need a tool container for this... (now we do!)
+#if 0
+                       if (ctrlDown)
+                       {
+                               painter->SetPen(0x00FF00, 2.0, LSSolid);
+                               overrideColor = true;
+                       }
+
+                       RenderObjects(painter, toolObjects);
+                       overrideColor = false;
+#endif
+
+                       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;
+                       
+                       painter->DrawLine(toolPoint[0], toolPoint[1]);
+                       // Likely we need a tool container for this... (now we do!)
+#if 0
+                       if (ctrlDown)
+                       {
+                               painter->SetPen(0x00FF00, 2.0, LSSolid);
+                               overrideColor = true;
+                       }
+
+                       RenderObjects(painter, toolObjects);
+                       overrideColor = false;
+#endif
+
+                       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)";
                }
        }
 }
@@ -690,6 +789,326 @@ void DrawingView::LineHandler(int mode, Point p)
 }
 
 
+void DrawingView::CircleHandler(int mode, Point p)
+{
+       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)
+               {
+                       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
+               {
+                       double length = Vector::Magnitude(toolPoint[0], toolPoint[1]);
+                       Circle * c = new Circle(toolPoint[0], length);
+                       document.objects.push_back(c);
+                       toolPoint[0] = toolPoint[1];
+                       Global::toolState = TSNone;
+               }
+       }
+}
+
+
+void DrawingView::ArcHandler(int mode, Point p)
+{
+       switch (mode)
+       {
+       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;
+
+               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;
+
+               break;
+       case ToolMouseUp:
+               if (Global::toolState == TSNone)
+               {
+                       // 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;
+                       }
+
+                       // Set the radius in toolPoint[1].x
+                       toolPoint[1].x = Vector::Magnitude(toolPoint[0], toolPoint[1]);
+                       Global::toolState = TSPoint3;
+               }
+               else if (Global::toolState == TSPoint3)
+               {
+                       // 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;
+
+                       if (span < 0)
+                               span += PI_TIMES_2;
+
+                       Arc * arc = new Arc(toolPoint[0], toolPoint[1].x, toolPoint[2].x, span);
+                       document.objects.push_back(arc);
+                       Global::toolState = TSNone;
+               }
+       }
+}
+
+
+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] > PI_TIMES_2)
+                                               obj2->angle[0] -= PI_TIMES_2;
+                               }
+                       }
+               }
+
+               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)
+                               {
+#if 0
+                                       // This is WRONG for mirroring...
+                                       obj2->angle[0] = obj.angle[0] + angle;
+#endif
+                                       obj2->angle[0] = (2.0 * angle) - obj.angle[0] - obj.angle[1];
+
+                                       if (obj2->angle[0] > PI_TIMES_2)
+                                               obj2->angle[0] -= PI_TIMES_2;
+                               }
+                       }
+               }
+
+               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)
@@ -708,13 +1127,14 @@ void DrawingView::mousePressEvent(QMouseEvent * event)
 //                     if (Global::snapPointIsValid)
 //                             point = Global::snapPoint;
                        
-                       ToolMouse(ToolMouseDown, point);
+                       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
@@ -725,6 +1145,7 @@ void DrawingView::mousePressEvent(QMouseEvent * event)
                        GetHovered(hover);      // prolly needed
 
                        // Needed for grab & moving objects
+                       // We do it *after*... why? (doesn't seem to confer any advantage...)
                        if (Global::snapToGrid)
                                oldPoint = SnapPointToGrid(point);
 
@@ -780,38 +1201,12 @@ void DrawingView::mouseMoveEvent(QMouseEvent * event)
        }
 
        // Handle object movement (left button down & over an object)
-       if ((event->buttons() & Qt::LeftButton) && numHovered)
+       if ((event->buttons() & Qt::LeftButton) && numHovered && !Global::tool)
        {
                if (Global::snapToGrid)
                        point = SnapPointToGrid(point);
 
-               Point delta = point - oldPoint;
-               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:
-               {
-                       Line * l = (Line *)obj;
-
-                       if (l->hitPoint[0])
-                               l->p1 = point;
-                       else if (l->hitPoint[1])
-                               l->p2 = point;
-                       else if (l->hitObject)
-                       {
-                               l->p1 += delta;
-                               l->p2 += delta;
-                       }
-
-                       break;
-               }
-               default:
-                       break;
-               }
-
+               HandleObjectMovement(point);
                update();
                oldPoint = point;
                return;
@@ -826,7 +1221,7 @@ void DrawingView::mouseMoveEvent(QMouseEvent * event)
                if (Global::snapToGrid)
                        point = SnapPointToGrid(point);
 
-               ToolMouse(ToolMouseMove, point);
+               ToolHandler(ToolMouseMove, point);
        }
 
        // This is used to draw the tool crosshair...
@@ -841,51 +1236,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:
 //             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);
-                       ToolMouse(ToolMouseUp, point);
+                       ToolHandler(ToolMouseUp, point);
                        return;
                }
-#endif
 
                if (Global::selectionInProgress)
-               {
-                       // Select all the stuff inside of selection
                        Global::selectionInProgress = false;
 
-                       // Clear our vectors
-                       select.clear();
-                       hover.clear();
+               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)
@@ -917,22 +1302,16 @@ 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;
 
@@ -942,16 +1321,17 @@ void DrawingView::keyPressEvent(QKeyEvent * event)
                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;
 
@@ -961,9 +1341,15 @@ void DrawingView::keyReleaseEvent(QKeyEvent * event)
                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
@@ -1000,7 +1386,7 @@ void DrawingView::CheckObjectBounds(void)
                {
                        Line * l = (Line *)obj;
 
-                       if (Global::selection.contains(l->p1.x, l->p1.y) && Global::selection.contains(l->p2.x, l->p2.y))
+                       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;
@@ -1009,7 +1395,7 @@ void DrawingView::CheckObjectBounds(void)
                {
                        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))
+                       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;
@@ -1018,8 +1404,8 @@ void DrawingView::CheckObjectBounds(void)
                {
                        Arc * a = (Arc *)obj;
 
-                       double start = a->angle1;
-                       double end = start + a->angle2;
+                       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);
@@ -1070,9 +1456,9 @@ void DrawingView::CheckObjectBounds(void)
                        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);
+                       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;
@@ -1103,13 +1489,12 @@ bool DrawingView::HitTestObjects(Point point)
                {
                case OTLine:
                {
-                       Line * l = (Line *)obj;
-                       bool oldHP0 = l->hitPoint[0], oldHP1 = l->hitPoint[1], oldHO = l->hitObject;
-                       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);
+                       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)
@@ -1122,34 +1507,71 @@ bool DrawingView::HitTestObjects(Point point)
                                        / lineSegment.Magnitude());
 
                        if ((v1.Magnitude() * Global::zoom) < 8.0)
-                       {
-                               l->hitPoint[0] = true;
-//             snapPoint = l->p1;
-//             snapPointIsValid = true;
-                       }
+                               obj->hitPoint[0] = true;
                        else if ((v2.Magnitude() * Global::zoom) < 8.0)
-                       {
-                               l->hitPoint[1] = true;
-//             snapPoint = l->p2;
-//             snapPointIsValid = true;
-                       }
+                               obj->hitPoint[1] = true;
                        else if ((distance * Global::zoom) < 5.0)
-                               l->hitObject = true;
+                               obj->hitObject = true;
 
-//                     bool oldHovered = l->hovered;
-                       l->hovered = (l->hitPoint[0] || l->hitPoint[1] || l->hitObject ? true : false);
-//                     l->hovered = l->hitObject;
+                       obj->hovered = (obj->hitPoint[0] || obj->hitPoint[1] || obj->hitObject ? true : false);
 
-//                     if (oldHovered != l->hovered)
-                       if ((oldHP0 != l->hitPoint[0]) || (oldHP1 != l->hitPoint[1]) || (oldHO != l->hitObject))
+                       if ((oldHP0 != obj->hitPoint[0]) || (oldHP1 != obj->hitPoint[1]) || (oldHO != obj->hitObject))
                                needUpdate = true;
 
                        break;
                }
                case OTCircle:
                {
-                       Circle * c = (Circle *)obj;
+                       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 += PI_TIMES_2;
+
+                       // 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;
                }
@@ -1168,6 +1590,117 @@ bool DrawingView::HitTestObjects(Point point)
 }
 
 
+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 += PI_TIMES_2;
+
+                               obj->angle[1] -= delta;
+                               obj->angle[0] = angle;
+
+                               if (obj->angle[1] < 0)
+                                       obj->angle[1] += PI_TIMES_2;
+
+                               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] += PI_TIMES_2;
+
+                               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] += PI_TIMES_2;
+
+                       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;
+       default:
+               break;
+       }
+}
+
+
+
 #if 0
        // This returns true if we've moved over an object...
        if (document.PointerMoved(point)) // <-- This