]> Shamusworld >> Repos - architektonas/blobdiff - src/drawingview.cpp
Fix scrollwheel zooming from walking all over the screen.
[architektonas] / src / drawingview.cpp
index ca063c51d548070b1a000158d15b0d53e9fdcf7e..dc6853203769404357e2ac7ca05c5e57f9c7329f 100644 (file)
@@ -1,7 +1,8 @@
+//
 // drawingview.cpp
 //
 // Part of the Architektonas Project
-// (C) 2011 Underground Software
+// (C) 2011-2020 Underground Software
 // See the README and GPLv3 files for licensing and warranty information
 //
 // JLH = James Hammons <jlhamm@acm.org>
 //
 // - Redo rendering code to *not* use Qt's transform functions, as they are tied
 //   to a left-handed system and we need a right-handed one. [DONE]
+// - Fixed length tool doesn't work on lines [DONE]
 //
 // STILL TO BE DONE:
 //
 // - Lots of stuff
 // - Layer locking (hiding works)
+// - Fixed angle tool doesn't work on lines
+// - Make it so "dirty" flag reflects drawing state
 //
 
 // Uncomment this for debugging...
 
 DrawingView::DrawingView(QWidget * parent/*= NULL*/): QWidget(parent),
        // The value in the settings file will override this.
-       useAntialiasing(true), /*numSelected(0),*/ numHovered(0), shiftDown(false),
-       ctrlDown(false),
+       useAntialiasing(true), numHovered(0), shiftDown(false),
+       ctrlDown(false), altDown(false),
        gridBackground(BACKGROUND_MAX_SIZE, BACKGROUND_MAX_SIZE),
        scale(1.0), offsetX(-10), offsetY(-10), document(true),
-       gridPixels(0), collided(false), hoveringIntersection(false),
-       dragged(NULL), draggingObject(false), angleSnap(false)
+       gridPixels(0), collided(false), scrollDrag(false), hoverPointValid(false),
+       hoveringIntersection(false), dragged(NULL), draggingObject(false),
+       angleSnap(false), dirty(false)
 {
 //wtf? doesn't work except in c++11??? document = { 0 };
        setBackgroundRole(QPalette::Base);
        setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
 
        Global::gridSpacing = 12.0;             // In base units (inch is default)
-#if 0
-       Line * line = new Line(Vector(5, 5), Vector(50, 40), &document);
+
+       Line * line = new Line(Vector(5, 5), Vector(50, 40), 2.0, 0xFF7F00, LSDash);
        document.Add(line);
-       document.Add(new Line(Vector(50, 40), Vector(10, 83), &document));
-       document.Add(new Line(Vector(10, 83), Vector(17, 2), &document));
-       document.Add(new Circle(Vector(100, 100), 36, &document));
-       document.Add(new Circle(Vector(50, 150), 49, &document));
-       document.Add(new Arc(Vector(300, 300), 32, PI / 4.0, PI * 1.3, &document)),
-       document.Add(new Arc(Vector(200, 200), 60, PI / 2.0, PI * 1.5, &document));
-#if 1
-       Dimension * dimension = new Dimension(Vector(0, 0), Vector(0, 0), DTLinear, &document);
-       line->SetDimensionOnLine(dimension);
-       document.Add(dimension);
-#else
-       // Alternate way to do the above...
-       line->SetDimensionOnLine();
-#endif
-#else
-       Line * line = new Line;//(Vector(5, 5), Vector(50, 40), &document);
-       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, 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
+       document.Add(new Line(Vector(50, 40), Vector(10, 83)));
+       document.Add(new Line(Vector(10, 83), Vector(17, 2)));
+       document.Add(new Circle(Vector(100, 100), 36));
+       document.Add(new Circle(Vector(50, 150), 49));
+       document.Add(new Arc(Vector(300, 300), 32, TAU / 8.0, TAU * 0.65)),
+       document.Add(new Arc(Vector(200, 200), 60, TAU / 4.0, TAU * 0.75));
+       document.Add(new Text(Vector(10, 83), "Here is some awesome text!"));
+
+       AddDimensionTo(line);
 
 /*
 Here we set the grid size in pixels--12 in this case. Initially, we have our
@@ -164,7 +145,8 @@ void DrawingView::SetGridSize(uint32_t size)
        pmp.end();
 
        // Set up new BG brush & zoom level (pixels per base unit)
-       Global::zoom = gridPixels / Global::gridSpacing;
+// This shouldn't be done here, because it fucks up the scrollwheel zooming...
+//     Global::zoom = gridPixels / Global::gridSpacing;
        UpdateGridBackground();
 }
 
@@ -258,7 +240,7 @@ zero; so we do another modulus operation on the result to achieve this.
 void DrawingView::DeleteCurrentLayer(int layer)
 {
 //printf("DrawingView::DeleteCurrentLayer(): currentLayer = %i\n", layer);
-       std::vector<void *>::iterator i = document.objects.begin();
+       VPVectorIter i = document.objects.begin();
 
        while (i != document.objects.end())
        {
@@ -296,10 +278,9 @@ void DrawingView::HandleLayerToggle(void)
 //
 void DrawingView::HandleLayerSwap(int layer1, int layer2)
 {
+// !!! FIX !!! This doesn't properly handle container contents...
 //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++)
+       for(VPVectorIter i=document.objects.begin(); i!=document.objects.end(); i++)
        {
                Object * obj = (Object *)(*i);
 
@@ -311,6 +292,55 @@ void DrawingView::HandleLayerSwap(int layer1, int layer2)
 }
 
 
+void DrawingView::HandlePenWidth(float width)
+{
+       for(VPVectorIter i=select.begin(); i!=select.end(); i++)
+       {
+               Object * obj = (Object *)(*i);
+               obj->thickness = width;
+       }
+}
+
+
+void DrawingView::HandlePenStyle(int style)
+{
+       for(VPVectorIter i=select.begin(); i!=select.end(); i++)
+       {
+               Object * obj = (Object *)(*i);
+               obj->style = style;
+       }
+}
+
+
+void DrawingView::HandlePenColor(uint32_t color)
+{
+       for(VPVectorIter i=select.begin(); i!=select.end(); i++)
+       {
+               Object * obj = (Object *)(*i);
+               obj->color = color;
+       }
+}
+
+
+void DrawingView::HandlePenStamp(void)
+{
+       VPVector flat = Flatten(select);
+
+       for(VPVectorIter i=flat.begin(); i!=flat.end(); i++)
+       {
+               Object * obj = (Object *)(*i);
+
+               if (obj->type != OTText)
+                       obj->thickness = Global::penWidth;
+
+               obj->style = Global::penStyle;
+               obj->color = Global::penColor;
+       }
+
+       update();
+}
+
+
 QPoint DrawingView::GetAdjustedMousePosition(QMouseEvent * event)
 {
        // This is undoing the transform, e.g. going from client coords to local
@@ -329,6 +359,18 @@ QPoint DrawingView::GetAdjustedClientPosition(int x, int y)
 }
 
 
+void DrawingView::focusOutEvent(QFocusEvent * /*event*/)
+{
+//     printf("DrawingView::focusOutEvent()...\n");
+       // Make sure all modkeys being held are marked as released when the app
+       // loses focus (N.B.: This only works because the app sets the focus policy
+       // of this object to something other than Qt::NoFocus)
+       shiftDown = ctrlDown = altDown = false;
+       scrollDrag = false;
+       setCursor(Qt::ArrowCursor);
+}
+
+
 void DrawingView::paintEvent(QPaintEvent * /*event*/)
 {
        QPainter qtPainter(this);
@@ -370,6 +412,9 @@ void DrawingView::paintEvent(QPaintEvent * /*event*/)
        if (hoveringIntersection)
                painter.DrawHandle(intersectionPoint);
 
+       if (hoverPointValid)
+               painter.DrawHandle(hoverPoint);
+
        if (!informativeText.isEmpty())
                painter.DrawInformativeText(informativeText);
 }
@@ -378,11 +423,15 @@ void DrawingView::paintEvent(QPaintEvent * /*event*/)
 //
 // Renders objects in the passed in vector
 //
-void DrawingView::RenderObjects(Painter * painter, std::vector<void *> & v, int layer, bool ignoreLayer/*= false*/)
+/*
+N.B.: Since we have "hoverPointValid" drawing regular object handles above,
+      we can probably do away with a lot of them that are being done down below.
+      !!! FIX !!!
+      [Well, it seems to work OK *except* when you move one of the points, then you get to see nothing. Is it worth fixing there to get rid of problems here? Have to investigate...]
+*/
+void DrawingView::RenderObjects(Painter * painter, VPVector & v, int layer, bool ignoreLayer/*= false*/)
 {
-       std::vector<void *>::iterator i;
-
-       for(i=v.begin(); i!=v.end(); i++)
+       for(VPVectorIter i=v.begin(); i!=v.end(); i++)
        {
                Object * obj = (Object *)(*i);
                float scaledThickness = Global::scale * obj->thickness;
@@ -415,7 +464,11 @@ void DrawingView::RenderObjects(Painter * painter, std::vector<void *> & v, int
                        if (obj->hitPoint[1])
                                painter->DrawHandle(obj->p[1]);
 
+                       if (obj->hitObject)
+                               painter->DrawSmallHandle(Geometry::Midpoint((Line *)obj));
+
                        break;
+
                case OTCircle:
                        painter->SetBrush(QBrush(Qt::NoBrush));
                        painter->DrawEllipse(obj->p[0], obj->radius[0], obj->radius[0]);
@@ -424,6 +477,7 @@ void DrawingView::RenderObjects(Painter * painter, std::vector<void *> & v, int
                                painter->DrawHandle(obj->p[0]);
 
                        break;
+
                case OTArc:
                        painter->DrawArc(obj->p[0], obj->radius[0], obj->angle[0], obj->angle[1]);
 
@@ -437,6 +491,7 @@ void DrawingView::RenderObjects(Painter * painter, std::vector<void *> & v, int
                                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;
@@ -497,10 +552,10 @@ void DrawingView::RenderObjects(Painter * painter, std::vector<void *> & v, int
 
                        unit = Vector(d->lp[0], d->lp[1]).Unit();
 
-                       Point p1 = d->lp[0] + (ortho * 10.0 * scaledThickness);
-                       Point p2 = d->lp[1] + (ortho * 10.0 * scaledThickness);
-                       Point p3 = d->lp[0] + (ortho * 16.0 * scaledThickness);
-                       Point p4 = d->lp[1] + (ortho * 16.0 * scaledThickness);
+                       Point p1 = d->lp[0] + (ortho * (d->offset + (10.0 * scaledThickness)));
+                       Point p2 = d->lp[1] + (ortho * (d->offset + (10.0 * scaledThickness)));
+                       Point p3 = d->lp[0] + (ortho * (d->offset + (16.0 * scaledThickness)));
+                       Point p4 = d->lp[1] + (ortho * (d->offset + (16.0 * scaledThickness)));
                        Point p5 = d->p[0] + (ortho * 4.0 * scaledThickness);
                        Point p6 = d->p[1] + (ortho * 4.0 * scaledThickness);
 
@@ -555,6 +610,9 @@ void DrawingView::RenderObjects(Painter * painter, std::vector<void *> & v, int
                                        dimText = QString("%1' %2\"").arg(feet).arg(inches);
                        }
 
+/*
+Where is the text offset?  It looks like it's drawing in the center, but obviously it isn't.  It isn't here, it's in Painter::DrawAngledText().
+*/
                        painter->DrawAngledText(ctr, angle, dimText, scaledThickness);
 
                        if (d->hitObject)
@@ -604,36 +662,50 @@ void DrawingView::RenderObjects(Painter * painter, std::vector<void *> & v, int
 
                        break;
                }
+
                case OTText:
                {
                        Text * t = (Text *)obj;
-                       painter->DrawTextObject(t->p[0], t->s.c_str(), scaledThickness);
+
+                       if (t->measured == false)
+                       {
+                               t->extents = painter->MeasureTextObject(t->s.c_str(), scaledThickness);
+                               t->measured = true;
+                       }
+
+                       painter->DrawTextObject(t->p[0], t->s.c_str(), scaledThickness, t->angle[0]);
                        break;
                }
+
                case OTSpline:
                {
                        break;
                }
+
                case OTPolygon:
                {
                        break;
                }
+
                case OTContainer:
                {
                        // Containers require recursive rendering...
                        Container * c = (Container *)obj;
-                       RenderObjects(painter, (*c).objects, layer);
+printf("About to render container: # objs=%i, layer=%i\n", (*c).objects.size(), layer);
+                       RenderObjects(painter, (*c).objects, layer, ignoreLayer);
 
 //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);
+//                             Rect r = GetObjectExtents(obj);
+//                             painter->DrawRectCorners(r);
+                               painter->DrawRectCorners(Rect(c->p[0], c->p[1]));
                        }
 
                        break;
                }
+
                default:
                        break;
                }
@@ -641,44 +713,45 @@ void DrawingView::RenderObjects(Painter * painter, std::vector<void *> & v, int
 }
 
 
+//
+// This toggles the selection being hovered (typically, only 1 object)
+//
 void DrawingView::AddHoveredToSelection(void)
 {
-       std::vector<void *>::iterator i;
-
-       for(i=document.objects.begin(); i!=document.objects.end(); i++)
+       for(VPVectorIter i=document.objects.begin(); i!=document.objects.end(); i++)
        {
                if (((Object *)(*i))->hovered)
-                       ((Object *)(*i))->selected = true;
+//                     ((Object *)(*i))->selected = true;
+                       ((Object *)(*i))->selected = !((Object *)(*i))->selected;
        }
 }
 
 
-void DrawingView::GetSelection(std::vector<void *> & v)
+VPVector DrawingView::GetSelection(void)
 {
-       v.clear();
-       std::vector<void *>::iterator i;
+       VPVector v;
 
-       for(i=document.objects.begin(); i!=document.objects.end(); i++)
+       for(VPVectorIter i=document.objects.begin(); i!=document.objects.end(); i++)
        {
                if (((Object *)(*i))->selected)
                        v.push_back(*i);
        }
+
+       return v;
 }
 
 
-void DrawingView::GetHovered(std::vector<void *> & v)
+VPVector DrawingView::GetHovered(void)
 {
-       v.clear();
-       std::vector<void *>::iterator i;
+       VPVector v;
 
-       for(i=document.objects.begin(); i!=document.objects.end(); i++)
+       for(VPVectorIter 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);
-//             }
        }
+
+       return v;
 }
 
 
@@ -704,6 +777,14 @@ void DrawingView::ToolHandler(int mode, Point p)
                RotateHandler(mode, p);
        else if (Global::tool == TTMirror)
                MirrorHandler(mode, p);
+       else if (Global::tool == TTDimension)
+               DimensionHandler(mode, p);
+       else if (Global::tool == TTTriangulate)
+               TriangulateHandler(mode, p);
+       else if (Global::tool == TTTrim)
+               TrimHandler(mode, p);
+       else if (Global::tool == TTParallel)
+               ParallelHandler(mode, p);
 }
 
 
@@ -826,7 +907,7 @@ void DrawingView::ToolDraw(Painter * painter)
                {
                        if (toolPoint[0] == toolPoint[1])
                                return;
-                       
+
                        Point mirrorPoint = toolPoint[0] + Vector(toolPoint[1], toolPoint[0]);
                        painter->DrawLine(mirrorPoint, toolPoint[1]);
 
@@ -842,6 +923,28 @@ void DrawingView::ToolDraw(Painter * painter)
                                informativeText += " (Copy)";
                }
        }
+       if (Global::tool == TTDimension)
+       {
+               if (Global::toolState == TSNone)
+               {
+                       painter->DrawHandle(toolPoint[0]);
+               }
+               else if ((Global::toolState == TSPoint2) && shiftDown)
+               {
+                       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);
+               }
+       }
 }
 
 
@@ -856,6 +959,7 @@ void DrawingView::LineHandler(int mode, Point p)
                        toolPoint[1] = p;
 
                break;
+
        case ToolMouseMove:
                if (Global::toolState == TSNone)
                        toolPoint[0] = p;
@@ -863,6 +967,7 @@ void DrawingView::LineHandler(int mode, Point p)
                        toolPoint[1] = p;
 
                break;
+
        case ToolMouseUp:
                if (Global::toolState == TSNone)
                {
@@ -878,7 +983,7 @@ void DrawingView::LineHandler(int mode, Point p)
                }
                else
                {
-                       Line * l = new Line(toolPoint[0], toolPoint[1]);
+                       Line * l = new Line(toolPoint[0], toolPoint[1], Global::penWidth, Global::penColor, Global::penStyle);
                        l->layer = Global::activeLayer;
                        document.objects.push_back(l);
                        toolPoint[0] = toolPoint[1];
@@ -898,6 +1003,7 @@ void DrawingView::CircleHandler(int mode, Point p)
                        toolPoint[1] = p;
 
                break;
+
        case ToolMouseMove:
                if (Global::toolState == TSNone)
                        toolPoint[0] = p;
@@ -905,6 +1011,7 @@ void DrawingView::CircleHandler(int mode, Point p)
                        toolPoint[1] = p;
 
                break;
+
        case ToolMouseUp:
                if (Global::toolState == TSNone)
                {
@@ -921,7 +1028,7 @@ void DrawingView::CircleHandler(int mode, Point p)
                else
                {
                        double length = Vector::Magnitude(toolPoint[0], toolPoint[1]);
-                       Circle * c = new Circle(toolPoint[0], length);
+                       Circle * c = new Circle(toolPoint[0], length, Global::penWidth, Global::penColor, Global::penStyle);
                        c->layer = Global::activeLayer;
                        document.objects.push_back(c);
                        toolPoint[0] = toolPoint[1];
@@ -946,6 +1053,7 @@ void DrawingView::ArcHandler(int mode, Point p)
                        toolPoint[3] = p;
 
                break;
+
        case ToolMouseMove:
                if (Global::toolState == TSNone)
                        toolPoint[0] = p;
@@ -963,6 +1071,7 @@ void DrawingView::ArcHandler(int mode, Point p)
                }
 
                break;
+
        case ToolMouseUp:
                if (Global::toolState == TSNone)
                {
@@ -998,7 +1107,7 @@ void DrawingView::ArcHandler(int mode, Point p)
                        if (span < 0)
                                span += TAU;
 
-                       Arc * arc = new Arc(toolPoint[0], toolPoint[1].x, toolPoint[2].x, span);
+                       Arc * arc = new Arc(toolPoint[0], toolPoint[1].x, toolPoint[2].x, span, Global::penWidth, Global::penColor, Global::penStyle);
                        arc->layer = Global::activeLayer;
                        document.objects.push_back(arc);
                        Global::toolState = TSNone;
@@ -1015,7 +1124,8 @@ void DrawingView::RotateHandler(int mode, Point p)
                if (Global::toolState == TSNone)
                {
                        toolPoint[0] = p;
-                       SavePointsFrom(select, toolScratch);
+//                     SavePointsFrom(select, toolScratch);
+                       CopyObjects(select, toolScratch2);
                        Global::toolState = TSPoint1;
                }
                else if (Global::toolState == TSPoint1)
@@ -1024,6 +1134,7 @@ void DrawingView::RotateHandler(int mode, Point p)
                        toolPoint[1] = p;
 
                break;
+
        case ToolMouseMove:
                if ((Global::toolState == TSPoint1) || (Global::toolState == TSNone))
                        toolPoint[0] = p;
@@ -1036,29 +1147,70 @@ void DrawingView::RotateHandler(int mode, Point p)
 
                        angleSnap = true;
                        double angle = Vector(toolPoint[0], toolPoint[1]).Angle();
-                       std::vector<void *>::iterator j = select.begin();
-                       std::vector<Object>::iterator i = toolScratch.begin();
+                       VPVectorIter j = select.begin();
+//                     std::vector<Object>::iterator i = toolScratch.begin();
+                       VPVectorIter i = toolScratch2.begin();
 
-                       for(; i!=toolScratch.end(); i++, j++)
+//                     for(; i!=toolScratch.end(); i++, j++)
+                       for(; i!=toolScratch2.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;
+//                             Object objT = *i;
+//                             Point p1 = Geometry::RotatePointAroundPoint(objT.p[0], toolPoint[0], angle);
+//                             Point p2 = Geometry::RotatePointAroundPoint(objT.p[1], toolPoint[0], angle);
+                               Object * objT = (Object *)(*i);
+                               Object * objS = (Object *)(*j);
 
-                               if (obj.type == OTArc)
+                               Point p1 = Geometry::RotatePointAroundPoint(objT->p[0], toolPoint[0], angle);
+                               Point p2 = Geometry::RotatePointAroundPoint(objT->p[1], toolPoint[0], angle);
+
+                               objS->p[0] = p1;
+                               objS->p[1] = p2;
+
+//                             if (objT.type == OTArc || objT.type == OTText)
+                               if (objT->type == OTArc || objT->type == OTText)
                                {
-                                       obj2->angle[0] = obj.angle[0] + angle;
+//                                     objS->angle[0] = objT.angle[0] + angle;
+                                       objS->angle[0] = objT->angle[0] + angle;
 
-                                       if (obj2->angle[0] > TAU)
-                                               obj2->angle[0] -= TAU;
+                                       if (objS->angle[0] > TAU)
+                                               objS->angle[0] -= TAU;
+                               }
+//                             else if (objT.type == OTContainer)
+                               else if (objT->type == OTContainer)
+                               {
+                                       // OK, this doesn't work because toolScratch only has points and nothing in the containers... [ACTUALLY... toolScratch is is a vector of type Object... which DOESN'T have an objects vector in it...]
+//                                     Container * c = (Container *)&objT;
+                                       Container * c = (Container *)objT;
+                                       Container * c2 = (Container *)objS;
+                                       VPVectorIter l = c->objects.begin();
+                                       // TODO: Rotate items in the container
+                                       // TODO: Make this recursive
+                                       for(VPVectorIter k=c2->objects.begin(); k!=c2->objects.end(); k++, l++)
+                                       {
+                                               Object * obj3 = (Object *)(*k);
+                                               Object * obj4 = (Object *)(*l);
+
+                                               p1 = Geometry::RotatePointAroundPoint(obj4->p[0], toolPoint[0], angle);
+                                               p2 = Geometry::RotatePointAroundPoint(obj4->p[1], toolPoint[0], angle);
+
+                                               obj3->p[0] = p1;
+                                               obj3->p[1] = p2;
+//                                             obj3->angle[0] = objT.angle[0] + angle;
+                                               obj3->angle[0] = obj4->angle[0] + angle;
+
+                                               if (obj3->angle[0] > TAU)
+                                                       obj3->angle[0] -= TAU;
+                                       }
+
+                                       Rect r = GetObjectExtents(objS);
+                                       c2->p[0] = r.TopLeft();
+                                       c2->p[1] = r.BottomRight();
                                }
                        }
                }
 
                break;
+
        case ToolMouseUp:
                if (Global::toolState == TSPoint1)
                {
@@ -1078,34 +1230,43 @@ void DrawingView::RotateHandler(int mode, Point p)
                        if (ctrlDown)
                        {
                                // Stamp a copy of the selection at the current rotation & bail
-                               std::vector<void *> temp;
+                               VPVector temp;
                                CopyObjects(select, temp);
                                ClearSelected(temp);
                                AddObjectsTo(document.objects, temp);
-                               RestorePointsTo(select, toolScratch);
+//                             RestorePointsTo(select, toolScratch);
+                               RestorePointsTo(select, toolScratch2);
                                return;
                        }
 
                        toolPoint[0] = p;
                        Global::toolState = TSPoint1;
-                       SavePointsFrom(select, toolScratch);
+//                     SavePointsFrom(select, toolScratch);
+                       DeleteContents(toolScratch2);
+                       CopyObjects(select, toolScratch2);
                }
 
                break;
+
        case ToolKeyDown:
                // Reset the selection if shift held down...
                if (shiftDown)
-                       RestorePointsTo(select, toolScratch);
+//                     RestorePointsTo(select, toolScratch);
+                       RestorePointsTo(select, toolScratch2);
 
                break;
+
        case ToolKeyUp:
                // Reset selection when key is let up
                if (!shiftDown)
                        RotateHandler(ToolMouseMove, toolPoint[1]);
 
                break;
+
        case ToolCleanup:
-               RestorePointsTo(select, toolScratch);
+//             RestorePointsTo(select, toolScratch);
+               RestorePointsTo(select, toolScratch2);
+               DeleteContents(toolScratch2);
        }
 }
 
@@ -1127,6 +1288,7 @@ void DrawingView::MirrorHandler(int mode, Point p)
                        toolPoint[1] = p;
 
                break;
+
        case ToolMouseMove:
                if ((Global::toolState == TSPoint1) || (Global::toolState == TSNone))
                        toolPoint[0] = p;
@@ -1139,7 +1301,7 @@ void DrawingView::MirrorHandler(int mode, Point p)
 
                        angleSnap = true;
                        double angle = Vector(toolPoint[0], toolPoint[1]).Angle();
-                       std::vector<void *>::iterator j = select.begin();
+                       VPVectorIter j = select.begin();
                        std::vector<Object>::iterator i = toolScratch.begin();
 
                        for(; i!=toolScratch.end(); i++, j++)
@@ -1151,6 +1313,11 @@ void DrawingView::MirrorHandler(int mode, Point p)
                                obj2->p[0] = p1;
                                obj2->p[1] = p2;
 
+/*
+N.B.: When mirroring an arc thru a horizontal axis, this causes the arc to have
+      a negative start angle which makes it impossible to interact with.
+      !!! FIX !!!
+*/
                                if (obj.type == OTArc)
                                {
                                        // This is 2*mirror angle - obj angle - obj span
@@ -1163,6 +1330,7 @@ void DrawingView::MirrorHandler(int mode, Point p)
                }
 
                break;
+
        case ToolMouseUp:
                if (Global::toolState == TSPoint1)
                {
@@ -1182,7 +1350,7 @@ void DrawingView::MirrorHandler(int mode, Point p)
                        if (ctrlDown)
                        {
                                // Stamp a copy of the selection at the current rotation & bail
-                               std::vector<void *> temp;
+                               VPVector temp;
                                CopyObjects(select, temp);
                                ClearSelected(temp);
                                AddObjectsTo(document.objects, temp);
@@ -1196,28 +1364,330 @@ void DrawingView::MirrorHandler(int mode, Point p)
                }
 
                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::DimensionHandler(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
+               {
+                       Dimension * d = new Dimension(toolPoint[0], toolPoint[1], DTLinear);
+                       d->layer = Global::activeLayer;
+                       document.objects.push_back(d);
+                       Global::toolState = TSNone;
+               }
+       }
+}
+
+
+void DrawingView::TriangulateHandler(int mode, Point/*p*/)
+{
+       switch (mode)
+       {
+       case ToolMouseDown:
+       {
+               // Skip if nothing hovered...
+               if (numHovered != 1)
+                       break;
+
+               VPVector hover = GetHovered();
+               Object * obj = (Object *)hover[0];
+
+               // Skip if it's not a line...
+               if (obj->type != OTLine)
+                       break;
+
+               if (Global::toolState == TSNone)
+                       toolObj[0] = obj;
+               else if (Global::toolState == TSPoint2)
+                       toolObj[1] = obj;
+               else
+                       toolObj[2] = obj;
+
+               break;
+       }
+#if 0
+       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;
+                       angleSnap = true;
+               }
+               else
+               {
+                       toolPoint[3] = p;
+                       angleSnap = true;
+               }
+
+               break;
+#endif
+       case ToolMouseUp:
+               if (Global::toolState == TSNone)
+               {
+                       Global::toolState = TSPoint2;
+               }
+               else if (Global::toolState == TSPoint2)
+               {
+/*                     if (shiftDown)
+                       {
+                               // Key override is telling us to start arc at new center, not
+                               // continue the current one.
+                               toolPoint[0] = toolPoint[1];
+                               return;
+                       }*/
+
+                       Global::toolState = TSPoint3;
+               }
+               else
+               {
+                       double len2 = Vector::Magnitude(toolObj[1]->p[0], toolObj[1]->p[1]);
+                       double len3 = Vector::Magnitude(toolObj[2]->p[0], toolObj[2]->p[1]);
+
+                       Circle c1(toolObj[0]->p[0], len2);
+                       Circle c2(toolObj[0]->p[1], len3);
+
+                       Geometry::CheckCircleToCircleIntersection((Object *)&c1, (Object *)&c2);
+
+                       // Only move lines if the triangle formed by them is not degenerate
+                       if (Global::numIntersectPoints > 0)
+                       {
+                               toolObj[1]->p[0] = toolObj[0]->p[0];
+                               toolObj[1]->p[1] = Global::intersectPoint[0];
+
+                               toolObj[2]->p[0] = Global::intersectPoint[0];
+                               toolObj[2]->p[1] = toolObj[0]->p[1];
+                       }
+
+                       Global::toolState = TSNone;
+               }
+       }
+}
+
+
+void DrawingView::TrimHandler(int mode, Point p)
+{
+/*
+n.b.: this code is lifted straight out of the old oo code.  needs to be updated.
+*/
+       switch (mode)
+       {
+       case ToolMouseDown:
+       {
+#if 0
+               Object * toTrim = doc->lastObjectHovered;
+
+               if (toTrim == NULL)
+                       return;
+
+               Vector v(((Line *)toTrim)->position, ((Line *)toTrim)->endpoint);
+
+               // Check to see which case we have...
+               // We're trimming point #1...
+               if (t == 0)
+               {
+                       ((Line *)toTrim)->position = ((Line *)toTrim)->position + (v * u);
+               }
+               else if (u == 1.0)
+               {
+                       ((Line *)toTrim)->endpoint = ((Line *)toTrim)->position + (v * t);
+               }
+               else
+               {
+                       Point p1 = ((Line *)toTrim)->position + (v * t);
+                       Point p2 = ((Line *)toTrim)->position + (v * u);
+                       Point p3 = ((Line *)toTrim)->endpoint;
+                       ((Line *)toTrim)->endpoint = p1;
+                       Line * line = new Line(p2, p3);
+                       emit ObjectReady(line);
+               }
+
+               doc->lastObjectHovered = NULL;
+#endif
+       }
+               break;
+
+       case ToolMouseMove:
+       {
+#if 0
+               Object * toTrim = doc->lastObjectHovered;
+               t = 0, u = 1.0;
+
+               if (toTrim == NULL)
+                       return;
+
+               if (toTrim->type != OTLine)
+                       return;
+
+               double pointHoveredT = Geometry::ParameterOfLineAndPoint(((Line *)toTrim)->position, ((Line *)toTrim)->endpoint, point);
+
+               std::vector<Object *>::iterator i;
+
+               for(i=doc->objects.begin(); i!=doc->objects.end(); i++)
+               {
+                       // Can't trim against yourself... :-P
+                       if (*i == toTrim)
+                               continue;
+
+                       Object * trimAgainst = *i;
+                       double t1;//, u1;
+
+                       if ((toTrim->type != OTLine) || (trimAgainst->type != OTLine))
+                               continue;
+
+                       int intersects = Geometry::Intersects((Line *)toTrim, (Line *)trimAgainst, &t1);//, &u1);
+
+                       if (intersects)
+                       {
+                               // Now what? We don't know which side to trim!
+                               // ... now we do, we know which side of the Line we're on!
+                               if ((t1 > t) && (t1 < pointHoveredT))
+                                       t = t1;
+
+                               if ((t1 < u) && (t1 > pointHoveredT))
+                                       u = t1;
+                       }
+               }
+#endif
+               // Bail out if nothing hovered...
+               if (numHovered != 1)
+               {
+                       toolObj[0] = NULL;
+                       return;
+               }
+
+               VPVector hover = GetHovered();
+               Object * obj = (Object *)hover[0];
+
+               // Skip if it's not a line...
+               if (obj->type != OTLine)
+               {
+                       toolObj[0] = NULL;
+                       return;
+               }
+
+               toolObj[0] = obj;
+               double hoveredParam = Geometry::ParameterOfLineAndPoint(obj->p[0], obj->p[1], p);
+
+               // Currently only deal with line against line trimming, can expand to
+               // others as well (line/circle, circle/circle, line/arc, etc)
+               VPVectorIter i;
+               for(i=document.objects.begin(); i!=document.objects.end(); i++)
+               {
+                       obj = (Object *)(*i);
+
+                       if (obj == toolObj[0])
+                               continue;
+                       else if (obj->type != OTLine)
+                               continue;
+
+                       Geometry::CheckLineToLineIntersection(toolObj[0], obj);
+
+                       if (Global::numIntersectParams > 0)
+                       {
+                               // Mark the line segment somehow (the side the mouse is on) so that it can be drawn & trimmed when we hit ToolMouseDown.
+                       }
+               }
+       }
+               break;
+
+       case ToolMouseUp:
+               break;
+
+       case ToolKeyDown:
+               break;
+
+       case ToolKeyUp:
+               break;
+
+       case ToolCleanup:
+               break;
+       }
+}
+
+
+void DrawingView::ParallelHandler(int mode, Point /*p*/)
+{
+       switch (mode)
+       {
+       case ToolMouseDown:
+               break;
+
+       case ToolMouseMove:
+               break;
+
+       case ToolMouseUp:
+               break;
+
+       case ToolKeyDown:
+               break;
+
+       case ToolKeyUp:
+               break;
+
+       case ToolCleanup:
+               break;
+       }
+}
+
+
 void DrawingView::mousePressEvent(QMouseEvent * event)
 {
        if (event->button() == Qt::LeftButton)
        {
+//printf("mousePressEvent::Qt::LeftButton numHovered=%li\n", numHovered);
                Vector point = Painter::QtToCartesianCoords(Vector(event->x(), event->y()));
 
                // Handle tool processing, if any
@@ -1225,6 +1695,8 @@ void DrawingView::mousePressEvent(QMouseEvent * event)
                {
                        if (hoveringIntersection)
                                point = intersectionPoint;
+                       else if (hoverPointValid)
+                               point = hoverPoint;
                        else if (Global::snapToGrid)
                                point = SnapPointToGrid(point);
 
@@ -1249,9 +1721,14 @@ void DrawingView::mousePressEvent(QMouseEvent * event)
                {
                        AddHoveredToSelection();
                        update();       // needed??
-                       GetHovered(hover);      // prolly needed
-                       dragged = (Object *)hover[0];
+//                     GetHovered(hover);      // prolly needed
+                       VPVector hover2 = GetHovered();
+                       dragged = (Object *)hover2[0];
                        draggingObject = true;
+//printf("mousePressEvent::numHovered > 0 (hover2[0]=$%llx, type=%s)\n", dragged, objName[dragged->type]);
+
+                       // Alert the pen widget
+                       emit ObjectSelected(dragged);
 
                        // See if anything is using just a straight click on a handle
                        if (HandleObjectClicked())
@@ -1265,16 +1742,35 @@ void DrawingView::mousePressEvent(QMouseEvent * event)
                        // We do it *after*... why? (doesn't seem to confer any advantage...)
                        if (hoveringIntersection)
                                oldPoint = intersectionPoint;
+                       else if (hoverPointValid)
+                               oldPoint = hoverPoint;
                        else if (Global::snapToGrid)
                                oldPoint = SnapPointToGrid(point);
 
+                       // Needed for fixed length handling
+                       if (Global::fixedLength)
+                       {
+                               if (dragged->type == OTLine)
+                               {
+                                       dragged->length = Vector::Magnitude(dragged->p[0], dragged->p[1]);
+                               }
+                       }
+
+                       if (dragged->type == OTCircle)
+                       {
+                               // Save for informative text, uh, er, informing
+                               dragged->length = dragged->radius[0];
+                       }
+
                        return;
                }
 
-               // Didn't hit any object and not using a tool, so do a selection rectangle
+               // 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));
+               select = GetSelection();
        }
        else if (event->button() == Qt::MiddleButton)
        {
@@ -1288,14 +1784,22 @@ void DrawingView::mousePressEvent(QMouseEvent * event)
 
 void DrawingView::mouseMoveEvent(QMouseEvent * event)
 {
+       // It seems that wheelEvent() triggers this for some reason...
+       if (scrollWheelSeen)
+       {
+               scrollWheelSeen = false;
+               return;
+       }
+
        Vector point = Painter::QtToCartesianCoords(Vector(event->x(), event->y()));
        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;
+       oldScrollPoint = Vector(event->x(), event->y());
 
        // Scrolling...
-       if (event->buttons() & Qt::MiddleButton)
+       if ((event->buttons() & Qt::MiddleButton) || scrollDrag)
        {
                point = Vector(event->x(), event->y());
                // Since we're using Qt coords for scrolling, we have to adjust them
@@ -1317,19 +1821,35 @@ void DrawingView::mouseMoveEvent(QMouseEvent * event)
        if (Global::selectionInProgress)
        {
                CheckObjectBounds();
+
+               // Make sure previously selected objects stay selected (CTRL held)
+               for(VPVectorIter i=select.begin(); i!=select.end(); i++)
+                       ((Object *)(*i))->selected = true;
+
                update();
                return;
        }
 
        // Do object hit testing...
        bool needUpdate = HitTestObjects(point);
-       GetHovered(hover);
+       VPVector hover2 = GetHovered();
+#if 0
+{
+if (needUpdate)
+{
+       printf("mouseMoveEvent:: numHovered=%li, hover2.size()=%li\n", numHovered, hover2.size());
+
+       if (hover2.size() > 0)
+               printf("                 (hover2[0]=$%llX, type=%s)\n", hover2[0], objName[((Object *)hover2[0])->type]);
+}
+}
+#endif
 
        // Check for multi-hover...
        if (numHovered > 1)
        {
 //need to check for case where hover is over 2 circles and a 3rd's center...
-               Object * obj1 = (Object *)hover[0], * obj2 = (Object *)hover[1];
+               Object * obj1 = (Object *)hover2[0], * obj2 = (Object *)hover2[1];
 
                Geometry::Intersects(obj1, obj2);
                int numIntersecting = Global::numIntersectParams;
@@ -1367,6 +1887,29 @@ void DrawingView::mouseMoveEvent(QMouseEvent * event)
                        intersectionPoint = v1;
                }
        }
+       else if (numHovered == 1)
+       {
+               Object * obj = (Object *)hover2[0];
+
+               if (obj->type == OTLine)
+               {
+/*
+Not sure that this is the best way to handle this, but it works(TM)...
+Except when lines are overlapping, then it doesn't work... !!! FIX !!!
+*/
+                       Point midpoint = Geometry::Midpoint((Line *)obj);
+                       Vector v1 = Vector::Magnitude(midpoint, point);
+
+                       if ((v1.Magnitude() * Global::zoom) < 8.0)
+                       {
+                               QString text = tr("Midpoint <%1, %2>");
+                               informativeText = text.arg(midpoint.x).arg(midpoint.y);
+                               hoverPointValid = true;
+                               hoverPoint = midpoint;
+                               needUpdate = true;
+                       }
+               }
+       }
 
        // Handle object movement (left button down & over an object)
        if ((event->buttons() & Qt::LeftButton) && draggingObject && !Global::tool)
@@ -1429,23 +1972,17 @@ void DrawingView::mouseReleaseEvent(QMouseEvent * event)
                        return;
                }
 
-               if (Global::selectionInProgress)
+//             if (Global::selectionInProgress)
                        Global::selectionInProgress = false;
 
                informativeText.clear();
 // Should we be doing this automagically? Hmm...
                // Clear our vectors
-               select.clear();
-               hover.clear();
+//             select.clear();
+////           hover.clear();
 
-               // 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);
-               }
+               // Scoop 'em up (do we need to??? Seems we do, because keyboard movement uses it.  Also, tools use it too.  But we can move it out of here)
+               select = GetSelection();
 
                draggingObject = false;
        }
@@ -1460,26 +1997,31 @@ void DrawingView::mouseReleaseEvent(QMouseEvent * event)
 void DrawingView::wheelEvent(QWheelEvent * event)
 {
        double zoomFactor = 1.25;
-       QSize sizeWin = size();
-       Vector center(sizeWin.width() / 2.0, sizeWin.height() / 2.0);
-       center = Painter::QtToCartesianCoords(center);
+       scrollWheelSeen = true;
 
-       // This is not centering for some reason. Need to figure out why. :-/
-       if (event->delta() > 0)
+       if (event->delta() < 0)
        {
-               Vector newOrigin = center - ((center - Global::origin) / zoomFactor);
-               Global::origin = newOrigin;
+               if (Global::zoom > 20.0)
+                       return;
+
                Global::zoom *= zoomFactor;
+//             Point np = Painter::QtToCartesianCoords(oldScrollPoint);
+//             Global::origin += (oldPoint - np);
        }
        else
        {
-               Vector newOrigin = center + ((-center + Global::origin) * zoomFactor);
-               Global::origin = newOrigin;
+               if (Global::zoom < 0.25)
+                       return;
+
                Global::zoom /= zoomFactor;
+//             Point np = Painter::QtToCartesianCoords(oldScrollPoint);
+//             Global::origin += (oldPoint - np);
        }
 
+       Point np = Painter::QtToCartesianCoords(oldScrollPoint);
+       Global::origin += (oldPoint - np);
+
 //     Global::gridSpacing = gridPixels / Painter::zoom;
-//     UpdateGridBackground();
        SetGridSize(Global::gridSpacing * Global::zoom);
        update();
 //     zoomIndicator->setText(QString("Grid: %1\", BU: Inch").arg(Global::gridSpacing));
@@ -1490,12 +2032,17 @@ void DrawingView::keyPressEvent(QKeyEvent * event)
 {
        bool oldShift = shiftDown;
        bool oldCtrl = ctrlDown;
+       bool oldAlt = altDown;
 
        if (event->key() == Qt::Key_Shift)
                shiftDown = true;
        else if (event->key() == Qt::Key_Control)
                ctrlDown = true;
+       else if (event->key() == Qt::Key_Alt)
+               altDown = true;
 
+       // If there's a change in any of the modifier key states, pass it on to
+       // the current tool's handler
        if ((oldShift != shiftDown) || (oldCtrl != ctrlDown))
        {
                if (Global::tool)
@@ -1504,6 +2051,13 @@ void DrawingView::keyPressEvent(QKeyEvent * event)
                update();
        }
 
+       if (oldAlt != altDown)
+       {
+               scrollDrag = true;
+               setCursor(Qt::SizeAllCursor);
+               oldPoint = oldScrollPoint;
+       }
+
        if (select.size() > 0)
        {
                if (event->key() == Qt::Key_Up)
@@ -1534,11 +2088,14 @@ void DrawingView::keyReleaseEvent(QKeyEvent * event)
 {
        bool oldShift = shiftDown;
        bool oldCtrl = ctrlDown;
+       bool oldAlt = altDown;
 
        if (event->key() == Qt::Key_Shift)
                shiftDown = false;
        else if (event->key() == Qt::Key_Control)
                ctrlDown = false;
+       else if (event->key() == Qt::Key_Alt)
+               altDown = false;
 
        if ((oldShift != shiftDown) || (oldCtrl != ctrlDown))
        {
@@ -1547,6 +2104,12 @@ void DrawingView::keyReleaseEvent(QKeyEvent * event)
 
                update();
        }
+
+       if (oldAlt != altDown)
+       {
+               scrollDrag = false;
+               setCursor(Qt::ArrowCursor);
+       }
 }
 
 
@@ -1601,12 +2164,14 @@ Rect DrawingView::GetObjectExtents(Object * obj)
                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;
@@ -1643,19 +2208,27 @@ Rect DrawingView::GetObjectExtents(Object * obj)
 
                rect *= a->radius[0];
                rect.Translate(a->p[0]);
+               break;
+       }
 
+       case OTText:
+       {
+               Text * t = (Text *)obj;
+               rect = Rect(t->p[0], Point(t->p[0].x + t->extents.Width(), t->p[0].y - t->extents.Height()));
                break;
        }
+
        case OTContainer:
        {
                Container * c = (Container *)obj;
-               std::vector<void *>::iterator i = c->objects.begin();
+               VPVectorIter i = c->objects.begin();
                rect = GetObjectExtents((Object *)*i);
                i++;
 
                for(; i!=c->objects.end(); i++)
                        rect |= GetObjectExtents((Object *)*i);
        }
+
        default:
                break;
        }
@@ -1666,7 +2239,7 @@ Rect DrawingView::GetObjectExtents(Object * obj)
 
 void DrawingView::CheckObjectBounds(void)
 {
-       std::vector<void *>::iterator i;
+       VPVectorIter i;
 
        for(i=document.objects.begin(); i!=document.objects.end(); i++)
        {
@@ -1685,6 +2258,7 @@ void DrawingView::CheckObjectBounds(void)
 
                        break;
                }
+
                case OTCircle:
                {
                        Circle * c = (Circle *)obj;
@@ -1694,6 +2268,7 @@ void DrawingView::CheckObjectBounds(void)
 
                        break;
                }
+
                case OTArc:
                {
                        Arc * a = (Arc *)obj;
@@ -1724,7 +2299,8 @@ void DrawingView::CheckObjectBounds(void)
                        bounds = bounds.normalized();
 #endif
 
-                       // If the end of the arc is before the beginning, add 360 degrees to it
+                       // If the end of the arc is before the beginning, add 360 degrees
+                       // to it
                        if (end < start)
                                end += TAU;
 
@@ -1759,16 +2335,38 @@ void DrawingView::CheckObjectBounds(void)
 
                        break;
                }
-               default:
+
+               case OTText:
+               {
+                       Text * t = (Text *)obj;
+                       Rect r(obj->p[0], Point(t->p[0].x + t->extents.Width(), t->p[0].y - t->extents.Height()));
+
+                       if (Global::selection.contains(r.l, r.t) && Global::selection.contains(r.r, r.b))
+                               t->selected = true;
+
+                       break;
+               }
+
+               case OTContainer:
+               {
+                       Container * c = (Container *)obj;
+
+                       if (Global::selection.contains(c->p[0].x, c->p[0].y) && Global::selection.contains(c->p[1].x, c->p[1].y))
+                               c->selected = true;
+
                        break;
                }
+
+//             default:
+//                     break;
+               }
        }
 }
 
 
 bool DrawingView::HitTestObjects(Point point)
 {
-       std::vector<void *>::iterator i;
+       VPVectorIter i;
        numHovered = 0;
        bool needUpdate = false;
        hoverPointValid = false;
@@ -1781,14 +2379,14 @@ bool DrawingView::HitTestObjects(Point point)
                if (draggingObject && (obj == dragged))
                        continue;
 
-               if (HitTest(obj, point))
+               if (HitTest(obj, point) == true)
                        needUpdate = true;
 
                if (obj->hovered)
                {
                        numHovered++;
 //printf("MouseMove: OBJECT HOVERED (numHovered = %i)\n", numHovered);
-                       emit(ObjectHovered(obj));
+                       emit ObjectHovered(obj);
                }
        }
 
@@ -1843,6 +2441,7 @@ bool DrawingView::HitTest(Object * obj, Point point)
 
                break;
        }
+
        case OTCircle:
        {
                bool oldHP = obj->hitPoint[0], oldHO = obj->hitObject;
@@ -1850,7 +2449,11 @@ bool DrawingView::HitTest(Object * obj, Point point)
                double length = Vector::Magnitude(obj->p[0], point);
 
                if ((length * Global::zoom) < 8.0)
+               {
                        obj->hitPoint[0] = true;
+                       hoverPoint = obj->p[0];
+                       hoverPointValid = true;
+               }
                else if ((fabs(length - obj->radius[0]) * Global::zoom) < 2.0)
                        obj->hitObject = true;
 
@@ -1861,6 +2464,7 @@ bool DrawingView::HitTest(Object * obj, Point point)
 
                break;
        }
+
        case OTArc:
        {
                bool oldHP0 = obj->hitPoint[0], oldHP1 = obj->hitPoint[1], oldHP2 = obj->hitPoint[2], oldHO = obj->hitObject;
@@ -1883,7 +2487,11 @@ bool DrawingView::HitTest(Object * obj, Point point)
                double length3 = Vector::Magnitude(point, handle2);
 
                if ((length * Global::zoom) < 8.0)
+               {
                        obj->hitPoint[0] = true;
+                       hoverPoint = obj->p[0];
+                       hoverPointValid = true;
+               }
                else if ((length2 * Global::zoom) < 8.0)
                {
                        obj->hitPoint[1] = true;
@@ -1906,6 +2514,7 @@ bool DrawingView::HitTest(Object * obj, Point point)
 
                break;
        }
+
        case OTDimension:
        {
                bool oldHP0 = obj->hitPoint[0], oldHP1 = obj->hitPoint[1], oldHP2 = obj->hitPoint[2], oldHP3 = obj->hitPoint[3], oldHP4 = obj->hitPoint[4], oldHO = obj->hitObject;
@@ -1916,8 +2525,13 @@ bool DrawingView::HitTest(Object * obj, Point point)
                Vector orthogonal = Vector::Normal(d->lp[0], d->lp[1]);
                // Get our line parallel to our points
                float scaledThickness = Global::scale * obj->thickness;
-               Point p1 = d->lp[0] + (orthogonal * 10.0 * scaledThickness);
-               Point p2 = d->lp[1] + (orthogonal * 10.0 * scaledThickness);
+#if 1
+               Point p1 = d->lp[0] + (orthogonal * (d->offset + (10.0 * scaledThickness)));
+               Point p2 = d->lp[1] + (orthogonal * (d->offset + (10.0 * scaledThickness)));
+#else
+               Point p1 = d->lp[0] + (orthogonal * (10.0 + d->offset) * scaledThickness);
+               Point p2 = d->lp[1] + (orthogonal * (10.0 + d->offset) * scaledThickness);
+#endif
                Point p3(p1, point);
 
                Vector v1(d->p[0], point);
@@ -1953,39 +2567,111 @@ bool DrawingView::HitTest(Object * obj, Point point)
                else if ((hCS2Point.Magnitude() * Global::zoom) < 8.0)
                        obj->hitPoint[4] = true;
 
-//             return (hitPoint1 || hitPoint2 || hitLine || hitFlipSwitch || hitChangeSwitch1 || hitChangeSwitch2 ? true : false);
                obj->hovered = (obj->hitPoint[0] || obj->hitPoint[1] || obj->hitPoint[2] || obj->hitPoint[3] || obj->hitPoint[4] || obj->hitObject ? true : false);
 
                if ((oldHP0 != obj->hitPoint[0]) || (oldHP1 != obj->hitPoint[1]) || (oldHP2 != obj->hitPoint[2]) || (oldHP3 != obj->hitPoint[3]) || (oldHP4 != obj->hitPoint[4]) || (oldHO != obj->hitObject))
                        needUpdate = true;
 
+               break;
+       }
+
+       case OTText:
+       {
+               Text * t = (Text *)obj;
+               bool oldHO = obj->hitObject;
+               obj->hitObject = false;
+
+               Rect r(obj->p[0], Point(obj->p[0].x + t->extents.Width(), obj->p[0].y - t->extents.Height()));
+//printf("Text: p=<%lf, %lf>, w/h=%lf, %lf [lrtb=%lf, %lf, %lf, %lf]\n", obj->p[0].x, obj->p[0].y, t->extents.Width(), t->extents.Height(), t->extents.l, t->extents.r, t->extents.t, t->extents.b);
+
+               if (r.Contains(point))
+                       obj->hitObject = true;
+
+               obj->hovered = (obj->hitObject ? true : false);
+
+               if (oldHO != obj->hitObject)
+                       needUpdate = true;
 
                break;
        }
+
        case OTContainer:
        {
-               // Containers must be recursively tested...
+               // Containers must be recursively tested...  Or do they???
+/*
+So the idea here is to flatten the structure, *then* test the objects within.  Flattening includes the Containers as well; we can do this because it's pointers all the way down.
+*/
+//             bool oldHitObj = c->hitObject, oldHovered = c->hovered;
+//             Object * oldClicked = c->clicked;
+/*
+still need to compare old state to new state, and set things up based upon that...
+likely we can just rely on the object itself and steal its state like we have in the commented out portion below; can prolly rewrite the HitTest() portion to be one line: needUpdate = HitTest(cObj, point);
+Well, you could if there was only one object in the Container.  But since there isn't, we have to keep the if HitTest() == true then needUpdate = true bit.  Because otherwise, a false result anywhere will kill the needed update elsewhere.
+*/
                Container * c = (Container *)obj;
                c->hitObject = false;
                c->hovered = false;
-               std::vector<void *>::iterator i;
+               c->clicked = NULL;
 
-               for(i=c->objects.begin(); i!=c->objects.end(); i++)
+               VPVector flat = Flatten(c);
+
+//printf("HitTest::OTContainer (size=%li)\n", flat.size());
+               for(VPVectorIter i=flat.begin(); i!=flat.end(); i++)
                {
-                       Object * cObj = (Object *)*i;
+                       Object * cObj = (Object *)(*i);
+
+                       // Skip the flattened containers (if any)...
+                       if (cObj->type == OTContainer)
+                               continue;
 
-                       if (HitTest(cObj, point))
+                       // We do it this way instead of needUpdate = HitTest() because we
+                       // are checking more than one object, and that way of doing will
+                       // not return consistent results.
+                       if (HitTest(cObj, point) == true)
+//                     {
+//printf("HitTest::OTContainer, subobj ($%llX) hit!\n", cObj);
                                needUpdate = true;
+//                             c->hitObject = true;
+//                             c->clicked = cObj;
+//                             c->hovered = true;
+//                     }
 
+                       // Same reasons for doing it this way here apply.
                        if (cObj->hitObject == true)
+                       {
+//printf("HitTest::cObj->hitObject == true! ($%llX)\n", cObj);
                                c->hitObject = true;
+                               c->clicked = cObj;
+                       }//*/
+
+                       if (cObj->hitPoint[0] == true)
+                       {
+//printf("HitTest::cObj->hitObject == true! ($%llX)\n", cObj);
+                               c->hitPoint[0] = true;
+                               c->clicked = cObj;
+                       }//*/
+
+                       if (cObj->hitPoint[1] == true)
+                       {
+//printf("HitTest::cObj->hitObject == true! ($%llX)\n", cObj);
+                               c->hitPoint[1] = true;
+                               c->clicked = cObj;
+                       }//*/
+
+                       if (cObj->hitPoint[2] == true)
+                       {
+//printf("HitTest::cObj->hitObject == true! ($%llX)\n", cObj);
+                               c->hitPoint[2] = true;
+                               c->clicked = cObj;
+                       }//*/
 
                        if (cObj->hovered == true)
-                               c->hovered = true;
+                               c->hovered = true;//*/
                }
 
                break;
        }
+
        default:
                break;
        }
@@ -2051,9 +2737,27 @@ void DrawingView::HandleObjectMovement(Point point)
        {
        case OTLine:
                if (obj->hitPoint[0])
+               {
+                       if (Global::fixedLength)
+                       {
+                               Vector line = point - obj->p[1];
+                               Vector unit = line.Unit();
+                               point = obj->p[1] + (unit * obj->length);
+                       }
+
                        obj->p[0] = point;
+               }
                else if (obj->hitPoint[1])
+               {
+                       if (Global::fixedLength)
+                       {
+                               Vector line = point - obj->p[0];
+                               Vector unit = line.Unit();
+                               point = obj->p[0] + (unit * obj->length);
+                       }
+
                        obj->p[1] = point;
+               }
                else if (obj->hitObject)
                {
                        obj->p[0] += delta;
@@ -2067,12 +2771,11 @@ void DrawingView::HandleObjectMovement(Point point)
                        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];
+                       double oldRadius = obj->length;
                        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);
+                       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;
@@ -2153,16 +2856,42 @@ void DrawingView::HandleObjectMovement(Point point)
                        obj->p[1] = point;
                else if (obj->hitObject)
                {
-                       obj->p[0] += delta;
-                       obj->p[1] += delta;
+                       // Move measurement lines in/out
+                       if (shiftDown)
+                       {
+                               Dimension * d = (Dimension *)obj;
+                               double dist = Geometry::DistanceToLineFromPoint(d->lp[0], d->lp[1], point);
+                               float scaledThickness = Global::scale * obj->thickness;
+                               // Looks like offset is 0 to +MAX, but line is at 10.0.  So
+                               // anything less than 10.0 should set the offset to 0.
+                               d->offset = 0;
+
+                               if (dist > (10.0 * scaledThickness))
+                                       d->offset = dist - (10.0 * scaledThickness);
+                       }
+                       else
+                       {
+                               obj->p[0] += delta;
+                               obj->p[1] += delta;
+                       }
                }
 
                break;
 
+       case OTText:
+               if (obj->hitObject)
+                       obj->p[0] += delta;
+
+               break;
+
        case OTContainer:
                // This is shitty, but works for now until I can code up something
                // nicer :-)
-               TranslateObject(obj, delta);
+/*
+The idea is to make it so whichever point on the object in question is being dragged becomes the snap point for the container; shouldn't be too difficult to figure out how to do this.
+*/
+//             TranslateObject(obj, delta);
+               TranslateContainer((Container *)obj, point, delta);
 
                break;
        default:
@@ -2170,3 +2899,24 @@ void DrawingView::HandleObjectMovement(Point point)
        }
 }
 
+
+void DrawingView::AddDimensionTo(void * o)
+{
+       Object * obj = (Object *)o;
+
+       switch (obj->type)
+       {
+       case OTLine:
+               document.Add(new Dimension(obj->p[0], obj->p[1]));
+               break;
+       case OTCircle:
+               break;
+       case OTEllipse:
+               break;
+       case OTArc:
+               break;
+       case OTSpline:
+               break;
+       }
+}
+