]> Shamusworld >> Repos - architektonas/blobdiff - src/drawingview.cpp
Added pen and dropper tools, and printing support.
[architektonas] / src / drawingview.cpp
index 1fd0482a83d266c0e313081f878ded884655b649..5b0eeeef6b214c5d4ef4b67f8f87b6af9c2c195c 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...
 #include "global.h"
 #include "mathconstants.h"
 #include "painter.h"
+#include "penwidget.h"
 #include "structs.h"
 #include "utils.h"
 
-
 #define BACKGROUND_MAX_SIZE    512
 
-
 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)
+       scale(1.0), offsetX(-10), offsetY(-10), supressSelected(false),
+       document(true),
+       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);
 
+       curMarker = QCursor(QPixmap(":/res/cursor-marker.png"), 1, 18);
+       curDropper = QCursor(QPixmap(":/res/cursor-dropper.png"), 1, 20);
+
        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
@@ -139,16 +123,82 @@ need a thickness parameter similar to the "size" param for dimensions. (And now
 we do! :-)
 
 */
-       SetGridSize(12);        // This is in pixels
+//     gridPixels = 12; //tmp???
+//     SetGridSize(12.0);      // This is in pixels
+}
+
+void DrawingView::DrawBackground(Painter * painter)
+{
+       Point ul = Painter::QtToCartesianCoords(Vector(0, 0));
+       Point br = Painter::QtToCartesianCoords(Vector(Global::screenSize.x, Global::screenSize.y));
+
+       painter->SetBrush(0xF0F0F0);
+       painter->SetPen(0xF0F0F0, 1, 1);
+       painter->DrawRect(QRectF(QPointF(ul.x, ul.y), QPointF(br.x, br.y)));
+
+       double spacing = Global::gridSpacing;
+
+       if (spacing < 1.0)
+               spacing = 1.0;
+
+       double leftx = floor(ul.x / spacing) * spacing;
+       double bottomy = floor(br.y / spacing) * spacing;
+
+       double w = (br.x - ul.x) + Global::gridSpacing + 1.0;
+       double h = (ul.y - br.y) + Global::gridSpacing + 1.0;
+
+       Vector start(leftx, bottomy), size(w, h);
+
+       if (Global::gridSpacing <= 0.015625)
+               DrawSubGrid(painter, 0xFFD2D2, 0.015625, start, size);
+
+       if (Global::gridSpacing <= 0.03125)
+               DrawSubGrid(painter, 0xFFD2D2, 0.03125, start, size);
+
+       if (Global::gridSpacing <= 0.0625)
+               DrawSubGrid(painter, 0xB8ECFF, 0.0625, start, size);
+
+       if (Global::gridSpacing <= 0.125)
+               DrawSubGrid(painter, 0xB8ECFF, 0.125, start, size);
+
+       if (Global::gridSpacing <= 0.25)
+               DrawSubGrid(painter, 0xDBDBFF, 0.25, start, size);
+
+       if (Global::gridSpacing <= 0.5)
+               DrawSubGrid(painter, 0xDBDBFF, 0.5, start, size);
+
+       painter->SetPen(QPen(QColor(0xD2, 0xD2, 0xFF), 2.0, Qt::SolidLine));
+
+       for(double i=0; i<=w; i+=spacing)
+               painter->DrawVLine(leftx + i);
+
+       for(double i=0; i<=h; i+=spacing)
+               painter->DrawHLine(bottomy + i);
 }
 
+void DrawingView::DrawSubGrid(Painter * painter, uint32_t color, double step, Vector start, Vector size)
+{
+       painter->SetPen(color, 1, 1);
+
+       for(double i=-step; i<=size.x; i+=step*2.0)
+               painter->DrawVLine(start.x + i);
+
+       for(double i=-step; i<=size.y; i+=step*2.0)
+               painter->DrawHLine(start.y + i);
+}
 
+#if 0
 void DrawingView::SetGridSize(uint32_t size)
 {
+#if 0
        // Sanity check
        if (size == gridPixels)
                return;
 
+       // tmp...
+       if (size <= 1)
+               return;
+
        // Recreate the background bitmap
        gridPixels = size;
        QPainter pmp(&gridBackground);
@@ -164,13 +214,15 @@ 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();
+#endif
 }
 
-
 void DrawingView::UpdateGridBackground(void)
 {
+#if 0
        // Transform the origin to Qt coordinates
        Vector pixmapOrigin = Painter::CartesianToQtCoords(Vector());
        int x = (int)pixmapOrigin.x;
@@ -245,9 +297,72 @@ zero; so we do another modulus operation on the result to achieve this.
        pal.setBrush(backgroundRole(), QBrush(pm));
        setAutoFillBackground(true);
        setPalette(pal);
+#endif
+}
+
+void DrawingView::SetGridSize(double size)
+{
+#if 0
+       // Sanity check
+       if (size == gridPixelsF)
+               return;
+
+       // tmp...
+       if (size <= 1)
+               return;
+
+       // Recreate the background bitmap
+       gridPixelsF = size;
+       QPainter pmp(&gridBackground);
+       pmp.fillRect(0, 0, BACKGROUND_MAX_SIZE, BACKGROUND_MAX_SIZE, QColor(240, 240, 240));
+       pmp.setPen(QPen(QColor(210, 210, 255), 2.0, Qt::SolidLine));
+
+       for(double i=0; i<(BACKGROUND_MAX_SIZE-1); i+=gridPixelsF)
+       {
+               pmp.drawLine(i, 0, i, (double)(BACKGROUND_MAX_SIZE - 1));
+               pmp.drawLine(0, i, (double)(BACKGROUND_MAX_SIZE - 1), i);
+       }
+
+       pmp.end();
+
+       // Set up new BG brush & zoom level (pixels per base unit)
+// This shouldn't be done here, because it fucks up the scrollwheel zooming...
+//     Global::zoom = gridPixels / Global::gridSpacing;
+       UpdateGridBackgroundF();
+#endif
 }
 
 
+void DrawingView::UpdateGridBackgroundF(void)
+{
+#if 0
+       // Transform the origin to Qt coordinates
+       Vector pixmapOrigin = Painter::CartesianToQtCoords(Vector());
+       int x = 0;// (int)pixmapOrigin.x;
+       int y = 0;// (int)pixmapOrigin.y;
+       // Use mod arithmetic to grab the correct swatch of background
+
+/*     if (x < 0)
+               x = -x % gridPixels;
+       else
+               x = (gridPixels - (x % gridPixels)) % gridPixels;
+
+       if (y < 0)
+               y = -y % gridPixels;
+       else
+               y = (gridPixels - (y % gridPixels)) % gridPixels;*/
+
+       // Here we grab a section of the bigger pixmap, so that the background
+       // *looks* like it's scrolling...
+       QPixmap pm = gridBackground.copy(x, y, gridPixelsF, gridPixelsF);
+       QPalette pal = palette();
+       pal.setBrush(backgroundRole(), QBrush(pm));
+       setAutoFillBackground(true);
+       setPalette(pal);
+#endif
+}
+#endif
+
 //
 // Basically, we just make a single pass through the Container. If the layer #
 // is less than the layer # being deleted, then do nothing. If the layer # is
@@ -258,7 +373,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())
        {
@@ -282,14 +397,12 @@ void DrawingView::DeleteCurrentLayer(int layer)
        update();
 }
 
-
 void DrawingView::HandleLayerToggle(void)
 {
        // A layer's visibility was toggled, so update the screen...
        update();
 }
 
-
 //
 // A layer was moved up or down in the layer list, so we have to swap the
 // document's object's layer numbers in the layers that were swapped.
@@ -297,9 +410,15 @@ void DrawingView::HandleLayerToggle(void)
 void DrawingView::HandleLayerSwap(int layer1, int layer2)
 {
 //printf("DrawingView: Swapping layers %i and %i.\n", layer1, layer2);
-       std::vector<void *>::iterator i;
+       HandleLayerSwap(layer1, layer2, document.objects);
+}
 
-       for(i=document.objects.begin(); i!=document.objects.end(); i++)
+/*
+We can roll this into the main one above, by having the LayerWidget's emit() call sending NULL for the VPVector, which we can test for and set to document.objects to grab the top layer.  Or, keep it a top level call and a recursive call.  Which is worse?  :-P
+*/
+void DrawingView::HandleLayerSwap(int layer1, int layer2, VPVector & v)
+{
+       for(VPVectorIter i=v.begin(); i!=v.end(); i++)
        {
                Object * obj = (Object *)(*i);
 
@@ -307,9 +426,80 @@ void DrawingView::HandleLayerSwap(int layer1, int layer2)
                        obj->layer = layer2;
                else if (obj->layer == layer2)
                        obj->layer = layer1;
+
+               if (obj->type == OTContainer)
+                       HandleLayerSwap(layer1, layer2, ((Container *)obj)->objects);
+       }
+}
+
+void DrawingView::HandlePenWidth(float width)
+{
+       for(VPVectorIter i=select.begin(); i!=select.end(); i++)
+       {
+               Object * obj = (Object *)(*i);
+               obj->thickness = width;
+       }
+
+       supressSelected = true;
+       update();
+}
+
+void DrawingView::HandlePenStyle(int style)
+{
+       for(VPVectorIter i=select.begin(); i!=select.end(); i++)
+       {
+               Object * obj = (Object *)(*i);
+               obj->style = style;
+       }
+
+       supressSelected = true;
+       update();
+}
+
+void DrawingView::HandlePenColor(uint32_t color)
+{
+       for(VPVectorIter i=select.begin(); i!=select.end(); i++)
+       {
+               Object * obj = (Object *)(*i);
+               obj->color = color;
        }
+
+       supressSelected = true;
+       update();
+}
+
+void DrawingView::HandlePenStamp(QAction * action)
+{
+       PenWidget * pw = (PenWidget *)action->parentWidget();
+       pw->dropperAction->setChecked(false);
+       Global::penDropper = false;
+       Global::penStamp = action->isChecked();
+
+       if (Global::penStamp)
+               setCursor(curMarker);
+       else
+               setCursor(Qt::ArrowCursor);
+
+       if (Global::penStamp == false)
+               ClearSelected(document.objects);
+
+       update();
 }
 
+void DrawingView::HandlePenDropper(QAction * action)
+{
+       PenWidget * pw = (PenWidget *)action->parentWidget();
+       pw->stampAction->setChecked(false);
+       Global::penStamp = false;
+       Global::penDropper = action->isChecked();
+
+       if (Global::penDropper)
+               setCursor(curDropper);
+       else
+               setCursor(Qt::ArrowCursor);
+
+       update();
+}
 
 QPoint DrawingView::GetAdjustedMousePosition(QMouseEvent * event)
 {
@@ -319,7 +509,6 @@ QPoint DrawingView::GetAdjustedMousePosition(QMouseEvent * event)
        return QPoint(offsetX + event->x(), offsetY + (size().height() - event->y()));
 }
 
-
 QPoint DrawingView::GetAdjustedClientPosition(int x, int y)
 {
        // VOODOO ALERT (ON Y COMPONENT!!!!) (eh?)
@@ -328,6 +517,27 @@ QPoint DrawingView::GetAdjustedClientPosition(int x, int y)
        return QPoint(-offsetX + x, (size().height() - (-offsetY + y)) * +1.0);
 }
 
+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::focusInEvent(QFocusEvent * /*event*/)
+{
+       if (Global::penStamp)
+               setCursor(curMarker);
+       else if (Global::penDropper)
+               setCursor(curDropper);
+//FocusOut already set this...
+//     else
+//             setCursor(Qt::ArrowCursor);
+}
 
 void DrawingView::paintEvent(QPaintEvent * /*event*/)
 {
@@ -339,6 +549,8 @@ void DrawingView::paintEvent(QPaintEvent * /*event*/)
 
        Global::viewportHeight = size().height();
 
+       DrawBackground(&painter);
+
        // Draw coordinate axes
        painter.SetPen(QPen(Qt::blue, 1.0, Qt::DotLine));
        painter.DrawLine(0, -16384, 0, 16384);
@@ -370,19 +582,25 @@ void DrawingView::paintEvent(QPaintEvent * /*event*/)
        if (hoveringIntersection)
                painter.DrawHandle(intersectionPoint);
 
+       if (hoverPointValid)
+               painter.DrawHandle(hoverPoint);
+
        if (!informativeText.isEmpty())
                painter.DrawInformativeText(informativeText);
 }
 
-
 //
 // 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;
@@ -400,7 +618,16 @@ void DrawingView::RenderObjects(Painter * painter, std::vector<void *> & v, int
                        painter->SetPen(obj->color, Global::zoom * scaledThickness, obj->style);
                        painter->SetBrush(obj->color);
 
-                       if (obj->selected || obj->hitObject)
+                       // penStamp supresses object highlighting, so that changes can be seen.
+                       if (supressSelected || Global::penStamp)
+                       {
+                               if (obj->hitObject)
+                               {
+                                       painter->SetPen(Global::penColor, Global::zoom * Global::scale * Global::penWidth, Global::penStyle);
+                                       painter->SetBrush(Global::penColor);
+                               }
+                       }
+                       else if (obj->selected || obj->hitObject)
                                painter->SetPen(0xFF0000, Global::zoom * scaledThickness, LSDash);
                }
 
@@ -415,7 +642,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 +655,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 +669,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;
@@ -444,7 +677,7 @@ void DrawingView::RenderObjects(Painter * painter, std::vector<void *> & v, int
                        Vector v(d->p[0], d->p[1]);
                        double angle = v.Angle();
                        Vector unit = v.Unit();
-                       Vector linePt1 = d->p[0], linePt2 = d->p[1];
+                       d->lp[0] = d->p[0], d->lp[1] = d->p[1];
                        Vector ortho;
                        double x1, y1, length;
 
@@ -465,7 +698,7 @@ void DrawingView::RenderObjects(Painter * painter, std::vector<void *> & v, int
                                        angle = QTR_TAU;
                                }
 
-                               linePt1.x = linePt2.x = x1;
+                               d->lp[0].x = d->lp[1].x = x1;
                                length = fabs(d->p[0].y - d->p[1].y);
                        }
                        else if (d->subtype == DTLinearHorz)
@@ -485,22 +718,22 @@ void DrawingView::RenderObjects(Painter * painter, std::vector<void *> & v, int
                                        angle = HALF_TAU;
                                }
 
-                               linePt1.y = linePt2.y = y1;
+                               d->lp[0].y = d->lp[1].y = y1;
                                length = fabs(d->p[0].x - d->p[1].x);
                        }
                        else if (d->subtype == DTLinear)
                        {
-                               angle = Vector(linePt1, linePt2).Angle();
-                               ortho = Vector::Normal(linePt1, linePt2);
+                               angle = Vector(d->lp[0], d->lp[1]).Angle();
+                               ortho = Vector::Normal(d->lp[0], d->lp[1]);
                                length = v.Magnitude();
                        }
 
-                       unit = Vector(linePt1, linePt2).Unit();
+                       unit = Vector(d->lp[0], d->lp[1]).Unit();
 
-                       Point p1 = linePt1 + (ortho * 10.0 * scaledThickness);
-                       Point p2 = linePt2 + (ortho * 10.0 * scaledThickness);
-                       Point p3 = linePt1 + (ortho * 16.0 * scaledThickness);
-                       Point p4 = linePt2 + (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);
 
@@ -514,7 +747,7 @@ void DrawingView::RenderObjects(Painter * painter, std::vector<void *> & v, int
 
                        // Calculate whether or not the arrowheads are too crowded to put
                        // inside the extension lines. 9.0 is the length of the arrowhead.
-                       double t = Geometry::ParameterOfLineAndPoint(linePt1, linePt2, linePt2 - (unit * 9.0 * scaledThickness));
+                       double t = Geometry::ParameterOfLineAndPoint(d->lp[0], d->lp[1], d->lp[1] - (unit * 9.0 * scaledThickness));
 
                        // On the screen, it's acting like this is actually 58%...
                        // This is correct, we want it to happen at > 50%
@@ -555,97 +788,160 @@ 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)
+                       {
+                               Point hp1 = (p1 + p2) / 2.0;
+                               Point hp2 = (p1 + hp1) / 2.0;
+                               Point hp3 = (hp1 + p2) / 2.0;
+
+                               if (d->hitPoint[2])
+                               {
+                                       painter->SetPen(QPen(Qt::magenta, 1.0, Qt::SolidLine));
+                                       painter->SetBrush(QBrush(QColor(Qt::magenta)));
+                                       painter->DrawArrowHandle(hp1, ortho.Angle() + HALF_TAU);
+                                       painter->SetPen(QPen(Qt::magenta, 2.0, Qt::DotLine));
+                               }
+
+                               painter->DrawHandle(hp1);
+                               painter->SetPen(QPen(Qt::blue, 1.0 * Global::zoom * scaledThickness, Qt::SolidLine));
+
+                               if (d->hitPoint[3])
+                               {
+                                       painter->SetPen(QPen(Qt::magenta, 1.0, Qt::SolidLine));
+                                       painter->SetBrush(QBrush(QColor(Qt::magenta)));
+                                       painter->DrawArrowToLineHandle(hp2, (d->subtype == DTLinearVert ? v.Angle() - QTR_TAU : (v.Angle() < HALF_TAU ? HALF_TAU : 0)));
+                                       painter->SetPen(QPen(Qt::magenta, 2.0, Qt::DotLine));
+                               }
+
+                               painter->DrawHandle(hp2);
+                               painter->SetPen(QPen(Qt::blue, 1.0 * Global::zoom * scaledThickness, Qt::SolidLine));
+
+                               if (d->hitPoint[4])
+                               {
+                                       painter->SetPen(QPen(Qt::magenta, 1.0, Qt::SolidLine));
+                                       painter->SetBrush(QBrush(QColor(Qt::magenta)));
+                                       painter->DrawArrowToLineHandle(hp3, (d->subtype == DTLinearHorz ? v.Angle() - QTR_TAU : (v.Angle() > HALF_TAU && v.Angle() < THREE_QTR_TAU ? THREE_QTR_TAU : QTR_TAU)));
+                                       painter->SetPen(QPen(Qt::magenta, 2.0, Qt::DotLine));
+                               }
+
+                               painter->DrawHandle(hp3);
+                       }
+
+                       if (obj->hitPoint[0])
+                               painter->DrawHandle(obj->p[0]);
+
+                       if (obj->hitPoint[1])
+                               painter->DrawHandle(obj->p[1]);
+
                        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;
                }
        }
-}
 
+       supressSelected = false;
+}
 
+//
+// 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;
+}
 
 void DrawingView::resizeEvent(QResizeEvent * /*event*/)
 {
        Global::screenSize = Vector(size().width(), size().height());
-       UpdateGridBackground();
+//     UpdateGridBackground();
 }
 
-
 void DrawingView::ToolHandler(int mode, Point p)
 {
+       // Drop angle snap until it's needed
+       angleSnap = false;
+
        if (Global::tool == TTLine)
                LineHandler(mode, p);
        else if (Global::tool == TTCircle)
@@ -656,9 +952,16 @@ 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);
 }
 
-
 void DrawingView::ToolDraw(Painter * painter)
 {
        if (Global::tool == TTLine)
@@ -778,7 +1081,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]);
 
@@ -794,8 +1097,29 @@ 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);
+               }
+       }
+}
 
 void DrawingView::LineHandler(int mode, Point p)
 {
@@ -808,6 +1132,7 @@ void DrawingView::LineHandler(int mode, Point p)
                        toolPoint[1] = p;
 
                break;
+
        case ToolMouseMove:
                if (Global::toolState == TSNone)
                        toolPoint[0] = p;
@@ -815,6 +1140,7 @@ void DrawingView::LineHandler(int mode, Point p)
                        toolPoint[1] = p;
 
                break;
+
        case ToolMouseUp:
                if (Global::toolState == TSNone)
                {
@@ -830,7 +1156,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];
@@ -838,7 +1164,6 @@ void DrawingView::LineHandler(int mode, Point p)
        }
 }
 
-
 void DrawingView::CircleHandler(int mode, Point p)
 {
        switch (mode)
@@ -850,6 +1175,7 @@ void DrawingView::CircleHandler(int mode, Point p)
                        toolPoint[1] = p;
 
                break;
+
        case ToolMouseMove:
                if (Global::toolState == TSNone)
                        toolPoint[0] = p;
@@ -857,6 +1183,7 @@ void DrawingView::CircleHandler(int mode, Point p)
                        toolPoint[1] = p;
 
                break;
+
        case ToolMouseUp:
                if (Global::toolState == TSNone)
                {
@@ -873,7 +1200,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];
@@ -882,7 +1209,6 @@ void DrawingView::CircleHandler(int mode, Point p)
        }
 }
 
-
 void DrawingView::ArcHandler(int mode, Point p)
 {
        switch (mode)
@@ -898,17 +1224,25 @@ void DrawingView::ArcHandler(int mode, Point p)
                        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;
+                       angleSnap = true;
+               }
                else
+               {
                        toolPoint[3] = p;
+                       angleSnap = true;
+               }
 
                break;
+
        case ToolMouseUp:
                if (Global::toolState == TSNone)
                {
@@ -920,7 +1254,7 @@ void DrawingView::ArcHandler(int mode, Point p)
                {
                        if (shiftDown)
                        {
-                               // Key override is telling us to start circle at new center, not
+                               // Key override is telling us to start arc at new center, not
                                // continue the current one.
                                toolPoint[0] = toolPoint[1];
                                return;
@@ -944,7 +1278,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;
@@ -952,7 +1286,6 @@ void DrawingView::ArcHandler(int mode, Point p)
        }
 }
 
-
 void DrawingView::RotateHandler(int mode, Point p)
 {
        switch (mode)
@@ -961,7 +1294,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)
@@ -970,6 +1304,7 @@ void DrawingView::RotateHandler(int mode, Point p)
                        toolPoint[1] = p;
 
                break;
+
        case ToolMouseMove:
                if ((Global::toolState == TSPoint1) || (Global::toolState == TSNone))
                        toolPoint[0] = p;
@@ -980,30 +1315,72 @@ void DrawingView::RotateHandler(int mode, Point p)
                        if (shiftDown)
                                return;
 
+                       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)
                {
@@ -1023,38 +1400,46 @@ 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);
        }
 }
 
-
 void DrawingView::MirrorHandler(int mode, Point p)
 {
        switch (mode)
@@ -1072,6 +1457,7 @@ void DrawingView::MirrorHandler(int mode, Point p)
                        toolPoint[1] = p;
 
                break;
+
        case ToolMouseMove:
                if ((Global::toolState == TSPoint1) || (Global::toolState == TSNone))
                        toolPoint[0] = p;
@@ -1082,8 +1468,9 @@ void DrawingView::MirrorHandler(int mode, Point p)
                        if (shiftDown)
                                return;
 
+                       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++)
@@ -1095,6 +1482,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
@@ -1107,6 +1499,7 @@ void DrawingView::MirrorHandler(int mode, Point p)
                }
 
                break;
+
        case ToolMouseUp:
                if (Global::toolState == TSPoint1)
                {
@@ -1126,7 +1519,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);
@@ -1140,28 +1533,325 @@ 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
@@ -1169,15 +1859,17 @@ void DrawingView::mousePressEvent(QMouseEvent * event)
                {
                        if (hoveringIntersection)
                                point = intersectionPoint;
+                       else if (hoverPointValid)
+                               point = hoverPoint;
                        else if (Global::snapToGrid)
                                point = SnapPointToGrid(point);
 
-                       //Also, may want to figure out if hovering over a snap point on an object,
-                       //snap to grid if not.
+                       //Also, may want to figure out if hovering over a snap point on an
+                       //object, snap to grid if not.
                        // Snap to object point if valid...
 //                     if (Global::snapPointIsValid)
 //                             point = Global::snapPoint;
-                       
+
                        ToolHandler(ToolMouseDown, point);
                        return;
                }
@@ -1193,22 +1885,74 @@ void DrawingView::mousePressEvent(QMouseEvent * event)
                {
                        AddHoveredToSelection();
                        update();       // needed??
-                       GetHovered(hover);      // prolly needed
+//                     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
+// Maybe do this with an eyedropper tool on the pen bar?  [YES]
+//                     emit ObjectSelected(dragged);
+                       if (Global::penDropper)
+                       {
+                               Global::penColor = dragged->color;
+                               Global::penWidth = dragged->thickness;
+                               Global::penStyle = dragged->style;
+                               emit ObjectSelected(dragged);
+                               ClearSelected(document.objects);
+                               return;
+                       }
+
+                       if (Global::penStamp)
+                       {
+                               dragged->color = Global::penColor;
+                               dragged->thickness = Global::penWidth;
+                               dragged->style = Global::penStyle;
+                               return;
+                       }
+
+                       // See if anything is using just a straight click on a handle
+                       if (HandleObjectClicked())
+                       {
+                               draggingObject = false;
+                               update();
+                               return;
+                       }
 
                        // Needed for grab & moving objects
                        // 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)
        {
@@ -1219,17 +1963,24 @@ 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
@@ -1240,7 +1991,7 @@ void DrawingView::mouseMoveEvent(QMouseEvent * event)
                delta.y = -delta.y;
                Global::origin -= delta;
 
-               UpdateGridBackground();
+//             UpdateGridBackground();
                update();
                oldPoint = point;
                return;
@@ -1251,40 +2002,49 @@ void DrawingView::mouseMoveEvent(QMouseEvent * event)
        if (Global::selectionInProgress)
        {
                CheckObjectBounds();
-               update();
-               return;
-       }
 
-       // Handle object movement (left button down & over an object)
-       if ((event->buttons() & Qt::LeftButton) && numHovered && !Global::tool)
-       {
-               if (hoveringIntersection)
-                       point = intersectionPoint;
-               else if (Global::snapToGrid)
-                       point = SnapPointToGrid(point);
+               // Make sure previously selected objects stay selected (CTRL held)
+               for(VPVectorIter i=select.begin(); i!=select.end(); i++)
+               {
+                       // Make sure *not* to select items on hidden layers
+                       if (Global::layerHidden[((Object *)(*i))->layer] == false)
+                               ((Object *)(*i))->selected = true;
+               }
 
-               HandleObjectMovement(point);
                update();
-               oldPoint = point;
                return;
        }
 
        // Do object hit testing...
        bool needUpdate = HitTestObjects(point);
+       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)
        {
-               GetHovered(hover);
-               Geometry::Intersects((Object *)hover[0], (Object *)hover[1]);
+//need to check for case where hover is over 2 circles and a 3rd's center...
+               Object * obj1 = (Object *)hover2[0], * obj2 = (Object *)hover2[1];
+
+               Geometry::Intersects(obj1, obj2);
                int numIntersecting = Global::numIntersectParams;
                double t = Global::intersectParam[0];
                double u = Global::intersectParam[1];
 
                if (numIntersecting > 0)
                {
-                       Vector v1 = Geometry::GetPointForParameter((Object *)hover[0], t);
-                       Vector v2 = Geometry::GetPointForParameter((Object *)hover[1], u);
+                       Vector v1 = Geometry::GetPointForParameter(obj1, t);
+                       Vector v2 = Geometry::GetPointForParameter(obj2, u);
                        QString text = tr("Intersection t=%1 (%3, %4), u=%2 (%5, %6)");
                        informativeText = text.arg(t).arg(u).arg(v1.x).arg(v1.y).arg(v2.x).arg(v2.y);
 
@@ -1312,15 +2072,37 @@ 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;
+                       }
+               }
+       }
 
-//this doesn't work down here for some reason... :-P
-//could be because the object being moved is part of the intersection, and this is screwing things up. In which case, we need to exclude the moving object somehow from the hit test function...
-#if 0
        // Handle object movement (left button down & over an object)
-       if ((event->buttons() & Qt::LeftButton) && numHovered && !Global::tool)
+       if ((event->buttons() & Qt::LeftButton) && draggingObject && !Global::tool)
        {
                if (hoveringIntersection)
                        point = intersectionPoint;
+               else if (hoverPointValid)
+                       point = hoverPoint;
                else if (Global::snapToGrid)
                        point = SnapPointToGrid(point);
 
@@ -1329,15 +2111,21 @@ void DrawingView::mouseMoveEvent(QMouseEvent * event)
                oldPoint = point;
                return;
        }
-#endif
 
        // Do tool handling, if any are active...
        if (Global::tool)
        {
                if (hoveringIntersection)
                        point = intersectionPoint;
+               else if (hoverPointValid)
+                       point = hoverPoint;
                else if (Global::snapToGrid)
-                       point = SnapPointToGrid(point);
+               {
+                       if (angleSnap)
+                               point = SnapPointToAngle(point);
+                       else
+                               point = SnapPointToGrid(point);
+               }
 
                ToolHandler(ToolMouseMove, point);
        }
@@ -1349,7 +2137,6 @@ void DrawingView::mouseMoveEvent(QMouseEvent * event)
                update();
 }
 
-
 void DrawingView::mouseReleaseEvent(QMouseEvent * event)
 {
        if (event->button() == Qt::LeftButton)
@@ -1369,75 +2156,77 @@ 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();
-
-               // Scoop 'em up
-               std::vector<void *>::iterator i;
+//             select.clear();
+////           hover.clear();
 
-               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();
 
-//hmm, this is no good, too late to do any good :-P
-//                     if ((*i)->hovered)
-//                             hover.push_back(*i);
-               }
+               draggingObject = false;
        }
        else if (event->button() == Qt::MiddleButton)
        {
                scrollDrag = false;
-               setCursor(Qt::ArrowCursor);
+
+               if (Global::penStamp)
+                       setCursor(curMarker);
+               else if (Global::penDropper)
+                       setCursor(curDropper);
+               else
+                       setCursor(Qt::ArrowCursor);
+
+               // Need to convert this, since it's in Qt coordinates (for wheelEvent)
+               oldPoint = Painter::QtToCartesianCoords(oldPoint);
        }
 }
 
-
 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);
+       double zoomFactor = 1.20;
+       scrollWheelSeen = true;
 
-       // This is not centering for some reason. Need to figure out why. :-/
-       if (event->delta() > 0)
+       if (event->angleDelta().y() < 0)
        {
-               Vector newOrigin = center - ((center - Global::origin) / zoomFactor);
-               Global::origin = newOrigin;
+               if (Global::zoom > 400.0)
+                       return;
+
                Global::zoom *= zoomFactor;
        }
        else
        {
-               Vector newOrigin = center + ((-center + Global::origin) * zoomFactor);
-               Global::origin = newOrigin;
+               if (Global::zoom < 0.125)
+                       return;
+
                Global::zoom /= zoomFactor;
        }
 
-//     Global::gridSpacing = gridPixels / Painter::zoom;
-//     UpdateGridBackground();
-       SetGridSize(Global::gridSpacing * Global::zoom);
-       update();
-//     zoomIndicator->setText(QString("Grid: %1\", BU: Inch").arg(Global::gridSpacing));
-}
+       Point np = Painter::QtToCartesianCoords(oldScrollPoint);
+       Global::origin += (oldPoint - np);
 
+       emit(NeedZoomUpdate());
+}
 
 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)
@@ -1445,18 +2234,51 @@ 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)
+               {
+                       TranslateObjects(select, Point(0, +1.0));
+                       update();
+               }
+               else if (event->key() == Qt::Key_Down)
+               {
+                       TranslateObjects(select, Point(0, -1.0));
+                       update();
+               }
+               else if (event->key() == Qt::Key_Right)
+               {
+                       TranslateObjects(select, Point(+1.0, 0));
+                       update();
+               }
+               else if (event->key() == Qt::Key_Left)
+               {
+                       TranslateObjects(select, Point(-1.0, 0));
+                       update();
+               }
+       }
+}
 
 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))
        {
@@ -1465,8 +2287,19 @@ void DrawingView::keyReleaseEvent(QKeyEvent * event)
 
                update();
        }
-}
 
+       if (oldAlt != altDown)
+       {
+               scrollDrag = false;
+
+               if (Global::penStamp)
+                       setCursor(curMarker);
+               else if (Global::penDropper)
+                       setCursor(curDropper);
+               else
+                       setCursor(Qt::ArrowCursor);
+       }
+}
 
 //
 // This looks strange, but it's really quite simple: We want a point that's
@@ -1487,6 +2320,22 @@ Point DrawingView::SnapPointToGrid(Point point)
        return point;
 }
 
+Point DrawingView::SnapPointToAngle(Point point)
+{
+       // Snap to a single digit angle (using toolpoint #1 as the center)
+       double angle = Vector::Angle(toolPoint[0], point);
+       double length = Vector::Magnitude(toolPoint[0], point);
+
+       // Convert from radians to degrees
+       double degAngle = angle * RADIANS_TO_DEGREES;
+       double snapAngle = (double)((int)(degAngle + 0.5));
+
+       Vector v;
+       v.SetAngleAndLength(snapAngle * DEGREES_TO_RADIANS, length);
+       point = toolPoint[0] + v;
+
+       return point;
+}
 
 Rect DrawingView::GetObjectExtents(Object * obj)
 {
@@ -1496,16 +2345,19 @@ Rect DrawingView::GetObjectExtents(Object * obj)
        switch (obj->type)
        {
        case OTLine:
+       case OTDimension:
        {
                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;
@@ -1542,19 +2394,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;
        }
@@ -1562,10 +2422,9 @@ Rect DrawingView::GetObjectExtents(Object * obj)
        return rect;
 }
 
-
 void DrawingView::CheckObjectBounds(void)
 {
-       std::vector<void *>::iterator i;
+       VPVectorIter i;
 
        for(i=document.objects.begin(); i!=document.objects.end(); i++)
        {
@@ -1575,6 +2434,7 @@ void DrawingView::CheckObjectBounds(void)
                switch (obj->type)
                {
                case OTLine:
+               case OTDimension:
                {
                        Line * l = (Line *)obj;
 
@@ -1583,6 +2443,7 @@ void DrawingView::CheckObjectBounds(void)
 
                        break;
                }
+
                case OTCircle:
                {
                        Circle * c = (Circle *)obj;
@@ -1592,6 +2453,7 @@ void DrawingView::CheckObjectBounds(void)
 
                        break;
                }
+
                case OTArc:
                {
                        Arc * a = (Arc *)obj;
@@ -1622,7 +2484,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;
 
@@ -1657,144 +2520,63 @@ void DrawingView::CheckObjectBounds(void)
 
                        break;
                }
-               default:
-                       break;
-               }
-       }
-}
-
-
-bool DrawingView::HitTestObjects(Point point)
-{
-       std::vector<void *>::iterator i;
-       numHovered = 0;
-       bool needUpdate = false;
-
-       for(i=document.objects.begin(); i!=document.objects.end(); i++)
-       {
-               Object * obj = (Object *)(*i);
 
-               if (HitTest(obj, point))
-                       needUpdate = true;
-#if 0
-               switch (obj->type)
-               {
-               case OTLine:
+               case OTText:
                {
-                       bool oldHP0 = obj->hitPoint[0], oldHP1 = obj->hitPoint[1], oldHO = obj->hitObject;
-                       obj->hitPoint[0] = obj->hitPoint[1] = obj->hitObject = false;
-                       Vector lineSegment = obj->p[1] - obj->p[0];
-                       Vector v1 = point - obj->p[0];
-                       Vector v2 = point - obj->p[1];
-                       double t = Geometry::ParameterOfLineAndPoint(obj->p[0], obj->p[1], point);
-                       double distance;
-
-                       if (t < 0.0)
-                               distance = v1.Magnitude();
-                       else if (t > 1.0)
-                               distance = v2.Magnitude();
-                       else
-                               // distance = ?Det?(ls, v1) / |ls|
-                               distance = fabs((lineSegment.x * v1.y - v1.x * lineSegment.y)
-                                       / lineSegment.Magnitude());
-
-                       if ((v1.Magnitude() * Global::zoom) < 8.0)
-                               obj->hitPoint[0] = true;
-                       else if ((v2.Magnitude() * Global::zoom) < 8.0)
-                               obj->hitPoint[1] = true;
-                       else if ((distance * Global::zoom) < 5.0)
-                               obj->hitObject = true;
-
-                       obj->hovered = (obj->hitPoint[0] || obj->hitPoint[1] || obj->hitObject ? true : false);
+                       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 ((oldHP0 != obj->hitPoint[0]) || (oldHP1 != obj->hitPoint[1]) || (oldHO != obj->hitObject))
-                               needUpdate = true;
+                       if (Global::selection.contains(r.l, r.t) && Global::selection.contains(r.r, r.b))
+                               t->selected = true;
 
                        break;
                }
-               case OTCircle:
-               {
-                       bool oldHP = obj->hitPoint[0], oldHO = obj->hitObject;
-                       obj->hitPoint[0] = obj->hitObject = false;
-                       double length = Vector::Magnitude(obj->p[0], point);
-
-                       if ((length * Global::zoom) < 8.0)
-                               obj->hitPoint[0] = true;
-                       else if ((fabs(length - obj->radius[0]) * Global::zoom) < 2.0)
-                               obj->hitObject = true;
 
-                       obj->hovered = (obj->hitPoint[0] || obj->hitObject ? true : false);
+               case OTContainer:
+               {
+                       Container * c = (Container *)obj;
 
-                       if ((oldHP != obj->hitPoint[0]) || (oldHO != obj->hitObject))
-                               needUpdate = true;
+                       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;
                }
-               case OTArc:
-               {
-                       bool oldHP0 = obj->hitPoint[0], oldHP1 = obj->hitPoint[1], oldHP2 = obj->hitPoint[2], oldHO = obj->hitObject;
-                       obj->hitPoint[0] = obj->hitPoint[1] = obj->hitPoint[2] = obj->hitObject = false;
-                       double length = Vector::Magnitude(obj->p[0], point);
-                       double angle = Vector::Angle(obj->p[0], point);
 
-                       // Make sure we get the angle in the correct spot
-                       if (angle < obj->angle[0])
-                               angle += TAU;
-
-                       // Get the span that we're pointing at...
-                       double span = angle - obj->angle[0];
-
-                       // N.B.: Still need to hit test the arc start & arc span handles...
-                       double spanAngle = obj->angle[0] + obj->angle[1];
-                       Point handle1 = obj->p[0] + (Vector(cos(obj->angle[0]), sin(obj->angle[0])) * obj->radius[0]);
-                       Point handle2 = obj->p[0] + (Vector(cos(spanAngle), sin(spanAngle)) * obj->radius[0]);
-                       double length2 = Vector::Magnitude(point, handle1);
-                       double length3 = Vector::Magnitude(point, handle2);
-
-                       if ((length * Global::zoom) < 8.0)
-                               obj->hitPoint[0] = true;
-                       else if ((length2 * Global::zoom) < 8.0)
-                               obj->hitPoint[1] = true;
-                       else if ((length3 * Global::zoom) < 8.0)
-                               obj->hitPoint[2] = true;
-                       else if (((fabs(length - obj->radius[0]) * Global::zoom) < 2.0) && (span < obj->angle[1]))
-                               obj->hitObject = true;
+//             default:
+//                     break;
+               }
+       }
+}
 
-                       obj->hovered = (obj->hitPoint[0] || obj->hitPoint[1] || obj->hitPoint[2] || obj->hitObject ? true : false);
+bool DrawingView::HitTestObjects(Point point)
+{
+       VPVectorIter i;
+       numHovered = 0;
+       bool needUpdate = false;
+       hoverPointValid = false;
 
-                       if ((oldHP0 != obj->hitPoint[0]) || (oldHP1 != obj->hitPoint[1]) || (oldHP2 != obj->hitPoint[2]) || (oldHO != obj->hitObject))
-                               needUpdate = true;
+       for(i=document.objects.begin(); i!=document.objects.end(); i++)
+       {
+               Object * obj = (Object *)(*i);
 
-                       break;
-               }
-               case OTContainer:
-               {
-                       // Containers must be recursively tested...
-                       Container * c = (Container *)obj;
-                       std::vector<void *>::iterator i;
+               // If we're seeing the object we're dragging, skip it
+               if (draggingObject && (obj == dragged))
+                       continue;
 
-                       for(i=c->objects.begin(); i!=c->objects.end(); i++)
-                       {
-                               
-                       }
-               }
-               default:
-                       break;
-               }
-#endif
+               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);
                }
        }
 
        return needUpdate;
 }
 
-
 bool DrawingView::HitTest(Object * obj, Point point)
 {
        bool needUpdate = false;
@@ -1821,9 +2603,17 @@ bool DrawingView::HitTest(Object * obj, Point point)
                                / lineSegment.Magnitude());
 
                if ((v1.Magnitude() * Global::zoom) < 8.0)
+               {
                        obj->hitPoint[0] = true;
+                       hoverPoint = obj->p[0];
+                       hoverPointValid = true;
+               }
                else if ((v2.Magnitude() * Global::zoom) < 8.0)
+               {
                        obj->hitPoint[1] = true;
+                       hoverPoint = obj->p[1];
+                       hoverPointValid = true;
+               }
                else if ((distance * Global::zoom) < 5.0)
                        obj->hitObject = true;
 
@@ -1834,6 +2624,7 @@ bool DrawingView::HitTest(Object * obj, Point point)
 
                break;
        }
+
        case OTCircle:
        {
                bool oldHP = obj->hitPoint[0], oldHO = obj->hitObject;
@@ -1841,7 +2632,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;
 
@@ -1852,6 +2647,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;
@@ -1874,11 +2670,23 @@ 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;
+                       hoverPoint = handle1;
+                       hoverPointValid = true;
+               }
                else if ((length3 * Global::zoom) < 8.0)
+               {
                        obj->hitPoint[2] = true;
+                       hoverPoint = handle2;
+                       hoverPointValid = true;
+               }
                else if (((fabs(length - obj->radius[0]) * Global::zoom) < 2.0) && (span < obj->angle[1]))
                        obj->hitObject = true;
 
@@ -1889,30 +2697,164 @@ 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;
+               obj->hitPoint[0] = obj->hitPoint[1] = obj->hitPoint[2] = obj->hitPoint[3] = obj->hitPoint[4] = obj->hitObject = false;
+
+               Dimension * d = (Dimension *)obj;
+
+               Vector orthogonal = Vector::Normal(d->lp[0], d->lp[1]);
+               // Get our line parallel to our points
+               float scaledThickness = Global::scale * obj->thickness;
+#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);
+               Vector v2(d->p[1], point);
+               Vector lineSegment(p1, p2);
+               double t = Geometry::ParameterOfLineAndPoint(p1, p2, point);
+               double distance;
+               Point midpoint = (p1 + p2) / 2.0;
+               Point hFSPoint = Point(midpoint, point);
+               Point hCS1Point = Point((p1 + midpoint) / 2.0, point);
+               Point hCS2Point = Point((midpoint + p2) / 2.0, point);
+
+               if (t < 0.0)
+                       distance = v1.Magnitude();
+               else if (t > 1.0)
+                       distance = v2.Magnitude();
+               else
+                       // distance = ?Det?(ls, v1) / |ls|
+                       distance = fabs((lineSegment.x * p3.y - p3.x * lineSegment.y)
+                               / lineSegment.Magnitude());
+
+               if ((v1.Magnitude() * Global::zoom) < 8.0)
+                       obj->hitPoint[0] = true;
+               else if ((v2.Magnitude() * Global::zoom) < 8.0)
+                       obj->hitPoint[1] = true;
+               else if ((distance * Global::zoom) < 5.0)
+                       obj->hitObject = true;
+
+               if ((hFSPoint.Magnitude() * Global::zoom) < 8.0)
+                       obj->hitPoint[2] = true;
+               else if ((hCS1Point.Magnitude() * Global::zoom) < 8.0)
+                       obj->hitPoint[3] = true;
+               else if ((hCS2Point.Magnitude() * Global::zoom) < 8.0)
+                       obj->hitPoint[4] = true;
+
+               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;
        }
@@ -1920,12 +2862,55 @@ bool DrawingView::HitTest(Object * obj, Point point)
        return needUpdate;
 }
 
+bool DrawingView::HandleObjectClicked(void)
+{
+       if (dragged->type == OTDimension)
+       {
+               Dimension * d = (Dimension *)dragged;
+
+               if (d->hitPoint[2])
+               {
+                       // Hit the "flip sides" switch, so flip 'em
+                       Point temp = d->p[0];
+                       d->p[0] = d->p[1];
+                       d->p[1] = temp;
+                       return true;
+               }
+               else if (d->hitPoint[3])
+               {
+                       // There are three cases here: aligned, horizontal, & vertical.
+                       // Aligned and horizontal do the same thing, vertical goes back to
+                       // linear.
+                       if (d->subtype == DTLinearVert)
+                               d->subtype = DTLinear;
+                       else
+                               d->subtype = DTLinearVert;
+
+                       return true;
+               }
+               else if (d->hitPoint[4])
+               {
+                       // There are three cases here: aligned, horizontal, & vertical.
+                       // Aligned and vertical do the same thing, horizontal goes back to
+                       // linear.
+                       if (d->subtype == DTLinearHorz)
+                               d->subtype = DTLinear;
+                       else
+                               d->subtype = DTLinearHorz;
+
+                       return true;
+               }
+       }
+
+       return false;
+}
 
 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];
+//     Object * obj = (Object *)hover[0];
+       Object * obj = dragged;
 //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"));
 
@@ -1933,9 +2918,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;
@@ -1943,20 +2946,21 @@ void DrawingView::HandleObjectMovement(Point point)
                }
 
                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];
+                       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;
+
        case OTArc:
                if (obj->hitPoint[0])
                        obj->p[0] = point;
@@ -2025,10 +3029,50 @@ void DrawingView::HandleObjectMovement(Point point)
                }
 
                break;
+
+       case OTDimension:
+               if (obj->hitPoint[0])
+                       obj->p[0] = point;
+               else if (obj->hitPoint[1])
+                       obj->p[1] = point;
+               else if (obj->hitObject)
+               {
+                       // 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:
@@ -2036,3 +3080,22 @@ 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;
+       }
+}