]> Shamusworld >> Repos - architektonas/blobdiff - src/drawingview.cpp
Fixed click selection and pen functionality.
[architektonas] / src / drawingview.cpp
index bdc3540b0442fc51b243e6096da6a94b362d2632..d8726fed5d91d82d86e2616877306ba2a1401cb6 100644 (file)
@@ -1,13 +1,14 @@
+//
 // 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>
 //
 // Who  When        What
-// ---  ----------  -------------------------------------------------------------
+// ---  ----------  ------------------------------------------------------------
 // JLH  03/22/2011  Created this file
 // JLH  09/29/2011  Added middle mouse button panning
 //
 //
 // - 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 "drawingview.h"
 
 #include <stdint.h>
+#include "geometry.h"
+#include "global.h"
 #include "mathconstants.h"
-
-#include "arc.h"
-#include "circle.h"
-#include "dimension.h"
-#include "line.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),
+       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(Vector(0, 0)),
-       /*gridSpacing(12.0),*/ gridPixels(0), collided(false), rotateTool(false),
-       rx(150.0), ry(150.0),
-       scrollDrag(false), addLineTool(false), addCircleTool(false),
-       addDimensionTool(false),
-       toolAction(NULL)
-{
-       document.isTopLevelContainer = true;
+       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);
 
-       Object::gridSpacing = 12.0;
-//     toolPalette = new ToolWindow();
-//     CreateCursors();
-//     setCursor(cur[TOOLSelect]);
-//     setMouseTracking(true);
+       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)
 
-       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
+       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
@@ -89,16 +86,16 @@ arbitrary 12 pixels) to anything we want (within reason, of course :-).
 
 The drawing enforces the grid spacing through the drawing->gridSpacing variable.
 
-       drawing->gridSpacing = 12.0 / Painter::zoom;
+       drawing->gridSpacing = 12.0 / Global::zoom;
 
-Painter::zoom is the zoom factor for the drawing, and all mouse clicks are
-translated to Cartesian coordinates through this. (Initially, Painter::zoom is
+Global::zoom is the zoom factor for the drawing, and all mouse clicks are
+translated to Cartesian coordinates through this. (Initially, Global::zoom is
 set to 1.0. SCREEN_ZOOM is set to 1.0/4.0.)
 
 Really, the 100% zoom level can be set at *any* zoom level, it's more of a
 convenience function than any measure of absolutes. Doing things that way we
 could rid ourselves of the whole SCREEN_ZOOM parameter and all the attendant
-shittyness that comes with it.
+shittiness that comes with it.
 
 However, it seems that SCREEN_ZOOM is used to make text and arrow sizes show up
 a certain way, which means we should probably create something else in those
@@ -112,199 +109,214 @@ inch regardless of the zoom level a piece of text can be larger or smaller than
 this. Maybe this is the case for having a base unit and basing point sizes off
 of that.
 
-Here's what's been figured out. Painter::zoom is simply the ratio of pixels to
+Here's what's been figured out. Global::zoom is simply the ratio of pixels to
 base units. What that means is that if you have a 12px grid with a 6" grid size
-(& base unit of "inches"), Painter::zoom becomes 12px / 6" = 2.0 px/in.
+(& base unit of "inches"), Global::zoom becomes 12px / 6" = 2.0 px/in.
 
 Dimensions now have a "size" parameter to set their absolute size in relation
 to the base unit. ATM, the arrows are drawn in pixels, but also scaled by
-Painter::zoom *and* size. Same with the dimension text; it's drawn at 10pt and
+Global::zoom *and* size. Same with the dimension text; it's drawn at 10pt and
 scaled the same way as the arrowheads.
 
 Need a way to scale line widths as well. :-/ Shouldn't be too difficult, just
 need a thickness parameter similar to the "size" param for dimensions. (And now
 we do! :-)
-
 */
-       SetGridSize(12);
 }
 
-
-void DrawingView::SetRotateToolActive(bool state/*= true*/)
+void DrawingView::DrawBackground(Painter * painter)
 {
-       rotateTool = state;
-       update();
+       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, 0xE6E6FF, 0.25, start, size);
+
+       if (Global::gridSpacing <= 0.5)
+               DrawSubGrid(painter, 0xE6E6FF, 0.5, start, size);
+
+       painter->SetPen(QPen(QColor(0xE0, 0xE0, 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);
+}
 
-void DrawingView::SetToolActive(Action * action)
+//
+// Basically, we just make a single pass through the Container. If the layer #
+// is less than the layer # being deleted, then do nothing. If the layer # is
+// equal to the layer # being deleted, then delete the object. If the layer #
+// is greater than the layer # being deleted, then set the layer # to its layer
+// # - 1.
+//
+void DrawingView::DeleteCurrentLayer(int layer)
 {
-       if (action != NULL)
+       VPVectorIter i = document.objects.begin();
+
+       while (i != document.objects.end())
        {
-               toolAction = action;
-               connect(toolAction, SIGNAL(ObjectReady(Object *)), this,
-                       SLOT(AddNewObjectToDocument(Object *)));
+               Object * obj = (Object *)(*i);
+
+               if (obj->layer < layer)
+                       i++;
+               else if (obj->layer == layer)
+               {
+                       document.objects.erase(i);
+                       delete obj;
+               }
+               else
+               {
+                       obj->layer--;
+                       i++;
+               }
        }
-}
 
+       // We've just done a destructive action, so update the screen!
+       update();
+}
 
-void DrawingView::SetGridSize(uint32_t size)
+void DrawingView::HandleLayerToggle(void)
 {
-       // Sanity check
-       if (size == gridPixels)
-               return;
+       // A layer's visibility was toggled, so update the screen...
+       update();
+}
 
-       // Recreate the background bitmap
-       gridPixels = 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));
+//
+// A layer was moved up or down in the layer list, so we have to swap the
+// document's object's layer numbers in the layers that were swapped.
+//
+void DrawingView::HandleLayerSwap(int layer1, int layer2)
+{
+       HandleLayerSwap(layer1, layer2, document.objects);
+}
 
-       for(int i=0; i<(BACKGROUND_MAX_SIZE-1); i+=gridPixels)
+/*
+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++)
        {
-               pmp.drawLine(i, 0, i, BACKGROUND_MAX_SIZE - 1);
-               pmp.drawLine(0, i, BACKGROUND_MAX_SIZE - 1, i);
-       }
+               Object * obj = (Object *)(*i);
 
-       pmp.end();
+               if (obj->layer == layer1)
+                       obj->layer = layer2;
+               else if (obj->layer == layer2)
+                       obj->layer = layer1;
 
-       // Set up new BG brush & zoom level (pixels per base unit)
-//     Painter::zoom = gridPixels / gridSpacing;
-       Painter::zoom = gridPixels / Object::gridSpacing;
-       UpdateGridBackground();
+               if (obj->type == OTContainer)
+                       HandleLayerSwap(layer1, layer2, ((Container *)obj)->objects);
+       }
 }
 
-
-void DrawingView::UpdateGridBackground(void)
+void DrawingView::HandlePenStamp(QAction * action)
 {
-       // Transform the origin to Qt coordinates
-       Vector pixmapOrigin = Painter::CartesianToQtCoords(Vector());
-       int x = (int)pixmapOrigin.x;
-       int y = (int)pixmapOrigin.y;
-       // Use mod arithmetic to grab the correct swatch of background
-/*
-Negative numbers still screw it up... Need to think about what we're
-trying to do here. The fact that it worked with 72 seems to have been pure luck.
-It seems the problem is negative numbers: We can't let that happen.
-When taking away the zero, it pops over 1 px at zero, then goes about 1/2 a
-grid at x<0.
-
-The bitmap looks like this:
-
-+---+---+---+---+---
-|   |   |   |   |
-|   |   |   |   |
-+---+---+---+---+---
-|   |   |   |   |
-|   |   |   |   |
-|   |   |   |   |
-
-@ x = 1, we want it to look like:
-
--+---+---+---+---+---
- |   |   |   |   |
- |   |   |   |   |
--+---+---+---+---+---
- |   |   |   |   |
- |   |   |   |   |
- |   |   |   |   |
-
-Which means we need to grab the sample from x = 3. @ x = -1:
-
----+---+---+---+---
-   |   |   |   |
-   |   |   |   |
----+---+---+---+---
-   |   |   |   |
-   |   |   |   |
-   |   |   |   |
-
-Which means we need to grab the sample from x = 1. Which means we have to take
-the mirror of the modulus of gridPixels.
-
-Doing a mod of a negative number is problematic: 1st, the compiler converts the
-negative number to an unsigned int, then it does the mod. Gets you wrong answers
-most of the time, unless you use a power of 2. :-P So what we do here is just
-take the modulus of the negation, which means we don't have to worry about
-mirroring it later.
-
-The positive case looks gruesome (and it is) but it boils down to this: We take
-the modulus of the X coordinate, then mirror it by subtraction from the
-maximum (in this case, gridPixels). This gives us a number in the range of 1 to
-gridPixels. But we need the case where the result equalling gridPixels to be
-zero; so we do another modulus operation on the result to achieve this.
-*/
-       if (x < 0)
-               x = -x % gridPixels;
-       else
-               x = (gridPixels - (x % gridPixels)) % gridPixels;
+       PenWidget * pw = (PenWidget *)action->parentWidget();
+       pw->dropperAction->setChecked(false);
+       Global::penDropper = false;
+       Global::penStamp = action->isChecked();
 
-       if (y < 0)
-               y = -y % gridPixels;
+       if (Global::penStamp)
+               setCursor(curMarker);
        else
-               y = (gridPixels - (y % gridPixels)) % gridPixels;
+               setCursor(Qt::ArrowCursor);
 
-       // Here we grab a section of the bigger pixmap, so that the background
-       // *looks* like it's scrolling...
-       QPixmap pm = gridBackground.copy(x, y, gridPixels, gridPixels);
-       QPalette pal = palette();
-       pal.setBrush(backgroundRole(), QBrush(pm));
-       setAutoFillBackground(true);
-       setPalette(pal);
-}
+       if (Global::penStamp == false)
+               ClearSelected(document.objects);
 
+       update();
+}
 
-void DrawingView::AddNewObjectToDocument(Object * object)
+void DrawingView::HandlePenDropper(QAction * action)
 {
-       if (object)
-       {
-               object->Reparent(&document);
-               document.Add(object);
-               update();
-       }
-//printf("DrawingView::AddNewObjectToDocument(). object=%08X\n", object);
-}
+       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)
 {
-       // This is undoing the transform, e.g. going from client coords to local coords.
-       // In essence, the height - y is height + (y * -1), the (y * -1) term doing the
-       // conversion of the y-axis from increasing bottom to top.
+       // This is undoing the transform, e.g. going from client coords to local
+       // coords. In essence, the height - y is height + (y * -1), the (y * -1)
+       // term doing the conversion of the y-axis from increasing bottom to top.
        return QPoint(offsetX + event->x(), offsetY + (size().height() - event->y()));
 }
 
-
 QPoint DrawingView::GetAdjustedClientPosition(int x, int y)
 {
        // VOODOO ALERT (ON Y COMPONENT!!!!) (eh?)
        // No voodoo here, it's just grouped wrong to see it. It should be:
-       // -offsetY + (size.height() + (y * -1.0)) <-- this is wrong, offsetY should be positive
+       // -offsetY + (size.height() + (y * -1.0)) <-- this is wrong, offsetY should be positive [why? we use -offsetX after all]
        return QPoint(-offsetX + x, (size().height() - (-offsetY + y)) * +1.0);
 }
 
-
-#if 0
-//
-// This looks strange, but it's really quite simple: We want a point that's
-// more than half-way to the next grid point to snap there while conversely we
-// want a point that's less than half-way to to the next grid point then snap
-// to the one before it. So we add half of the grid spacing to the point, then
-// divide by it so that we can remove the fractional part, then multiply it
-// back to get back to the correct answer.
-//
-Vector DrawingView::SnapPointToGrid(Vector point)
+void DrawingView::focusOutEvent(QFocusEvent * /*event*/)
 {
-       point += gridSpacing / 2.0;             // *This* adds to Z!!!
-       point /= gridSpacing;
-       point.x = floor(point.x);//need to fix this for negative numbers...
-       point.y = floor(point.y);
-       point.z = 0;                                    // Make *sure* Z doesn't go anywhere!!!
-       point *= gridSpacing;
-       return point;
+       // 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);
 }
-#endif
 
+void DrawingView::focusInEvent(QFocusEvent * /*event*/)
+{
+       if (Global::penStamp)
+               setCursor(curMarker);
+       else if (Global::penDropper)
+               setCursor(curDropper);
+}
 
 void DrawingView::paintEvent(QPaintEvent * /*event*/)
 {
@@ -314,180 +326,2717 @@ void DrawingView::paintEvent(QPaintEvent * /*event*/)
        if (useAntialiasing)
                qtPainter.setRenderHint(QPainter::Antialiasing);
 
-//     Painter::screenSize = Vector(size().width(), size().height());
-       Object::SetViewportHeight(size().height());
+       Global::viewportHeight = size().height();
 
-       // Draw coordinate axes
+       DrawBackground(&painter);
 
+       // Draw coordinate axes
        painter.SetPen(QPen(Qt::blue, 1.0, Qt::DotLine));
        painter.DrawLine(0, -16384, 0, 16384);
        painter.DrawLine(-16384, 0, 16384, 0);
 
-       // Draw supplemental (tool related) points
-// NOTE that this can be done as an action!
-// In that case, we would need access to the document...
-// [We can do that by making the document a class object...]
-       if (rotateTool)
+       // Do object rendering...
+       for(int i=0; i<Global::numLayers; i++)
        {
-               painter.SetPen(QPen(QColor(0, 200, 0), 2.0, Qt::SolidLine));
-               painter.DrawLine(rx - 10, ry, rx + 10, ry);
-               painter.DrawLine(rx, ry - 10, rx, ry + 10);
+               if (Global::layerHidden[i] == false)
+                       RenderObjects(&painter, document.objects, i);
        }
 
-// Maybe we can make the grid into a background brush instead, and let Qt deal
-// with it??? YES!!
-
-       // The top level document takes care of rendering for us...
-       document.Draw(&painter);
-
-       if (toolAction)
+       // Do tool rendering, if any...
+       if (Global::tool)
        {
-//             painter.SetPen(QPen(Qt::green, 1.0, Qt::DashLine));
-               painter.SetPen(QPen(QColor(200, 100, 0, 255), 1.0, Qt::DashLine));
-               painter.DrawCrosshair(oldPoint);
-               toolAction->Draw(&painter);
+               if (Global::toolSuppressCrosshair == false)
+               {
+                       painter.SetPen(QPen(QColor(200, 100, 0, 255), 1.0, Qt::DashLine));
+                       painter.DrawCrosshair(oldPoint);
+               }
+
+               ToolDraw(&painter);
        }
 
-       if (Object::selectionInProgress)
+       // Do selection rectangle rendering, if any
+       if (Global::selectionInProgress)
        {
-//             painter.SetPen(QPen(Qt::green, 1.0, Qt::SolidLine));
                painter.SetPen(QPen(QColor(255, 127, 0, 255)));
-//             painter.SetBrush(QBrush(Qt::NoBrush));
                painter.SetBrush(QBrush(QColor(255, 127, 0, 100)));
-               painter.DrawRect(Object::selection);
+               painter.DrawRect(Global::selection);
        }
-}
 
+       if (hoveringIntersection)
+               painter.DrawHandle(intersectionPoint);
 
-void DrawingView::resizeEvent(QResizeEvent * /*event*/)
-{
-       Painter::screenSize = Vector(size().width(), size().height());
-       UpdateGridBackground();
-}
+       if (hoverPointValid)
+               painter.DrawHandle(hoverPoint);
 
+       if (!informativeText.isEmpty())
+               painter.DrawInformativeText(informativeText);
+}
 
-void DrawingView::mousePressEvent(QMouseEvent * event)
+//
+// Renders objects in the passed in vector
+//
+/*
+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*/)
 {
-       if (event->button() == Qt::LeftButton)
+       for(VPVectorIter i=v.begin(); i!=v.end(); i++)
        {
-               Vector point = Painter::QtToCartesianCoords(Vector(event->x(), event->y()));
+               Object * obj = (Object *)(*i);
+               float scaledThickness = Global::scale * obj->thickness;
 
-// Problem with this: Can't select stuff very well with the snap grid on.
-// Completely screws things up, as sometimes things don't fall on the grid.
-/*
-So, how to fix this? Have the Object check itself?
-Maybe we can fix this by having the initial point not be snapped, but when there's
-a drag, we substitute the snapped point 'oldPoint' which the Object keeps track of
-internally to know how far it was dragged...
+               // If the object isn't on the current layer being drawn, skip it
+               if (!ignoreLayer && (obj->layer != layer))
+                       continue;
+
+               if ((Global::tool == TTRotate) && ctrlDown && obj->selected)
+               {
+                       painter->SetPen(0x00FF00, 2.0, LSSolid);
+               }
+               else
+               {
+                       painter->SetPen(obj->color, Global::zoom * scaledThickness, obj->style);
+                       painter->SetBrush(obj->color);
+
+                       // 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);
+               }
+
+               switch (obj->type)
+               {
+               case OTLine:
+                       painter->DrawLine(obj->p[0], obj->p[1]);
+
+                       if (obj->hitPoint[0])
+                               painter->DrawHandle(obj->p[0]);
+
+                       if (obj->hitPoint[1])
+                               painter->DrawHandle(obj->p[1]);
+
+                       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]);
+
+                       if (obj->hitPoint[0])
+                               painter->DrawHandle(obj->p[0]);
+
+                       break;
+
+               case OTArc:
+                       painter->DrawArc(obj->p[0], obj->radius[0], obj->angle[0], obj->angle[1]);
+
+                       if (obj->hitPoint[0])
+                               painter->DrawHandle(obj->p[0]);
+
+                       if (obj->hitPoint[1])
+                               painter->DrawHandle(obj->p[0] + (Vector(cos(obj->angle[0]), sin(obj->angle[0])) * obj->radius[0]));
+
+                       if (obj->hitPoint[2])
+                               painter->DrawHandle(obj->p[0] + (Vector(cos(obj->angle[0] + obj->angle[1]), sin(obj->angle[0] + obj->angle[1])) * obj->radius[0]));
 
-Now we do... :-/
+                       break;
+
+               case OTDimension:
+               {
+                       Dimension * d = (Dimension *)obj;
+
+                       Vector v(d->p[0], d->p[1]);
+                       double angle = v.Angle();
+                       Vector unit = v.Unit();
+                       d->lp[0] = d->p[0], d->lp[1] = d->p[1];
+                       Vector ortho;
+                       double x1, y1, length;
+
+                       if (d->subtype == DTLinearVert)
+                       {
+                               if ((angle < 0) || (angle > HALF_TAU))
+                               {
+                                       x1 = (d->p[0].x > d->p[1].x ? d->p[0].x : d->p[1].x);
+                                       y1 = (d->p[0].y > d->p[1].y ? d->p[0].y : d->p[1].y);
+                                       ortho = Vector(1.0, 0);
+                                       angle = THREE_QTR_TAU;
+                               }
+                               else
+                               {
+                                       x1 = (d->p[0].x > d->p[1].x ? d->p[1].x : d->p[0].x);
+                                       y1 = (d->p[0].y > d->p[1].y ? d->p[1].y : d->p[0].y);
+                                       ortho = Vector(-1.0, 0);
+                                       angle = QTR_TAU;
+                               }
+
+                               d->lp[0].x = d->lp[1].x = x1;
+                               length = fabs(d->p[0].y - d->p[1].y);
+                       }
+                       else if (d->subtype == DTLinearHorz)
+                       {
+                               if ((angle < QTR_TAU) || (angle > THREE_QTR_TAU))
+                               {
+                                       x1 = (d->p[0].x > d->p[1].x ? d->p[0].x : d->p[1].x);
+                                       y1 = (d->p[0].y > d->p[1].y ? d->p[0].y : d->p[1].y);
+                                       ortho = Vector(0, 1.0);
+                                       angle = 0;
+                               }
+                               else
+                               {
+                                       x1 = (d->p[0].x > d->p[1].x ? d->p[1].x : d->p[0].x);
+                                       y1 = (d->p[0].y > d->p[1].y ? d->p[1].y : d->p[0].y);
+                                       ortho = Vector(0, -1.0);
+                                       angle = HALF_TAU;
+                               }
+
+                               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(d->lp[0], d->lp[1]).Angle();
+                               ortho = Vector::Normal(d->lp[0], d->lp[1]);
+                               length = v.Magnitude();
+                       }
+
+                       unit = Vector(d->lp[0], d->lp[1]).Unit();
+
+                       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);
+
+               /*
+               The numbers hardcoded into here, what are they?
+               I believe they are pixels.
+               */
+                       // Draw extension lines (if certain type)
+                       painter->DrawLine(p3, p5);
+                       painter->DrawLine(p4, p6);
+
+                       // Calculate whether or not the arrowheads are too crowded to put
+                       // inside the extension lines. 9.0 is the length of the arrowhead.
+                       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%
+                       if (t > 0.58)
+                       {
+                               // Draw main dimension line + arrowheads
+                               painter->DrawLine(p1, p2);
+                               painter->DrawArrowhead(p1, p2, scaledThickness);
+                               painter->DrawArrowhead(p2, p1, scaledThickness);
+                       }
+                       else
+                       {
+                               // Draw outside arrowheads
+                               Point p7 = p1 - (unit * 9.0 * scaledThickness);
+                               Point p8 = p2 + (unit * 9.0 * scaledThickness);
+                               painter->DrawArrowhead(p1, p7, scaledThickness);
+                               painter->DrawArrowhead(p2, p8, scaledThickness);
+                               painter->DrawLine(p1, p1 - (unit * 14.0 * scaledThickness));
+                               painter->DrawLine(p2, p2 + (unit * 14.0 * scaledThickness));
+                       }
+
+                       // Draw length of dimension line...
+                       painter->SetFont(QFont("Arial", 8.0 * Global::zoom * scaledThickness));
+                       Point ctr = p2 + (Vector(p2, p1) / 2.0);
+
+                       QString dimText;
+
+                       if (length < 12.0)
+                               dimText = QString("%1\"").arg(length);
+                       else
+                       {
+                               double feet = (double)((int)length / 12);
+                               double inches = length - (feet * 12.0);
+
+                               if (inches == 0)
+                                       dimText = QString("%1'").arg(feet);
+                               else
+                                       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().
 */
-#if 0
-               if (Object::snapToGrid)
-                       point = Object::SnapPointToGrid(point);
-#endif
+                       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;
+               }
 
-               collided = document.Collided(point);
+               case OTText:
+               {
+                       Text * t = (Text *)obj;
 
-               if (collided)
-                       update();       // Do an update if collided with at least *one* object in the document
+                       if (t->measured == false)
+                       {
+                               t->extents = painter->MeasureTextObject(t->s.c_str(), scaledThickness);
+                               t->measured = true;
+                       }
 
-               if (toolAction)
-                       toolAction->MouseDown(point);
+                       painter->DrawTextObject(t->p[0], t->s.c_str(), scaledThickness, t->angle[0]);
+                       break;
+               }
 
-               // Didn't hit any object and not using a tool, so do a selection rectangle
-               if (!(collided || toolAction))
+               case OTSpline:
                {
-                       Object::selectionInProgress = true;
-                       Object::selection.setTopLeft(QPointF(point.x, point.y));
-                       Object::selection.setBottomRight(QPointF(point.x, point.y));
+                       break;
+               }
+
+               case OTPolygon:
+               {
+                       break;
+               }
+
+               case OTPolyline:
+               {
+                       break;
+               }
+
+               case OTContainer:
+               {
+                       // Containers require recursive rendering...
+                       Container * c = (Container *)obj;
+//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);
+                               painter->DrawRectCorners(Rect(c->p[0], c->p[1]));
+                       }
+
+                       break;
+               }
+
+               default:
+                       break;
                }
        }
-       else if (event->button() == Qt::MiddleButton)
-       {
-               scrollDrag = true;
-               oldPoint = Vector(event->x(), event->y());
-               // Should also change the mouse pointer as well...
-               setCursor(Qt::SizeAllCursor);
-       }
-}
 
+       supressSelected = false;
+}
 
-void DrawingView::mouseMoveEvent(QMouseEvent * event)
+//
+// This toggles the selection being hovered (typically, only 1 object).  We
+// toggle because the CTRL key might be held, in which case, we want to
+// deselect a selected object.
+//
+void DrawingView::HandleSelectionClick(VPVector & v)
 {
-       Vector point = Painter::QtToCartesianCoords(Vector(event->x(), event->y()));
-       Object::selection.setBottomRight(QPointF(point.x, point.y));
-
-       if (event->buttons() & Qt::MiddleButton)
+       if (ctrlDown)
        {
-               point = Vector(event->x(), event->y());
-               // Since we're using Qt coords for scrolling, we have to adjust them here to
-               // conform to Cartesian coords, since the origin is using Cartesian. :-)
-               Vector delta(point, oldPoint);
-               delta /= Painter::zoom;
-               delta.y = -delta.y;
-               Painter::origin -= delta;
+               for(VPVectorIter i=v.begin(); i!=v.end(); i++)
+               {
+                       Object * obj = (Object *)(*i);
+
+                       if (obj->hovered)
+                               obj->selected = !obj->selected;
+               }
 
-               UpdateGridBackground();
-               update();
-               oldPoint = point;
                return;
        }
 
-       // Grid processing...
-       if ((event->buttons() & Qt::LeftButton) && Object::snapToGrid)
+       for(VPVectorIter i=v.begin(); i!=v.end(); i++)
+               ((Object *)(*i))->selected = false;
+
+       // Check if the hover changed, and if so, reset the selection stack
+       if (oldHover.size() != v.size())
        {
-               point = Object::SnapPointToGrid(point);
+               oldHover = v;
+               currentSelect = 0;
        }
+       else
+       {
+               // Select next object in the stack under the cursor
+               currentSelect++;
 
-       oldPoint = point;
-//we should keep track of the last point here and only pass this down *if* the point
-//changed...
-       document.PointerMoved(point);
+               if (currentSelect >= v.size())
+                       currentSelect = 0;
+       }
 
-       if (document.NeedsUpdate() || Object::selectionInProgress)
-               update();
+       dragged = (Object *)v[currentSelect];
+       dragged->selected = true;
+}
 
-       if (toolAction)
+VPVector DrawingView::GetSelection(void)
+{
+       VPVector v;
+
+       for(VPVectorIter i=document.objects.begin(); i!=document.objects.end(); i++)
+       {
+               if (((Object *)(*i))->selected)
+                       v.push_back(*i);
+       }
+
+       return v;
+}
+
+//
+// When testing for hovered intersections, we need to be able to exclude some
+// objects which have funky characteristics or handles; so we allow for that
+// here.
+//
+VPVector DrawingView::GetHovered(bool exclude/*= false*/)
+{
+       VPVector v;
+
+       for(VPVectorIter i=document.objects.begin(); i!=document.objects.end(); i++)
        {
-               if (Object::snapToGrid)
+               Object * obj = (Object *)(*i);
+
+               if (obj->hovered)
                {
-                       point = Object::SnapPointToGrid(point);
-                       oldPoint = point;
+                       if (exclude
+                               && ((obj->type == OTDimension)
+                                       || ((obj->type == OTCircle) && (obj->hitPoint[0] == true))
+                                       || ((obj->type == OTArc) && (obj->hitPoint[0] == true))
+                                       || (draggingObject && (obj == dragged))))
+                               continue;
+
+                       v.push_back(*i);
                }
+       }
 
-               toolAction->MouseMoved(point);
-               update();
+       return v;
+}
+
+void DrawingView::MoveSelectedToLayer(int layer)
+{
+       for(VPVectorIter i=document.objects.begin(); i!=document.objects.end(); i++)
+       {
+               Object * obj = (Object *)(*i);
+
+               if (obj->selected || obj->hovered)
+                       obj->layer = layer;
        }
 }
 
+void DrawingView::resizeEvent(QResizeEvent * /*event*/)
+{
+       Global::screenSize = Vector(size().width(), size().height());
+}
 
-void DrawingView::mouseReleaseEvent(QMouseEvent * event)
+void DrawingView::ToolHandler(int mode, Point p)
 {
-       if (event->button() == Qt::LeftButton)
+       // Drop angle snap until it's needed
+       angleSnap = false;
+
+       if (Global::tool == TTLine)
+               LineHandler(mode, p);
+       else if (Global::tool == TTCircle)
+               CircleHandler(mode, p);
+       else if (Global::tool == TTArc)
+               ArcHandler(mode, p);
+       else if (Global::tool == TTRotate)
+               RotateHandler(mode, p);
+       else if (Global::tool == TTMirror)
+               MirrorHandler(mode, p);
+       else if (Global::tool == TTDimension)
+               DimensionHandler(mode, p);
+       else if (Global::tool == TTDelete)
+               DeleteHandler(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)
+       {
+               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);
+               }
+       }
+       else if (Global::tool == TTCircle)
+       {
+               if (Global::toolState == TSNone)
+               {
+                       painter->DrawHandle(toolPoint[0]);
+               }
+               else if ((Global::toolState == TSPoint2) && shiftDown)
+               {
+                       painter->DrawHandle(toolPoint[1]);
+               }
+               else
+               {
+                       painter->DrawCross(toolPoint[0]);
+                       double length = Vector::Magnitude(toolPoint[0], toolPoint[1]);
+                       painter->SetBrush(QBrush(Qt::NoBrush));
+                       painter->DrawEllipse(toolPoint[0], length, length);
+                       QString text = tr("Radius: %1 in.");
+                       informativeText = text.arg(length);
+               }
+       }
+       else if (Global::tool == TTArc)
+       {
+               if (Global::toolState == TSNone)
+               {
+                       painter->DrawHandle(toolPoint[0]);
+               }
+               else if (Global::toolState == TSPoint2)
+               {
+                       double length = Vector::Magnitude(toolPoint[0], toolPoint[1]);
+                       painter->SetBrush(QBrush(Qt::NoBrush));
+                       painter->DrawEllipse(toolPoint[0], length, length);
+                       painter->DrawLine(toolPoint[0], toolPoint[1]);
+                       painter->DrawHandle(toolPoint[1]);
+                       QString text = tr("Radius: %1 in.");
+                       informativeText = text.arg(length);
+               }
+               else if (Global::toolState == TSPoint3)
+               {
+                       double angle = Vector::Angle(toolPoint[0], toolPoint[2]);
+                       painter->DrawLine(toolPoint[0], toolPoint[2]);
+                       painter->SetBrush(QBrush(Qt::NoBrush));
+                       painter->DrawEllipse(toolPoint[0], toolPoint[1].x, toolPoint[1].x);
+                       painter->DrawHandle(toolPoint[0] + (Vector(cos(angle), sin(angle)) * toolPoint[1].x));
+                       QString text = tr("Angle start: %1") + QChar(0x00B0);
+                       informativeText = text.arg(RADIANS_TO_DEGREES * angle);
+               }
+               else
+               {
+                       double angle = Vector::Angle(toolPoint[0], toolPoint[3]);
+                       double span = angle - toolPoint[2].x;
+
+                       if (span < 0)
+                               span += TAU;
+
+                       painter->DrawLine(toolPoint[0], toolPoint[3]);
+                       painter->SetBrush(QBrush(Qt::NoBrush));
+                       painter->DrawEllipse(toolPoint[0], toolPoint[1].x, toolPoint[1].x);
+                       painter->SetPen(0xFF00FF, 2.0, LSSolid);
+                       painter->DrawArc(toolPoint[0], toolPoint[1].x, toolPoint[2].x, span);
+                       painter->DrawHandle(toolPoint[0] + (Vector(cos(angle), sin(angle)) * toolPoint[1].x));
+                       QString text = tr("Arc span: %1") + QChar(0x00B0);
+                       informativeText = text.arg(RADIANS_TO_DEGREES * span);
+               }
+       }
+       else if (Global::tool == TTRotate)
        {
-               document.PointerReleased();
+               if ((Global::toolState == TSNone) || (Global::toolState == TSPoint1))
+                       painter->DrawHandle(toolPoint[0]);
+               else if ((Global::toolState == TSPoint2) && shiftDown)
+                       painter->DrawHandle(toolPoint[1]);
+               else
+               {
+                       if (toolPoint[0] == toolPoint[1])
+                               return;
 
-//We need to update especially if nothing collided and the state needs to change. !!! FIX !!!
-//could set it up to use the document's update function (assumes that all object updates
-//are being reported correctly:
-//             if (document.NeedsUpdate())
-//             if (collided)
-                       update();       // Do an update if collided with at least *one* object in the document
+                       painter->DrawLine(toolPoint[0], toolPoint[1]);
+
+                       double absAngle = (Vector(toolPoint[1] - toolPoint[0]).Angle()) * RADIANS_TO_DEGREES;
+                       QString text = QChar(0x2221) + QObject::tr(": %1");
+                       informativeText = text.arg(absAngle);
+
+                       if (ctrlDown)
+                               informativeText += " (Copy)";
+               }
+       }
+       else if (Global::tool == TTMirror)
+       {
+               if ((Global::toolState == TSNone) || (Global::toolState == TSPoint1))
+                       painter->DrawHandle(toolPoint[0]);
+               else if ((Global::toolState == TSPoint2) && shiftDown)
+                       painter->DrawHandle(toolPoint[1]);
+               else
+               {
+                       if (toolPoint[0] == toolPoint[1])
+                               return;
+
+                       Point mirrorPoint = toolPoint[0] + Vector(toolPoint[1], toolPoint[0]);
+                       painter->DrawLine(mirrorPoint, toolPoint[1]);
 
-               if (toolAction)
-                       toolAction->MouseReleased();
+                       double absAngle = (Vector(toolPoint[1] - toolPoint[0]).Angle()) * RADIANS_TO_DEGREES;
 
-               if (Object::selectionInProgress)
+                       if (absAngle > 180.0)
+                               absAngle -= 180.0;
+
+                       QString text = QChar(0x2221) + QObject::tr(": %1");
+                       informativeText = text.arg(absAngle);
+
+                       if (ctrlDown)
+                               informativeText += " (Copy)";
+               }
+       }
+       else if (Global::tool == TTDimension)
+       {
+               if (Global::toolState == TSNone)
+               {
+                       painter->DrawHandle(toolPoint[0]);
+               }
+               else if ((Global::toolState == TSPoint2) && shiftDown)
+               {
+                       painter->DrawHandle(toolPoint[1]);
+               }
+               else
                {
-                       // Select all the stuff inside of selection
-                       Object::selectionInProgress = false;
+                       painter->DrawLine(toolPoint[0], toolPoint[1]);
+                       painter->DrawHandle(toolPoint[1]);
+
+                       Vector v(toolPoint[0], toolPoint[1]);
+                       double absAngle = v.Angle() * RADIANS_TO_DEGREES;
+                       double absLength = v.Magnitude();
+                       QString text = tr("Length: %1 in.\n") + QChar(0x2221) + tr(": %2");
+                       informativeText = text.arg(absLength).arg(absAngle);
                }
        }
-       else if (event->button() == Qt::MiddleButton)
+       else if (Global::tool == TTTrim)
+       {
+               if (toolObj[0] != NULL)
+               {
+                       // We're assuming ATM it's just a line...
+                       painter->SetPen(0xAF0000, 3.0, LSSolid);
+                       painter->DrawLine(toolPoint[0], toolPoint[1]);
+//                     QString text = tr("Arc span: %1") + QChar(0x00B0);
+//                     informativeText = text.arg(RADIANS_TO_DEGREES * span);
+               }
+       }
+       else if (Global::tool == TTParallel)
        {
-               scrollDrag = false;
-               setCursor(Qt::ArrowCursor);
        }
 }
 
+void DrawingView::LineHandler(int mode, Point p)
+{
+       switch (mode)
+       {
+       case ToolMouseDown:
+/*             toolObj[0] = NULL;
+
+               // Check to see if we can do a circle tangent snap
+               if (numHovered == 1)
+               {
+                       VPVector hover = GetHovered();
+                       Object * obj = (Object *)hover[0];
+
+                       // Save for later if the object clicked was a circle (need to check that it wasn't the center clicked on, because that will fuck up connecting centers of circles with lines... and now we do! :-)
+                       if ((obj->type == OTCircle) && (obj->hitPoint[0] == false))
+                               toolObj[0] = obj;
+               }*/
+
+               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;
+/*                     bool isCircle = false;
+
+                       if (numHovered == 1)
+                       {
+                               VPVector hover = GetHovered();
+                               Object * obj = (Object *)hover[0];
+
+                               if ((obj->type == OTCircle) && (obj->hitPoint[0] == false))
+                               {
+                                       isCircle = true;
+                                       toolObj[1] = obj;
+                               }
+                       }
+
+                       // Adjust initial point if it's on a circle (tangent point)
+                       if (toolObj[0] != NULL)
+                       {
+                               if (isCircle)
+                               {
+                                       Geometry::FindTangents(toolObj[0], toolObj[1]);
+
+                                       if (Global::numIntersectPoints > 0)
+                                       {
+                                               toolPoint[0] = Global::intersectPoint[0];
+                                               toolPoint[1] = Global::intersectPoint[1];
+                                       }
+                               }
+                               else
+                               {
+                                       Geometry::FindTangents(toolObj[0], p);
+
+                                       if (Global::numIntersectPoints > 0)
+                                               toolPoint[0] = Global::intersectPoint[0];
+                               }
+                       }
+                       else
+                       {
+                               if (isCircle)
+                               {
+                                       Geometry::FindTangents(toolObj[1], toolPoint[0]);
+
+                                       if (Global::numIntersectPoints > 0)
+                                               toolPoint[1] = Global::intersectPoint[0];
+                               }
+                       }*/
+               }
+
+               break;
+
+       case ToolMouseUp:
+               if (Global::toolState == TSNone)
+               {
+                       Global::toolState = TSPoint2;
+                       // Prevent spurious line from drawing...
+                       toolPoint[1] = toolPoint[0];
+               }
+               else if ((Global::toolState == TSPoint2) && shiftDown)
+               {
+                       // Key override is telling us to make a new line, not continue the
+                       // previous one.
+                       toolPoint[0] = toolPoint[1];
+               }
+               else
+               {
+                       Line * l = new Line(toolPoint[0], toolPoint[1], Global::penWidth, Global::penColor, Global::penStyle);
+                       l->layer = Global::activeLayer;
+                       document.objects.push_back(l);
+                       toolPoint[0] = toolPoint[1];
+               }
+       }
+}
+
+void DrawingView::CircleHandler(int mode, Point p)
+{
+       switch (mode)
+       {
+       case ToolMouseDown:
+               if (Global::toolState == TSNone)
+                       toolPoint[0] = p;
+               else
+                       toolPoint[1] = p;
+
+               break;
+
+       case ToolMouseMove:
+               if (Global::toolState == TSNone)
+                       toolPoint[0] = p;
+               else
+                       toolPoint[1] = p;
+
+               break;
+
+       case ToolMouseUp:
+               if (Global::toolState == TSNone)
+               {
+                       Global::toolState = TSPoint2;
+                       // Prevent spurious line from drawing...
+                       toolPoint[1] = toolPoint[0];
+               }
+               else if ((Global::toolState == TSPoint2) && shiftDown)
+               {
+                       // Key override is telling us to make a new line, not continue the
+                       // previous one.
+                       toolPoint[0] = toolPoint[1];
+               }
+               else
+               {
+                       double length = Vector::Magnitude(toolPoint[0], toolPoint[1]);
+                       Circle * c = new Circle(toolPoint[0], length, Global::penWidth, Global::penColor, Global::penStyle);
+                       c->layer = Global::activeLayer;
+                       document.objects.push_back(c);
+                       toolPoint[0] = toolPoint[1];
+                       Global::toolState = TSNone;
+               }
+       }
+}
+
+void DrawingView::ArcHandler(int mode, Point p)
+{
+       switch (mode)
+       {
+       case ToolMouseDown:
+               if (Global::toolState == TSNone)
+                       toolPoint[0] = p;
+               else if (Global::toolState == TSPoint2)
+                       toolPoint[1] = p;
+               else if (Global::toolState == TSPoint3)
+                       toolPoint[2] = p;
+               else
+                       toolPoint[3] = p;
+
+               break;
+
+       case ToolMouseMove:
+               if (Global::toolState == TSNone)
+                       toolPoint[0] = p;
+               else if (Global::toolState == TSPoint2)
+                       toolPoint[1] = p;
+               else if (Global::toolState == TSPoint3)
+               {
+                       toolPoint[2] = p;
+                       angleSnap = true;
+               }
+               else
+               {
+                       toolPoint[3] = p;
+                       angleSnap = true;
+               }
+
+               break;
+
+       case ToolMouseUp:
+               if (Global::toolState == TSNone)
+               {
+                       // Prevent spurious line from drawing...
+                       toolPoint[1] = toolPoint[0];
+                       Global::toolState = TSPoint2;
+               }
+               else if (Global::toolState == TSPoint2)
+               {
+                       if (shiftDown)
+                       {
+                               // Key override is telling us to start arc at new center, not
+                               // continue the current one.
+                               toolPoint[0] = toolPoint[1];
+                               return;
+                       }
+
+                       // Set the radius in toolPoint[1].x
+                       toolPoint[1].x = Vector::Magnitude(toolPoint[0], toolPoint[1]);
+                       Global::toolState = TSPoint3;
+               }
+               else if (Global::toolState == TSPoint3)
+               {
+                       // Set the angle in toolPoint[2].x
+                       toolPoint[2].x = Vector::Angle(toolPoint[0], toolPoint[2]);
+                       Global::toolState = TSPoint4;
+               }
+               else
+               {
+                       double endAngle = Vector::Angle(toolPoint[0], toolPoint[3]);
+                       double span = endAngle - toolPoint[2].x;
+
+                       if (span < 0)
+                               span += TAU;
+
+                       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;
+               }
+       }
+}
+
+void DrawingView::RotateHandler(int mode, Point p)
+{
+       switch (mode)
+       {
+       case ToolMouseDown:
+               if (Global::toolState == TSNone)
+               {
+                       toolPoint[0] = p;
+//                     SavePointsFrom(select, toolScratch);
+                       CopyObjects(select, toolScratch2);
+                       Global::toolState = TSPoint1;
+               }
+               else if (Global::toolState == TSPoint1)
+                       toolPoint[0] = p;
+               else
+                       toolPoint[1] = p;
+
+               break;
+
+       case ToolMouseMove:
+               if ((Global::toolState == TSPoint1) || (Global::toolState == TSNone))
+                       toolPoint[0] = p;
+               else if (Global::toolState == TSPoint2)
+               {
+                       toolPoint[1] = p;
+
+                       if (shiftDown)
+                               return;
+
+                       angleSnap = true;
+                       double angle = Vector(toolPoint[0], toolPoint[1]).Angle();
+                       VPVectorIter j = select.begin();
+//                     std::vector<Object>::iterator i = toolScratch.begin();
+                       VPVectorIter i = toolScratch2.begin();
+
+//                     for(; i!=toolScratch.end(); i++, j++)
+                       for(; i!=toolScratch2.end(); i++, j++)
+                       {
+//                             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);
+
+                               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)
+                               {
+//                                     objS->angle[0] = objT.angle[0] + angle;
+                                       objS->angle[0] = objT->angle[0] + angle;
+
+                                       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)
+               {
+                       Global::toolState = TSPoint2;
+                       // Prevent spurious line from drawing...
+                       toolPoint[1] = toolPoint[0];
+               }
+               else if ((Global::toolState == TSPoint2) && shiftDown)
+               {
+                       // Key override is telling us to make a new line, not continue the
+                       // previous one.
+                       toolPoint[0] = toolPoint[1];
+               }
+               else
+               {
+                       // Either we're finished with our rotate, or we're stamping a copy.
+                       if (ctrlDown)
+                       {
+                               // Stamp a copy of the selection at the current rotation & bail
+                               VPVector temp;
+                               CopyObjects(select, temp);
+                               ClearSelected(temp);
+                               AddObjectsTo(document.objects, temp);
+//                             RestorePointsTo(select, toolScratch);
+                               RestorePointsTo(select, toolScratch2);
+                               return;
+                       }
+
+                       toolPoint[0] = p;
+                       Global::toolState = TSPoint1;
+//                     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, 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, toolScratch2);
+               DeleteContents(toolScratch2);
+       }
+}
+
+void DrawingView::MirrorHandler(int mode, Point p)
+{
+       switch (mode)
+       {
+       case ToolMouseDown:
+               if (Global::toolState == TSNone)
+               {
+                       toolPoint[0] = p;
+                       SavePointsFrom(select, toolScratch);
+                       Global::toolState = TSPoint1;
+               }
+               else if (Global::toolState == TSPoint1)
+                       toolPoint[0] = p;
+               else
+                       toolPoint[1] = p;
+
+               break;
+
+       case ToolMouseMove:
+               if ((Global::toolState == TSPoint1) || (Global::toolState == TSNone))
+                       toolPoint[0] = p;
+               else if (Global::toolState == TSPoint2)
+               {
+                       toolPoint[1] = p;
+
+                       if (shiftDown)
+                               return;
+
+                       angleSnap = true;
+                       double angle = Vector(toolPoint[0], toolPoint[1]).Angle();
+                       VPVectorIter j = select.begin();
+                       std::vector<Object>::iterator i = toolScratch.begin();
+
+                       for(; i!=toolScratch.end(); i++, j++)
+                       {
+                               Object obj = *i;
+                               Point p1 = Geometry::MirrorPointAroundLine(obj.p[0], toolPoint[0], toolPoint[1]);
+                               Point p2 = Geometry::MirrorPointAroundLine(obj.p[1], toolPoint[0], toolPoint[1]);
+                               Object * obj2 = (Object *)(*j);
+                               obj2->p[0] = p1;
+                               obj2->p[1] = p2;
+
+/*
+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
+                                       obj2->angle[0] = (2.0 * angle) - obj.angle[0] - obj.angle[1];
+
+                                       if (obj2->angle[0] > TAU)
+                                               obj2->angle[0] -= TAU;
+                               }
+                       }
+               }
+
+               break;
+
+       case ToolMouseUp:
+               if (Global::toolState == TSPoint1)
+               {
+                       Global::toolState = TSPoint2;
+                       // Prevent spurious line from drawing...
+                       toolPoint[1] = toolPoint[0];
+               }
+               else if ((Global::toolState == TSPoint2) && shiftDown)
+               {
+                       // Key override is telling us to make a new line, not continue the
+                       // previous one.
+                       toolPoint[0] = toolPoint[1];
+               }
+               else
+               {
+                       // Either we're finished with our rotate, or we're stamping a copy.
+                       if (ctrlDown)
+                       {
+                               // Stamp a copy of the selection at the current rotation & bail
+                               VPVector temp;
+                               CopyObjects(select, temp);
+                               ClearSelected(temp);
+                               AddObjectsTo(document.objects, temp);
+                               RestorePointsTo(select, toolScratch);
+                               return;
+                       }
+
+                       toolPoint[0] = p;
+                       Global::toolState = TSPoint1;
+                       SavePointsFrom(select, toolScratch);
+               }
+
+               break;
+
+       case ToolKeyDown:
+               // Reset the selection if shift held down...
+               if (shiftDown)
+                       RestorePointsTo(select, toolScratch);
+
+               break;
+
+       case ToolKeyUp:
+               // Reset selection when key is let up
+               if (!shiftDown)
+                       MirrorHandler(ToolMouseMove, toolPoint[1]);
+
+               break;
+
+       case ToolCleanup:
+               RestorePointsTo(select, toolScratch);
+       }
+}
+
+void DrawingView::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, 0, Global::penWidth);
+                       d->layer = Global::activeLayer;
+                       document.objects.push_back(d);
+                       Global::toolState = TSNone;
+               }
+       }
+}
+
+void DrawingView::DeleteHandler(int mode, Point /*p*/)
+{
+       switch (mode)
+       {
+       case ToolMouseDown:
+       {
+               VPVector hovered = GetHovered();
+
+               RemoveHoveredObjects(document.objects);
+               DeleteContents(hovered);
+       }
+               break;
+
+       case ToolMouseMove:
+               break;
+
+       case ToolMouseUp:
+               break;
+
+       case ToolKeyDown:
+               break;
+
+       case ToolKeyUp:
+               break;
+
+       case ToolCleanup:
+               break;
+       }
+}
+
+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)
+{
+       switch (mode)
+       {
+       case ToolMouseDown:
+       {
+       }
+               break;
+
+       case ToolMouseMove:
+       {
+               // 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);
+               double t = 0, u = 1.0;
+
+               // 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)
+                       {
+                               // Skip endpoint-endpoint intersections
+                               if ((Global::numIntersectParams == 2)
+                                       && (Global::intersectParam[0] == 0
+                                               || Global::intersectParam[0] == 1.0)
+                                       && (Global::intersectParam[1] == 0
+                                               || Global::intersectParam[1] == 1.0))
+                                       continue;
+
+                               // Mark the line segment somehow (the side the mouse is on) so that it can be drawn & trimmed when we hit ToolMouseDown.
+                               if ((Global::intersectParam[0] > t) && (Global::intersectParam[0] < hoveredParam))
+                                       t = Global::intersectParam[0];
+
+                               if ((Global::intersectParam[0] < u) && (Global::intersectParam[0] > hoveredParam))
+                                       u = Global::intersectParam[0];
+                       }
+               }
+
+               toolParam[0] = t;
+               toolParam[1] = u;
+               toolPoint[0] = Geometry::GetPointForParameter(toolObj[0], t);
+               toolPoint[1] = Geometry::GetPointForParameter(toolObj[0], u);
+       }
+               break;
+
+       case ToolMouseUp:
+       {
+               // Bail out if there's no object to trim
+               if (toolObj[0] == NULL)
+                       return;
+
+               Vector v(toolObj[0]->p[0], toolObj[0]->p[1]);
+
+               // Check to see which case we have.
+               if ((toolParam[0] == 0) && (toolParam[1] == 1.0))
+               {
+                       // There was no intersection, so delete the object
+                       toolObj[0]->selected = true;
+                       DeleteSelectedObjects(document.objects);
+               }
+               else if (toolParam[0] == 0)
+               {
+                       // We delete the end near point #1
+                       toolObj[0]->p[0] = toolObj[0]->p[0] + (v * toolParam[1]);
+               }
+               else if (toolParam[1] == 1.0)
+               {
+                       // We delete the end near point #2
+                       toolObj[0]->p[1] = toolObj[0]->p[0] + (v * toolParam[0]);
+               }
+               else
+               {
+                       // We delete the segment in between, and create a new line in the process
+                       Point p1 = toolObj[0]->p[0] + (v * toolParam[0]);
+                       Point p2 = toolObj[0]->p[0] + (v * toolParam[1]);
+                       Point p3 = toolObj[0]->p[1];
+                       toolObj[0]->p[1] = p1;
+                       Line * l = new Line(p2, p3, toolObj[0]->thickness, toolObj[0]->color, toolObj[0]->style);
+                       document.objects.push_back(l);
+//                     Global::toolState = TSNone;
+               }
+
+               toolObj[0]->hitObject = toolObj[0]->hitPoint[0] = toolObj[0]->hitPoint[1] = false;
+               toolObj[0] = NULL;
+       }
+               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)
+       {
+               Vector point = Painter::QtToCartesianCoords(Vector(event->x(), event->y()));
+
+               // Handle tool processing, if any
+               if (Global::tool)
+               {
+                       if (hoveringIntersection)
+                               point = intersectionPoint;
+                       else if (hoverPointValid)
+                               point = hoverPoint;
+                       else if (Global::snapToGrid)
+                               point = SnapPointToGrid(point);
+
+                       ToolHandler(ToolMouseDown, point);
+                       return;
+               }
+
+               // Clear the selection only if CTRL isn't being held on click
+               if (!ctrlDown)
+                       ClearSelected(document.objects);
+
+               // If any objects are being hovered on click, deal with 'em
+               if (numHovered > 0)
+               {
+                       VPVector hover2 = GetHovered();
+                       dragged = (Object *)hover2[0];
+
+                       // Alert the pen widget
+                       if (Global::penDropper)
+                       {
+                               Global::penColor = dragged->color;
+                               Global::penWidth = dragged->thickness;
+                               Global::penStyle = dragged->style;
+                               emit ObjectSelected(dragged);
+                               return;
+                       }
+                       else 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 custom
+                       // object handle (like Dimension objects)
+                       else if (HandleObjectClicked())
+                       {
+                               update();
+                               return;
+                       }
+
+                       draggingObject = true;
+                       HandleSelectionClick(hover2);
+                       update();       // needed??
+
+                       // 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
+               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)
+       {
+               scrollDrag = true;
+               oldPoint = Vector(event->x(), event->y());
+               // Should also change the mouse pointer as well...
+               setCursor(Qt::SizeAllCursor);
+       }
+}
+
+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) || scrollDrag)
+       {
+               point = Vector(event->x(), event->y());
+               // Since we're using Qt coords for scrolling, we have to adjust them
+               // here to conform to Cartesian coords, since the origin is using
+               // Cartesian. :-)
+               Vector delta(oldPoint, point);
+               delta /= Global::zoom;
+               delta.y = -delta.y;
+               Global::origin -= delta;
+
+//             UpdateGridBackground();
+               update();
+               oldPoint = point;
+               return;
+       }
+
+       // If we're doing a selection rect, see if any objects are engulfed by it
+       // (implies left mouse button held down)
+       if (Global::selectionInProgress)
+       {
+               CheckObjectBounds();
+
+               // 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;
+               }
+
+               update();
+               return;
+       }
+
+       // Do object hit testing...
+       bool needUpdate = HitTestObjects(point);
+       VPVector hover2 = GetHovered(true); // Exclude dimension objects and circle centers (probably need to add arc centers too) from hover (also dragged objects...)
+#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 (hover2.size() > 1)
+       {
+//need to check for case where hover is over 2 circles and a 3rd's center (no longer a problem, I think)...
+               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(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);
+
+                       hoveringIntersection = true;
+                       intersectionPoint = v1;
+               }
+
+               numIntersecting = Global::numIntersectPoints;
+
+               if (numIntersecting > 0)
+               {
+                       Vector v1 = Global::intersectPoint[0];
+
+                       if (numIntersecting == 2)
+                       {
+                               Vector v2 = Global::intersectPoint[1];
+
+                               if (Vector::Magnitude(v2, point) < Vector::Magnitude(v1, point))
+                                       v1 = v2;
+                       }
+
+                       QString text = tr("Intersection <%1, %2>");
+                       informativeText = text.arg(v1.x).arg(v1.y);
+                       hoveringIntersection = true;
+                       intersectionPoint = v1;
+               }
+       }
+       else if (hover2.size() == 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)...
+*/
+                       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;
+                       }
+               }
+               else if (obj->type == OTCircle)
+               {
+                       if ((draggingObject && (dragged->type == OTLine)) && (dragged->hitPoint[0] || dragged->hitPoint[1]))
+                       {
+                               Point p = (dragged->hitPoint[0] ? dragged->p[1] : dragged->p[0]);
+                               Geometry::FindTangents(obj, p);
+
+                               if (Global::numIntersectPoints > 0)
+                               {
+                                       hoveringIntersection = true;
+                                       intersectionPoint = Geometry::NearestTo(point, Global::intersectPoint[0], Global::intersectPoint[1]);
+                               }
+                       }
+                       else if ((Global::tool == TTLine) && (Global::toolState == TSPoint2))
+                       {
+                               Geometry::FindTangents(obj, toolPoint[0]);
+
+                               if (Global::numIntersectPoints > 0)
+                               {
+                                       hoveringIntersection = true;
+                                       intersectionPoint = Geometry::NearestTo(point, Global::intersectPoint[0], Global::intersectPoint[1]);
+                               }
+                       }
+               }
+       }
+
+       // Handle object movement (left button down & over an object)
+       if ((event->buttons() & Qt::LeftButton) && draggingObject && !Global::tool)
+       {
+               if (hoveringIntersection)
+                       point = intersectionPoint;
+               else if (hoverPointValid)
+                       point = hoverPoint;
+               else if (Global::snapToGrid)
+                       point = SnapPointToGrid(point);
+
+               HandleObjectMovement(point);
+               update();
+               oldPoint = point;
+               return;
+       }
+
+       // Do tool handling, if any are active...
+       if (Global::tool)
+       {
+               if (hoveringIntersection)
+                       point = intersectionPoint;
+               else if (hoverPointValid)
+                       point = hoverPoint;
+               else if (Global::snapToGrid)
+               {
+                       if (angleSnap)
+                               point = SnapPointToAngle(point);
+                       else
+                               point = SnapPointToGrid(point);
+               }
+
+               ToolHandler(ToolMouseMove, point);
+       }
+
+       // This is used to draw the tool crosshair...
+       oldPoint = point;
+
+       if (needUpdate || Global::tool)
+               update();
+}
+
+void DrawingView::mouseReleaseEvent(QMouseEvent * event)
+{
+       if (event->button() == Qt::LeftButton)
+       {
+//We need to update especially if nothing collided and the state needs to change. !!! FIX !!!
+//could set it up to use the document's update function (assumes that all object
+//updates are being reported correctly:
+//             if (document.NeedsUpdate())
+               // Do an update if collided with at least *one* object in the document
+//             if (collided)
+                       update();
+
+               if (Global::tool)
+               {
+                       Vector point = Painter::QtToCartesianCoords(Vector(event->x(), event->y()));
+                       ToolHandler(ToolMouseUp, point);
+                       return;
+               }
+
+               Global::selectionInProgress = false;
+               informativeText.clear();
+
+// Should we be doing this automagically? Hmm...
+               // Clear our vectors
+//             select.clear();
+////           hover.clear();
+
+               // 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 [where to???])
+               select = GetSelection();
+
+               draggingObject = false;
+       }
+       else if (event->button() == Qt::MiddleButton)
+       {
+               scrollDrag = false;
+
+               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.20;
+       scrollWheelSeen = true;
+
+       if (event->angleDelta().y() < 0)
+       {
+               if (Global::zoom > 400.0)
+                       return;
+
+               Global::zoom *= zoomFactor;
+       }
+       else
+       {
+               if (Global::zoom < 0.125)
+                       return;
+
+               Global::zoom /= zoomFactor;
+       }
+
+       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)
+                       ToolHandler(ToolKeyDown, Point(0, 0));
+
+               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))
+       {
+               if (Global::tool)
+                       ToolHandler(ToolKeyUp, Point(0, 0));
+
+               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
+// more than half-way to the next grid point to snap there while conversely we
+// want a point that's less than half-way to to the next grid point then snap
+// to the one before it. So we add half of the grid spacing to the point, then
+// divide by it so that we can remove the fractional part, then multiply it
+// back to get back to the correct answer.
+//
+Point DrawingView::SnapPointToGrid(Point point)
+{
+       point += Global::gridSpacing / 2.0;             // *This* adds to Z!!!
+       point /= Global::gridSpacing;
+       point.x = floor(point.x);//need to fix this for negative numbers...
+       point.y = floor(point.y);
+       point.z = 0;                                    // Make *sure* Z doesn't go anywhere!!!
+       point *= Global::gridSpacing;
+       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)
+{
+       // Default to empty rect, if object checks below fail for some reason
+       Rect rect;
+
+       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;
+
+               double start = a->angle[0];
+               double end = start + a->angle[1];
+               rect = Rect(Point(cos(start), sin(start)), Point(cos(end), sin(end)));
+
+               // If the end of the arc is before the beginning, add 360 degrees to it
+               if (end < start)
+                       end += TAU;
+
+               // Adjust the bounds depending on which axes are crossed
+               if ((start < QTR_TAU) && (end > QTR_TAU))
+                       rect.t = 1.0;
+
+               if ((start < HALF_TAU) && (end > HALF_TAU))
+                       rect.l = -1.0;
+
+               if ((start < THREE_QTR_TAU) && (end > THREE_QTR_TAU))
+                       rect.b = -1.0;
+
+               if ((start < TAU) && (end > TAU))
+                       rect.r = 1.0;
+
+               if ((start < (TAU + QTR_TAU)) && (end > (TAU + QTR_TAU)))
+                       rect.t = 1.0;
+
+               if ((start < (TAU + HALF_TAU)) && (end > (TAU + HALF_TAU)))
+                       rect.l = -1.0;
+
+               if ((start < (TAU + THREE_QTR_TAU)) && (end > (TAU + THREE_QTR_TAU)))
+                       rect.b = -1.0;
+
+               rect *= a->radius[0];
+               rect.Translate(a->p[0]);
+               break;
+       }
+
+       case 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;
+               VPVectorIter i = c->objects.begin();
+               rect = GetObjectExtents((Object *)*i);
+               i++;
+
+               for(; i!=c->objects.end(); i++)
+                       rect |= GetObjectExtents((Object *)*i);
+       }
+
+       default:
+               break;
+       }
+
+       return rect;
+}
+
+void DrawingView::CheckObjectBounds(void)
+{
+       VPVectorIter i;
+
+       for(i=document.objects.begin(); i!=document.objects.end(); i++)
+       {
+               Object * obj = (Object *)(*i);
+               obj->selected = false;
+
+               switch (obj->type)
+               {
+               case OTLine:
+               case OTDimension: // N.B.: We don't check this properly...
+               {
+                       Line * l = (Line *)obj;
+
+                       if (Global::selection.contains(l->p[0].x, l->p[0].y) && Global::selection.contains(l->p[1].x, l->p[1].y))
+                               l->selected = true;
+
+                       break;
+               }
+
+               case OTCircle:
+               {
+                       Circle * c = (Circle *)obj;
+
+                       if (Global::selection.contains(c->p[0].x - c->radius[0], c->p[0].y - c->radius[0]) && Global::selection.contains(c->p[0].x + c->radius[0], c->p[0].y + c->radius[0]))
+                               c->selected = true;
+
+                       break;
+               }
+
+               case OTArc:
+               {
+                       Arc * a = (Arc *)obj;
+
+                       double start = a->angle[0];
+                       double end = start + a->angle[1];
+                       QPointF p1(cos(start), sin(start));
+                       QPointF p2(cos(end), sin(end));
+                       QRectF bounds(p1, p2);
+
+#if 1
+                       // Swap X/Y coordinates if they're backwards...
+                       if (bounds.left() > bounds.right())
+                       {
+                               double temp = bounds.left();
+                               bounds.setLeft(bounds.right());
+                               bounds.setRight(temp);
+                       }
+
+                       if (bounds.bottom() > bounds.top())
+                       {
+                               double temp = bounds.bottom();
+                               bounds.setBottom(bounds.top());
+                               bounds.setTop(temp);
+                       }
+#else
+                       // Doesn't work as advertised! For shame!
+                       bounds = bounds.normalized();
+#endif
+
+                       // If the end of the arc is before the beginning, add 360 degrees
+                       // to it
+                       if (end < start)
+                               end += TAU;
+
+                       // Adjust the bounds depending on which axes are crossed
+                       if ((start < QTR_TAU) && (end > QTR_TAU))
+                               bounds.setTop(1.0);
+
+                       if ((start < HALF_TAU) && (end > HALF_TAU))
+                               bounds.setLeft(-1.0);
+
+                       if ((start < THREE_QTR_TAU) && (end > THREE_QTR_TAU))
+                               bounds.setBottom(-1.0);
+
+                       if ((start < TAU) && (end > TAU))
+                               bounds.setRight(1.0);
+
+                       if ((start < (TAU + QTR_TAU)) && (end > (TAU + QTR_TAU)))
+                               bounds.setTop(1.0);
+
+                       if ((start < (TAU + HALF_TAU)) && (end > (TAU + HALF_TAU)))
+                               bounds.setLeft(-1.0);
+
+                       if ((start < (TAU + THREE_QTR_TAU)) && (end > (TAU + THREE_QTR_TAU)))
+                               bounds.setBottom(-1.0);
+
+                       bounds.setTopLeft(QPointF(bounds.left() * a->radius[0], bounds.top() * a->radius[0]));
+                       bounds.setBottomRight(QPointF(bounds.right() * a->radius[0], bounds.bottom() * a->radius[0]));
+                       bounds.translate(a->p[0].x, a->p[0].y);
+
+                       if (Global::selection.contains(bounds))
+                               a->selected = true;
+
+                       break;
+               }
+
+               case OTText:
+               {
+                       Text * t = (Text *)obj;
+                       Rect r(obj->p[0], Point(t->p[0].x + t->extents.Width(), t->p[0].y - t->extents.Height()));
+
+                       if (Global::selection.contains(r.l, r.t) && Global::selection.contains(r.r, r.b))
+                               t->selected = true;
+
+                       break;
+               }
+
+               case OTContainer:
+               {
+                       Container * c = (Container *)obj;
+
+                       if (Global::selection.contains(c->p[0].x, c->p[0].y) && Global::selection.contains(c->p[1].x, c->p[1].y))
+                               c->selected = true;
+
+                       break;
+               }
+
+//             default:
+//                     break;
+               }
+       }
+}
+
+bool DrawingView::HitTestObjects(Point point)
+{
+       VPVectorIter i;
+       numHovered = 0;
+       bool needUpdate = false;
+       hoverPointValid = false;
+
+       for(i=document.objects.begin(); i!=document.objects.end(); i++)
+       {
+               Object * obj = (Object *)(*i);
+
+               // If we're seeing the object we're dragging, skip it
+               if (draggingObject && (obj == dragged))
+                       continue;
+
+               if (HitTest(obj, point) == true)
+                       needUpdate = true;
+
+               if (obj->hovered)
+               {
+                       numHovered++;
+//printf("MouseMove: OBJECT HOVERED (numHovered = %i)\n", numHovered);
+                       emit ObjectHovered(obj);
+               }
+       }
+
+       return needUpdate;
+}
+
+bool DrawingView::HitTest(Object * obj, Point point)
+{
+       bool needUpdate = false;
+
+       switch (obj->type)
+       {
+       case OTLine:
+       {
+               bool oldHP0 = obj->hitPoint[0], oldHP1 = obj->hitPoint[1], oldHO = obj->hitObject;
+               obj->hitPoint[0] = obj->hitPoint[1] = obj->hitObject = false;
+               Vector lineSegment = obj->p[1] - obj->p[0];
+               Vector v1 = point - obj->p[0];
+               Vector v2 = point - obj->p[1];
+               double t = Geometry::ParameterOfLineAndPoint(obj->p[0], obj->p[1], point);
+               double distance;
+
+               if (t < 0.0)
+                       distance = v1.Magnitude();
+               else if (t > 1.0)
+                       distance = v2.Magnitude();
+               else
+                       // distance = ?Det?(ls, v1) / |ls|
+                       distance = fabs((lineSegment.x * v1.y - v1.x * lineSegment.y)
+                               / lineSegment.Magnitude());
+
+               if ((v1.Magnitude() * Global::zoom) < 8.0)
+               {
+                       obj->hitPoint[0] = true;
+                       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;
+
+               obj->hovered = (obj->hitPoint[0] || obj->hitPoint[1] || obj->hitObject ? true : false);
+
+               if ((oldHP0 != obj->hitPoint[0]) || (oldHP1 != obj->hitPoint[1]) || (oldHO != obj->hitObject))
+                       needUpdate = true;
+
+               break;
+       }
+
+       case OTCircle:
+       {
+               bool oldHP = obj->hitPoint[0], oldHO = obj->hitObject;
+               obj->hitPoint[0] = obj->hitObject = false;
+               double length = Vector::Magnitude(obj->p[0], point);
+
+               if ((length * Global::zoom) < 8.0)
+               {
+                       obj->hitPoint[0] = true;
+                       hoverPoint = obj->p[0];
+                       hoverPointValid = true;
+               }
+               else if ((fabs(length - obj->radius[0]) * Global::zoom) < 2.0)
+                       obj->hitObject = true;
+
+               obj->hovered = (obj->hitPoint[0] || obj->hitObject ? true : false);
+
+               if ((oldHP != obj->hitPoint[0]) || (oldHO != obj->hitObject))
+                       needUpdate = true;
+
+               break;
+       }
+
+       case OTArc:
+       {
+               bool oldHP0 = obj->hitPoint[0], oldHP1 = obj->hitPoint[1], oldHP2 = obj->hitPoint[2], oldHO = obj->hitObject;
+               obj->hitPoint[0] = obj->hitPoint[1] = obj->hitPoint[2] = obj->hitObject = false;
+               double length = Vector::Magnitude(obj->p[0], point);
+               double angle = Vector::Angle(obj->p[0], point);
+
+               // Make sure we get the angle in the correct spot
+               if (angle < obj->angle[0])
+                       angle += TAU;
+
+               // Get the span that we're pointing at...
+               double span = angle - obj->angle[0];
+
+               // N.B.: Still need to hit test the arc start & arc span handles...
+               double spanAngle = obj->angle[0] + obj->angle[1];
+               Point handle1 = obj->p[0] + (Vector(cos(obj->angle[0]), sin(obj->angle[0])) * obj->radius[0]);
+               Point handle2 = obj->p[0] + (Vector(cos(spanAngle), sin(spanAngle)) * obj->radius[0]);
+               double length2 = Vector::Magnitude(point, handle1);
+               double length3 = Vector::Magnitude(point, handle2);
+
+               if ((length * Global::zoom) < 8.0)
+               {
+                       obj->hitPoint[0] = true;
+                       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;
+
+               obj->hovered = (obj->hitPoint[0] || obj->hitPoint[1] || obj->hitPoint[2] || obj->hitObject ? true : false);
+
+               if ((oldHP0 != obj->hitPoint[0]) || (oldHP1 != obj->hitPoint[1]) || (oldHP2 != obj->hitPoint[2]) || (oldHO != obj->hitObject))
+                       needUpdate = true;
+
+               break;
+       }
+
+       case 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...  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;
+               c->clicked = NULL;
+
+               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);
+
+                       // Skip the flattened containers (if any)...
+                       if (cObj->type == OTContainer)
+                               continue;
+
+                       // 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;//*/
+               }
+
+               break;
+       }
+
+       default:
+               break;
+       }
+
+       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 = 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"));
+
+       switch (obj->type)
+       {
+       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;
+                       obj->p[1] += delta;
+               }
+
+               break;
+
+       case OTCircle:
+               if (obj->hitPoint[0])
+                       obj->p[0] = point;
+               else if (obj->hitObject)
+               {
+                       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);
+               }
+
+               break;
+
+       case OTArc:
+               if (obj->hitPoint[0])
+                       obj->p[0] = point;
+               else if (obj->hitPoint[1])
+               {
+                       // Change the Arc's span (handle #1)
+                       if (shiftDown)
+                       {
+                               double angle = Vector::Angle(obj->p[0], point);
+                               double delta = angle - obj->angle[0];
+
+                               if (delta < 0)
+                                       delta += TAU;
+
+                               obj->angle[1] -= delta;
+                               obj->angle[0] = angle;
+
+                               if (obj->angle[1] < 0)
+                                       obj->angle[1] += TAU;
+
+                               QString text = QObject::tr("Span: %1") + QChar(0x00B0) + QObject::tr("\n%2") + QChar(0x00B0) + QObject::tr(" - %3") + QChar(0x00B0);
+                               informativeText = text.arg(obj->angle[1] * RADIANS_TO_DEGREES, 0, 'd', 4).arg(obj->angle[0] * RADIANS_TO_DEGREES, 0, 'd', 2).arg((obj->angle[0] + obj->angle[1]) * RADIANS_TO_DEGREES, 0, 'd', 2);
+                               return;
+                       }
+
+                       double angle = Vector::Angle(obj->p[0], point);
+                       obj->angle[0] = angle;
+                       QString text = QObject::tr("Start angle: %1") + QChar(0x00B0);
+                       informativeText = text.arg(obj->angle[0] * RADIANS_TO_DEGREES, 0, 'd', 4);
+               }
+               else if (obj->hitPoint[2])
+               {
+                       // Change the Arc's span (handle #2)
+                       if (shiftDown)
+                       {
+                               double angle = Vector::Angle(obj->p[0], point);
+                               obj->angle[1] = angle - obj->angle[0];
+
+                               if (obj->angle[1] < 0)
+                                       obj->angle[1] += TAU;
+
+                               QString text = QObject::tr("Span: %1") + QChar(0x00B0) + QObject::tr("\n%2") + QChar(0x00B0) + QObject::tr(" - %3") + QChar(0x00B0);
+                               informativeText = text.arg(obj->angle[1] * RADIANS_TO_DEGREES, 0, 'd', 4).arg(obj->angle[0] * RADIANS_TO_DEGREES, 0, 'd', 2).arg((obj->angle[0] + obj->angle[1]) * RADIANS_TO_DEGREES, 0, 'd', 2);
+                               return;
+                       }
+
+                       double angle = Vector::Angle(obj->p[0], point);
+                       obj->angle[0] = angle - obj->angle[1];
+
+                       if (obj->angle[0] < 0)
+                               obj->angle[0] += TAU;
+
+                       QString text = QObject::tr("End angle: %1") + QChar(0x00B0);
+                       informativeText = text.arg((obj->angle[0] + obj->angle[1]) * RADIANS_TO_DEGREES, 0, 'd', 4);
+               }
+               else if (obj->hitObject)
+               {
+                       if (shiftDown)
+                       {
+                               return;
+                       }
+
+                       obj->radius[0] = Vector::Magnitude(obj->p[0], point);
+                       QString text = QObject::tr("Radius: %1");
+                       informativeText = text.arg(obj->radius[0], 0, 'd', 4);
+               }
+
+               break;
+
+       case 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 :-)
+/*
+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:
+               break;
+       }
+}
+
+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;
+       }
+}