]> Shamusworld >> Repos - architektonas/blobdiff - src/drawingview.cpp
Added object pane, grouping, load/save functionality.
[architektonas] / src / drawingview.cpp
index e904bdb7ef2a5240fa152392d33f121891a9ce91..1fd0482a83d266c0e313081f878ded884655b649 100644 (file)
@@ -4,18 +4,23 @@
 // (C) 2011 Underground Software
 // See the README and GPLv3 files for licensing and warranty information
 //
-// JLH = James L. Hammons <jlhamm@acm.org>
+// 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
 //
 
 // FIXED:
 //
+// - 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]
 //
 // STILL TO BE DONE:
 //
+// - Lots of stuff
+// - Layer locking (hiding works)
 //
 
 // Uncomment this for debugging...
 #include "drawingview.h"
 
 #include <stdint.h>
+#include "geometry.h"
+#include "global.h"
 #include "mathconstants.h"
+#include "painter.h"
+#include "structs.h"
+#include "utils.h"
 
-#include "arc.h"
-#include "circle.h"
-#include "dimension.h"
-#include "line.h"
+
+#define BACKGROUND_MAX_SIZE    512
 
 
 DrawingView::DrawingView(QWidget * parent/*= NULL*/): QWidget(parent),
        // The value in the settings file will override this.
-       useAntialiasing(true),
-//     scale(1.0), offsetX(-10), offsetY(-10), tool(TOOLSelect),
-//     ptHighlight(-1), oldPtHighlight(-1), ptNextHighlight(-1), oldPtNextHighlight(-1),
-//     polyFirstPoint(true)
-       scale(1.0), offsetX(-10), offsetY(-10),
-       document(Vector(0, 0)),
-       gridSpacing(32.0), collided(false), rotateTool(false), rx(150.0), ry(150.0)
+       useAntialiasing(true), /*numSelected(0),*/ numHovered(0), shiftDown(false),
+       ctrlDown(false),
+       gridBackground(BACKGROUND_MAX_SIZE, BACKGROUND_MAX_SIZE),
+       scale(1.0), offsetX(-10), offsetY(-10), document(true),
+       gridPixels(0), collided(false), hoveringIntersection(false)
 {
+//wtf? doesn't work except in c++11??? document = { 0 };
        setBackgroundRole(QPalette::Base);
        setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
 
-//     toolPalette = new ToolWindow();
-//     CreateCursors();
-//     setCursor(cur[TOOLSelect]);
-//     setMouseTracking(true);
+       Global::gridSpacing = 12.0;             // In base units (inch is default)
 
+#if 0
        Line * line = new Line(Vector(5, 5), Vector(50, 40), &document);
        document.Add(line);
        document.Add(new Line(Vector(50, 40), Vector(10, 83), &document));
@@ -61,647 +66,1973 @@ DrawingView::DrawingView(QWidget * parent/*= NULL*/): QWidget(parent),
        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), &document);
+       Dimension * dimension = new Dimension(Vector(0, 0), Vector(0, 0), DTLinear, &document);
        line->SetDimensionOnLine(dimension);
-//     line->SetDimensionOnPoint2(dimension);
        document.Add(dimension);
 #else
        // Alternate way to do the above...
        line->SetDimensionOnLine();
 #endif
-}
+#else
+       Line * line = new Line;//(Vector(5, 5), Vector(50, 40), &document);
+       line->p[0] = Vector(5, 5);
+       line->p[1] = Vector(50, 40);
+       line->type = OTLine;
+       line->thickness = 2.0;
+       line->style = LSDash;
+       line->color = 0xFF7F00;
+       line->layer = 0;
+       document.objects.push_back(line);
+       document.objects.push_back(new Line(Vector(50, 40), Vector(10, 83)));
+       document.objects.push_back(new Line(Vector(10, 83), Vector(17, 2)));
+       document.objects.push_back(new Circle(Vector(100, 100), 36));
+       document.objects.push_back(new Circle(Vector(50, 150), 49));
+       document.objects.push_back(new Arc(Vector(300, 300), 32, TAU / 8.0, TAU * 0.65)),
+       document.objects.push_back(new Arc(Vector(200, 200), 60, TAU / 4.0, TAU * 0.75));
+       document.objects.push_back(new Dimension(Vector(50, 40), Vector(5, 5)));
+       document.objects.push_back(new Text(Vector(10, 83), "Here is some awesome text!"));
+#endif
 
-void DrawingView::SetRotateToolActive(bool state/*= true*/)
-{
-       rotateTool = state;
-       update();
-}
+/*
+Here we set the grid size in pixels--12 in this case. Initially, we have our
+zoom set to make this represent 12 inches at a zoom factor of 25%. (This is
+arbitrary.) So, to be able to decouple the grid size from the zoom, we need
+to be able to set the size of the background grid (which we do here at an
+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 / Global::zoom;
+
+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
+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
+objects to take its place--like some kind of scale factor. This would seem to
+imply that certain point sizes actually *do* tie things like fonts to absolute
+sizes on the screen, but not necessarily because you could have an inch scale
+with text that is quite small relative to other objects on the screen, which
+currently you have to zoom in to see (and which blows up the text). Point sizes
+in an application like this are a bit meaningless; even though an inch is an
+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. 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"), 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
+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! :-)
 
-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.
-       return QPoint(offsetX + event->x(), offsetY + (size().height() - event->y()));
+*/
+       SetGridSize(12);        // This is in pixels
 }
 
-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
-       return QPoint(-offsetX + x, (size().height() - (-offsetY + y)) * +1.0);
-}
 
-void DrawingView::paintEvent(QPaintEvent * /*event*/)
+void DrawingView::SetGridSize(uint32_t size)
 {
-       QPainter painter(this);
-
-       if (useAntialiasing)
-               painter.setRenderHint(QPainter::Antialiasing);
-
-#if 0
-       painter.translate(QPoint(-offsetX, size.height() - (-offsetY)));
-       painter.scale(1.0, -1.0);
-#else
-       QTransform transform;
-//order of operations is important! N.B.: Can't use scaling other than 1.0, it
-//causes lines to look strange (i.e., it scales the pen strokes too)
-//     transform.translate(-offsetX, size().height() - (-offsetY));
-       transform.scale(1.0, -1.0);
-       transform.translate(-offsetX, -size().height() - offsetY);
-//     transform.scale(0.25, 0.25);
-       painter.setTransform(transform);
-#endif
-       Object::SetViewportHeight(size().height());
-
-       // Draw coordinate axes
+       // Sanity check
+       if (size == gridPixels)
+               return;
 
-       painter.setPen(QPen(Qt::blue, 1.0, Qt::DotLine));
-       painter.drawLine(0, -16384, 0, 16384);
-       painter.drawLine(-16384, 0, 16384, 0);
+       // 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));
 
-       // Draw supplemental (tool related) points
-
-       if (rotateTool)
+       for(int i=0; i<(BACKGROUND_MAX_SIZE-1); i+=gridPixels)
        {
-               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);
+               pmp.drawLine(i, 0, i, BACKGROUND_MAX_SIZE - 1);
+               pmp.drawLine(0, i, BACKGROUND_MAX_SIZE - 1, i);
        }
 
-// Maybe we can make the grid into a background brush instead, and let Qt deal
-// with it???
-       // Draw grid
-
-#if 0
-       painter.setPen(QPen(QColor(90, 90, 90), 1.0, Qt::DotLine));
-
-       //these two loops kill performance!
-       for(double x=0; x<size().width(); x+=gridSpacing*10.0)
-               p.drawLine((int)x, -16384, (int)x, 16384);
+       pmp.end();
 
-       for(double y=0; y<size().height(); y+=gridSpacing*10.0)
-               p.drawLine(-16384, (int)y, 16384, (int)y);
-#endif
-
-       painter.setPen(QPen(Qt::black, 1.0, Qt::SolidLine));
+       // Set up new BG brush & zoom level (pixels per base unit)
+       Global::zoom = gridPixels / Global::gridSpacing;
+       UpdateGridBackground();
+}
 
-       for(double x=0; x<size().width(); x+=gridSpacing)
-               for(double y=0; y<size().height(); y+=gridSpacing)
-                       painter.drawPoint((int)x, (int)y);
 
-       // The top level document takes care of rendering for us...
-       document.Draw(&painter);
+void DrawingView::UpdateGridBackground(void)
+{
+       // 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;
+
+       if (y < 0)
+               y = -y % gridPixels;
+       else
+               y = (gridPixels - (y % gridPixels)) % gridPixels;
+
+       // Here we grab a section of the bigger pixmap, so that the background
+       // *looks* like it's scrolling...
+       QPixmap pm = gridBackground.copy(x, y, gridPixels, gridPixels);
+       QPalette pal = palette();
+       pal.setBrush(backgroundRole(), QBrush(pm));
+       setAutoFillBackground(true);
+       setPalette(pal);
 }
 
-void DrawingView::mousePressEvent(QMouseEvent * event)
+
+//
+// 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 (event->button() == Qt::LeftButton)
-       {
-               QPoint pt = GetAdjustedMousePosition(event);
-               Vector point(pt.x(), pt.y());
+//printf("DrawingView::DeleteCurrentLayer(): currentLayer = %i\n", layer);
+       std::vector<void *>::iterator i = document.objects.begin();
 
-               collided = document.Collided(point);
+       while (i != document.objects.end())
+       {
+               Object * obj = (Object *)(*i);
 
-               if (collided)
-                       update();       // Do an update if collided with at least *one* object in the document
+               if (obj->layer < layer)
+                       i++;
+               else if (obj->layer == layer)
+               {
+                       document.objects.erase(i);
+                       delete obj;
+               }
+               else
+               {
+                       obj->layer--;
+                       i++;
+               }
        }
-}
 
-void DrawingView::mouseMoveEvent(QMouseEvent * event)
-{
-       QPoint pt = GetAdjustedMousePosition(event);
-       Vector point(pt.x(), pt.y());
+       // We've just done a destructive action, so update the screen!
+       update();
+}
 
-       // Grid processing...
-#if 1
-       // 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.
-       if (event->buttons() & Qt::LeftButton)
-       {
-               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;
-       }
-#endif
-//we should keep track of the last point here and only pass this down *if* the point
-//changed...
-       document.PointerMoved(point);
 
-       if (document.NeedsUpdate())
-               update();
+void DrawingView::HandleLayerToggle(void)
+{
+       // A layer's visibility was toggled, so update the screen...
+       update();
 }
 
-void DrawingView::mouseReleaseEvent(QMouseEvent * event)
+
+//
+// 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)
 {
-       if (event->button() == Qt::LeftButton)
+//printf("DrawingView: Swapping layers %i and %i.\n", layer1, layer2);
+       std::vector<void *>::iterator i;
+
+       for(i=document.objects.begin(); i!=document.objects.end(); i++)
        {
-               document.PointerReleased();
+               Object * obj = (Object *)(*i);
 
-//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
+               if (obj->layer == layer1)
+                       obj->layer = layer2;
+               else if (obj->layer == layer2)
+                       obj->layer = layer1;
        }
 }
 
 
-#if 0
-QSize DrawingView::minimumSizeHint() const
+QPoint DrawingView::GetAdjustedMousePosition(QMouseEvent * event)
 {
-       return QSize(50, 50);
+       // 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()));
 }
 
-QSize DrawingView::sizeHint() const
-{
-       return QSize(400, 400);
-}
 
-void DrawingView::CreateCursors(void)
+QPoint DrawingView::GetAdjustedClientPosition(int x, int y)
 {
-       int hotx[8] = {  1,  1, 11, 15,  1,  1,  1,  1 };
-       int hoty[8] = {  1,  1, 11, 13,  1,  1,  1,  1 };
-
-       for(int i=0; i<8; i++)
-       {
-               QString s;
-               s.sprintf(":/res/cursor%u.png", i+1);
-               QPixmap pmTmp(s);
-               cur[i] = QCursor(pmTmp, hotx[i], hoty[i]);
-       }
+       // 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 [why? we use -offsetX after all]
+       return QPoint(-offsetX + x, (size().height() - (-offsetY + y)) * +1.0);
 }
 
-/*
-TODO:
- o  Different colors for polys on selected points
- o  Different colors for handles on non-selected polys
- o  Line of sight (dashed, dotted) for off-curve points
- o  Repaints for press/release of CTRL/SHIFT during point creation
-*/
+
 void DrawingView::paintEvent(QPaintEvent * /*event*/)
 {
-       QPainter p(this);
-//hm, causes lockup
-//     p.setRenderHint(QPainter::Antialiasing);
-//Doesn't do crap!
-//dc.SetBackground(*wxWHITE_BRUSH);
+       QPainter qtPainter(this);
+       Painter painter(&qtPainter);
+
+       if (useAntialiasing)
+               qtPainter.setRenderHint(QPainter::Antialiasing);
+
+       Global::viewportHeight = size().height();
 
-// Due to the screwiness of wxWidgets coord system, the origin is ALWAYS
-// the upper left corner--regardless of axis orientation, etc...
-//     int width, height;
-//     dc.GetSize(&width, &height);
-       QSize winSize = size();
+       // Draw coordinate axes
+       painter.SetPen(QPen(Qt::blue, 1.0, Qt::DotLine));
+       painter.DrawLine(0, -16384, 0, 16384);
+       painter.DrawLine(-16384, 0, 16384, 0);
+
+       // Do object rendering...
+       for(int i=0; i<Global::numLayers; i++)
+       {
+               if (Global::layerHidden[i] == false)
+                       RenderObjects(&painter, document.objects, i);
+       }
 
-//     dc.SetDeviceOrigin(-offsetX, height - (-offsetY));
-//     dc.SetAxisOrientation(true, true);
-       p.translate(QPoint(-offsetX, winSize.height() - (-offsetY)));
-       p.scale(1.0, -1.0);
+       // Do tool rendering, if any...
+       if (Global::tool)
+       {
+               painter.SetPen(QPen(QColor(200, 100, 0, 255), 1.0, Qt::DashLine));
+               painter.DrawCrosshair(oldPoint);
+               ToolDraw(&painter);
+       }
 
-// Scrolling can be done by using OffsetViewportOrgEx
-// Scaling can be done by adjusting SetWindowExtEx (it's denominator of txform)
-// you'd use: % = ViewportExt / WindowExt
-// But it makes the window look like crap: fuggetuboutit.
-// Instead, we have to scale EVERYTHING by hand. Crap!
-// It's not *that* bad, but not as convenient either...
+       // Do selection rectangle rendering, if any
+       if (Global::selectionInProgress)
+       {
+               painter.SetPen(QPen(QColor(255, 127, 0, 255)));
+               painter.SetBrush(QBrush(QColor(255, 127, 0, 100)));
+               painter.DrawRect(Global::selection);
+       }
 
-//     dc.SetPen(*(wxThePenList->FindOrCreatePen(wxColour(0x00, 0x00, 0xFF), 1, wxDOT)));
-////   dc.DrawLine(0, 0, 10, 10);
-       p.setPen(QPen(Qt::blue, 1.0, Qt::DotLine));
+       if (hoveringIntersection)
+               painter.DrawHandle(intersectionPoint);
 
-    // Draw coordinate axes
+       if (!informativeText.isEmpty())
+               painter.DrawInformativeText(informativeText);
+}
 
-//     dc.CrossHair(0, 0);
-       p.drawLine(0, -16384, 0, 16384);
-       p.drawLine(-16384, 0, 16384, 0);
 
-    // Draw points
+//
+// Renders objects in the passed in vector
+//
+void DrawingView::RenderObjects(Painter * painter, std::vector<void *> & v, int layer, bool ignoreLayer/*= false*/)
+{
+       std::vector<void *>::iterator i;
 
-       for(int i=0; i<pts.GetNumPoints(); i++)
+       for(i=v.begin(); i!=v.end(); i++)
        {
-               if (i == ptHighlight)
-               {
-//                     dc.SetPen(*(wxThePenList->FindOrCreatePen(wxColour(0xFF, 0x00, 0x00), 1, wxSOLID)));
-////                   SelectObject(hdc, hRedPen1);
-                       p.setPen(QPen(Qt::red, 1.0, Qt::SolidLine));
+               Object * obj = (Object *)(*i);
+               float scaledThickness = Global::scale * obj->thickness;
 
-                       if (pts.GetOnCurve(i))
-                       {
-                               DrawSquareDotN(p, pts.GetX(i), pts.GetY(i), 7);
-                               DrawSquareDotN(p, pts.GetX(i), pts.GetY(i), 9);
-                       }
-                       else
-                       {
-                               DrawRoundDotN(p, pts.GetX(i), pts.GetY(i), 7);
-                               DrawRoundDotN(p, pts.GetX(i), pts.GetY(i), 9);
-                       }
-               }
-               else if ((i == ptHighlight || i == ptNextHighlight) && tool == TOOLAddPt)
-               {
-//                     dc.SetPen(*(wxThePenList->FindOrCreatePen(wxColour(0x00, 0xAF, 0x00), 1, wxSOLID)));
-////                   SelectObject(hdc, hGreenPen1);
-                       p.setPen(QPen(Qt::green, 1.0, Qt::SolidLine));
+               // If the object isn't on the current layer being drawn, skip it
+               if (!ignoreLayer && (obj->layer != layer))
+                       continue;
 
-                       if (pts.GetOnCurve(i))
-                       {
-                               DrawSquareDotN(p, pts.GetX(i), pts.GetY(i), 7);
-                               DrawSquareDotN(p, pts.GetX(i), pts.GetY(i), 9);
-                       }
-                       else
-                       {
-                               DrawRoundDotN(p, pts.GetX(i), pts.GetY(i), 7);
-                               DrawRoundDotN(p, pts.GetX(i), pts.GetY(i), 9);
-                       }
+               if ((Global::tool == TTRotate) && ctrlDown && obj->selected)
+               {
+                       painter->SetPen(0x00FF00, 2.0, LSSolid);
                }
                else
                {
-//                     dc.SetPen(*(wxThePenList->FindOrCreatePen(wxColour(0x00, 0x00, 0x00), 1, wxSOLID)));
-////                   SelectObject(hdc, hBlackPen1);
-                       p.setPen(QPen(Qt::black, 1.0, Qt::SolidLine));
+                       painter->SetPen(obj->color, Global::zoom * scaledThickness, obj->style);
+                       painter->SetBrush(obj->color);
 
-                       if (pts.GetOnCurve(i))
-                               DrawSquareDot(p, pts.GetX(i), pts.GetY(i));
-                       else
-                               DrawRoundDot(p, pts.GetX(i), pts.GetY(i));
+                       if (obj->selected || obj->hitObject)
+                               painter->SetPen(0xFF0000, Global::zoom * scaledThickness, LSDash);
                }
 
-               if (tool == TOOLDelPt && i == ptHighlight)
+               switch (obj->type)
                {
-#if 0
-                       dc.SetPen(*(wxThePenList->FindOrCreatePen(wxColour(0xFF, 0x00, 0x00), 1, wxSOLID)));
-//                     SelectObject(hdc, hRedPen1);
-//                     MoveToEx(hdc, pts.GetX(i) - 5, pts.GetY(i) - 5, NULL);
-//                     LineTo(hdc, pts.GetX(i) + 5, pts.GetY(i) + 5);
-//                     LineTo(hdc, pts.GetX(i) - 5, pts.GetY(i) - 5);//Lameness!
-//                     MoveToEx(hdc, pts.GetX(i) - 5, pts.GetY(i) + 5, NULL);
-//                     LineTo(hdc, pts.GetX(i) + 5, pts.GetY(i) - 5);
-//                     LineTo(hdc, pts.GetX(i) - 5, pts.GetY(i) + 5);//More lameness!!
-#endif
-                       p.setPen(QPen(Qt::red, 1.0, Qt::SolidLine));
-                       p.drawLine(pts.GetX(i) - 5, pts.GetY(i) - 5, pts.GetX(i) + 5, pts.GetY(i) + 5);
-                       p.drawLine(pts.GetX(i) + 5, pts.GetY(i) - 5, pts.GetX(i) - 5, pts.GetY(i) + 5);
-               }
-       }
+               case OTLine:
+                       painter->DrawLine(obj->p[0], obj->p[1]);
 
-////           SelectObject(hdc, hBlackPen1);
-//     dc.SetPen(*(wxThePenList->FindOrCreatePen(wxColour(0x00, 0x00, 0x00), 1, wxSOLID)));
-       p.setPen(QPen(Qt::black, 1.0, Qt::SolidLine));
+                       if (obj->hitPoint[0])
+                               painter->DrawHandle(obj->p[0]);
 
-       // Draw curve formed by points
+                       if (obj->hitPoint[1])
+                               painter->DrawHandle(obj->p[1]);
 
-       for(int poly=0; poly<pts.GetNumPolys(); poly++)
-       {
-               if (pts.GetNumPoints(poly) > 2)
-               {
-                       // Initial move...
-                       // If it's not on curve, then move to it, otherwise move to last point...
+                       break;
+               case OTCircle:
+                       painter->SetBrush(QBrush(Qt::NoBrush));
+                       painter->DrawEllipse(obj->p[0], obj->radius[0], obj->radius[0]);
 
-                       int x, y;
+                       if (obj->hitPoint[0])
+                               painter->DrawHandle(obj->p[0]);
 
-                       if (pts.GetOnCurve(poly, pts.GetNumPoints(poly) - 1))
-                               x = (int)pts.GetX(poly, pts.GetNumPoints(poly) - 1), y = (int)pts.GetY(poly, pts.GetNumPoints(poly) - 1);
-                       else
-                               x = (int)pts.GetX(poly, 0), y = (int)pts.GetY(poly, 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]));
+
+                       break;
+               case OTDimension:
+               {
+                       Dimension * d = (Dimension *)obj;
+
+                       Vector v(d->p[0], d->p[1]);
+                       double angle = v.Angle();
+                       Vector unit = v.Unit();
+                       Vector linePt1 = d->p[0], linePt2 = d->p[1];
+                       Vector ortho;
+                       double x1, y1, length;
 
-                       for(int i=0; i<pts.GetNumPoints(poly); i++)
+                       if (d->subtype == DTLinearVert)
                        {
-                               if (pts.GetOnCurve(poly, i))
-//                                     LineTo(hdc, pts.GetX(poly, i), pts.GetY(poly, i));
+                               if ((angle < 0) || (angle > HALF_TAU))
                                {
-                                       p.drawLine(x, y, pts.GetX(poly, i), pts.GetY(poly, i));
-                                       x = (int)pts.GetX(poly, i), y = (int)pts.GetY(poly, i);
+                                       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
                                {
-                                       uint32 prev = pts.GetPrev(poly, i), next = pts.GetNext(poly, i);
-                                       float px = pts.GetX(poly, prev), py = pts.GetY(poly, prev),
-                                               nx = pts.GetX(poly, next), ny = pts.GetY(poly, next);
-
-                                       if (!pts.GetOnCurve(poly, prev))
-                                               px = (px + pts.GetX(poly, i)) / 2.0f,
-                                               py = (py + pts.GetY(poly, i)) / 2.0f;
-
-                                       if (!pts.GetOnCurve(poly, next))
-                                               nx = (nx + pts.GetX(poly, i)) / 2.0f,
-                                               ny = (ny + pts.GetY(poly, i)) / 2.0f;
-
-                                       Bezier(p, point(px, py), point(pts.GetX(poly, i), pts.GetY(poly, i)), point(nx, ny));
-                                       x = (int)nx, y = (int)ny;
+                                       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;
+                               }
 
-                                       if (pts.GetOnCurve(poly, next))
-                                               i++;                                    // Following point is on curve, so move past it
+                               linePt1.x = linePt2.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;
                                }
+
+                               linePt1.y = linePt2.y = y1;
+                               length = fabs(d->p[0].x - d->p[1].x);
+                       }
+                       else if (d->subtype == DTLinear)
+                       {
+                               angle = Vector(linePt1, linePt2).Angle();
+                               ortho = Vector::Normal(linePt1, linePt2);
+                               length = v.Magnitude();
                        }
-               }
-       }
-}
 
-void DrawingView::mousePressEvent(QMouseEvent * event)
-{
-       if (event->button() == Qt::RightButton)
-       {
-               toolPalette->move(event->globalPos());
-               toolPalette->setVisible(true);
-               setCursor(cur[TOOLSelect]);
-               toolPalette->prevTool = TOOLSelect;
-       }
-       else if (event->button() == Qt::MidButton)
-       {
-               setCursor(cur[2]);                                                      // Scrolling cursor
-       }
-       else if (event->button() == Qt::LeftButton)
-       {
-               if (tool == TOOLScroll || tool == TOOLZoom)
-;//meh                 CaptureMouse();                                         // Make sure we capture the mouse when in scroll/zoom mode
-               else if (tool == TOOLAddPt)             // "Add Point" tool
-               {
-                       if (pts.GetNumPoints() > 0)
+                       unit = Vector(linePt1, linePt2).Unit();
+
+                       Point p1 = linePt1 + (ortho * 10.0 * scaledThickness);
+                       Point p2 = linePt2 + (ortho * 10.0 * scaledThickness);
+                       Point p3 = linePt1 + (ortho * 16.0 * scaledThickness);
+                       Point p4 = linePt2 + (ortho * 16.0 * scaledThickness);
+                       Point 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(linePt1, linePt2, linePt2 - (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)
                        {
-                               QPoint pt = GetAdjustedMousePosition(event);
-                               pts.InsertPoint(pts.GetNext(ptHighlight), pt.x(), pt.y(), ((event->modifiers() == Qt::ShiftModifier || event->modifiers() == Qt::ControlModifier) ? false : true));
-                               ptHighlight = ptNextHighlight;
-                               update();
+                               // Draw main dimension line + arrowheads
+                               painter->DrawLine(p1, p2);
+                               painter->DrawArrowhead(p1, p2, scaledThickness);
+                               painter->DrawArrowhead(p2, p1, scaledThickness);
                        }
-               }
-               else if (tool == TOOLAddPoly)   // "Add Poly" tool
-               {
-#ifdef DEBUGFOO
-WriteLogMsg("Adding point... # polys: %u, # points: %u", pts.GetNumPolys(), pts.GetNumPoints());
-#endif
-                       if (polyFirstPoint)
+                       else
                        {
-                               polyFirstPoint = false;
-                               pts.AddNewPolyAtEnd();
+                               // 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));
                        }
 
-                       QPoint pt = GetAdjustedMousePosition(event);
-//printf("GetAdjustedMousePosition = %i, %i\n", pt.x(), pt.y());
-                       // Append a point to the end of the structure
-                       pts += IPoint(pt.x(), pt.y(), ((event->modifiers() == Qt::ShiftModifier || event->modifiers() == Qt::ControlModifier) ? false : true));
-                       ptHighlight = pts.GetNumPoints() - 1;
-                       update();
-#ifdef DEBUGFOO
-WriteLogMsg(" --> [# polys: %u, # points: %u]\n", pts.GetNumPolys(), pts.GetNumPoints());
-#endif
-               }
-               else if (tool == TOOLSelect || tool == TOOLPolySelect)
-               {
-                       if (pts.GetNumPoints() > 0)
+                       // 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
                        {
-                               pt = GetAdjustedClientPosition(pts.GetX(ptHighlight), pts.GetY(ptHighlight));
-//printf("GetAdjustedClientPosition = %i, %i\n", pt.x(), pt.y());
-//                             WarpPointer(pt.x, pt.y);
-                               QCursor::setPos(mapToGlobal(pt));
+                               double feet = (double)((int)length / 12);
+                               double inches = length - (feet * 12.0);
 
-                               if (event->modifiers() == Qt::ShiftModifier || event->modifiers() == Qt::ControlModifier)
-                               {
-                                       pts.SetOnCurve(ptHighlight, !pts.GetOnCurve(ptHighlight));
-                                       update();
-                               }
+                               if (inches == 0)
+                                       dimText = QString("%1'").arg(feet);
+                               else
+                                       dimText = QString("%1' %2\"").arg(feet).arg(inches);
                        }
+
+                       painter->DrawAngledText(ctr, angle, dimText, scaledThickness);
+
+                       break;
                }
-               else if (tool == TOOLDelPt)
+               case OTText:
                {
-                       if (pts.GetNumPoints() > 0)
-//Or could use:
-//                     if (ptHighlight != -1)
+                       Text * t = (Text *)obj;
+                       painter->DrawTextObject(t->p[0], t->s.c_str(), scaledThickness);
+                       break;
+               }
+               case OTSpline:
+               {
+                       break;
+               }
+               case OTPolygon:
+               {
+                       break;
+               }
+               case OTContainer:
+               {
+                       // Containers require recursive rendering...
+                       Container * c = (Container *)obj;
+                       RenderObjects(painter, (*c).objects, layer);
+
+//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)
                        {
-//This assumes that WM_MOUSEMOVE happens before this!
-//The above commented out line should take care of this contingency... !!! FIX !!!
-                               pts.DeletePoint(ptHighlight);
-                               update();
+                               Rect r = GetObjectExtents(obj);
+                               painter->DrawRectCorners(r);
                        }
+
+                       break;
+               }
+               default:
+                       break;
                }
        }
-
-       event->accept();
 }
 
-void DrawingView::mouseMoveEvent(QMouseEvent * event)
-{
-       if (event->buttons() == Qt::RightButton)
-       {
-               ToolType newTool = toolPalette->FindSelectedTool();
 
-               if (newTool != toolPalette->prevTool)
-               {
-                       toolPalette->prevTool = newTool;
-                       toolPalette->repaint();
-               }
-       }
-       else if (event->buttons() == Qt::MidButton)
-       {
-               // Calc offset from previous point
-               pt = event->pos();
-               ptOffset = QPoint(pt.x() - ptPrevious.x(), pt.y() - ptPrevious.y());
+void DrawingView::AddHoveredToSelection(void)
+{
+       std::vector<void *>::iterator i;
 
-// Then multiply it by the scaling factor. Whee!
-               // This looks wacky because we're using screen coords for the offset...
-               // Otherwise, we would subtract both offsets!
-               offsetX -= ptOffset.x(), offsetY += ptOffset.y();
-               update();
-               ptPrevious = pt;
-       }
-       else if (event->buttons() == Qt::LeftButton)
+       for(i=document.objects.begin(); i!=document.objects.end(); i++)
        {
-#if 0
-                       if (tool == TOOLScroll)
-                       {
-                           // Extract current point from lParam/calc offset from previous point
-
-                               pt = e.GetPosition();
-                               ptOffset.x = pt.x - ptPrevious.x,
-                               ptOffset.y = pt.y - ptPrevious.y;
-
-                               // NOTE: OffsetViewportOrg operates in DEVICE UNITS...
-
-//Seems there's no equivalent for this in wxWidgets...!
-//!!! FIX !!!
-//                             hdc = GetDC(hWnd);
-//                             OffsetViewportOrgEx(hdc, ptOffset.x, ptOffset.y, NULL);
-//                             ReleaseDC(hWnd, hdc);
-
-// this shows that it works, so the logic above must be faulty...
-// And it is. It should convert the coords first, then do the subtraction to figure the offset...
-// Above: DONE
-// Then multiply it by the scaling factor. Whee!
-                               // This looks wacky because we're using screen coords for the offset...
-                               // Otherwise, we would subtract both offsets!
-                               offsetX -= ptOffset.x, offsetY += ptOffset.y;
-                               Refresh();
-                       }
-                       else
-#endif
-                       if (tool == TOOLAddPt || tool == TOOLAddPoly || tool == TOOLSelect)
-                       {
-                               if (tool != TOOLAddPt || pts.GetNumPoints() > 0)//yecch.
-                               {
-//temporary, for testing. BTW, Select drag bug is here...!
-#if 1
-                                       QPoint pt2 = GetAdjustedMousePosition(event);
-                                       pts.SetXY(ptHighlight, pt2.x(), pt2.y());
-                                       update();
-#endif
-                               }
-                       }
-                       else if (tool == TOOLPolySelect)
-                       {
-                               if (pts.GetNumPoints() > 0)
-                               {
-                                       QPoint pt2 = GetAdjustedMousePosition(event);
-                                       // Should also set onCurve here as well, depending on keystate
-//Or should we?
-                                       pts.OffsetPoly(pts.GetPoly(ptHighlight), pt2.x() - pts.GetX(ptHighlight), pt2.y() - pts.GetY(ptHighlight));
-                                       update();
-                               }
-                       }
+               if (((Object *)(*i))->hovered)
+                       ((Object *)(*i))->selected = true;
        }
-       else if (event->buttons() == Qt::NoButton)
-       {
-               // Moving, not dragging...
-               if (tool == TOOLSelect || tool == TOOLDelPt || tool == TOOLAddPt
-                       || tool == TOOLPolySelect)// || tool == TOOLAddPoly)
-               {
-                       QPoint pt2 = GetAdjustedMousePosition(event);
-                       double closest = 1.0e+99;
-
-                       for(int i=0; i<pts.GetNumPoints(); i++)
-                       {
-                               double dist = ((pt2.x() - pts.GetX(i)) * (pt2.x() - pts.GetX(i)))
-                                       + ((pt2.y() - pts.GetY(i)) * (pt2.y() - pts.GetY(i)));
+}
 
-                               if (dist < closest)
-                                       closest = dist, ptHighlight = i;
-                       }
 
-                       if (ptHighlight != oldPtHighlight)
-                       {
-                               oldPtHighlight = ptHighlight;
-                               update();
-                       }
+void DrawingView::GetSelection(std::vector<void *> & v)
+{
+       v.clear();
+       std::vector<void *>::iterator i;
 
-                       // What follows here looks like voodoo, but is really simple. What we do is
-                       // check to see if the mouse point has a perpendicular intersection with any of
-                       // the line segments. If it does, calculate the length of the perpendicular
-                       // and choose the smallest length. If there is no perpendicular, then choose the
-                       // length of line connecting the closer of either the first endpoint or the
-                       // second and choose the smallest of those.
+       for(i=document.objects.begin(); i!=document.objects.end(); i++)
+       {
+               if (((Object *)(*i))->selected)
+                       v.push_back(*i);
+       }
+}
 
-                       // There is one bit of math that looks like voodoo to me ATM--will explain once
-                       // I understand it better (the calculation of the length of the perpendicular).
 
-                       if (pts.GetNumPoints() > 1 && tool == TOOLAddPt)
-                       {
-                               double smallest = 1.0e+99;
+void DrawingView::GetHovered(std::vector<void *> & v)
+{
+       v.clear();
+       std::vector<void *>::iterator i;
 
-                               for(int i=0; i<pts.GetNumPoints(); i++)
-                               {
-                                       int32 p1x = pts.GetX(i), p1y = pts.GetY(i),
-                                               p2x = pts.GetX(pts.GetNext(i)), p2y = pts.GetY(pts.GetNext(i));
-
-                                       vector ls(p2x, p2y, 0, p1x, p1y, 0), v1(pt2.x(), pt2.y(), 0, p1x, p1y, 0),
-                                               v2(pt2.x(), pt2.y(), 0, p2x, p2y, 0);
-                                       double pp = ls.dot(v1) / ls.length(), dist;
-// Geometric interpretation:
-// pp is the paremeterized point on the vector ls where the perpendicular intersects ls.
-// If pp < 0, then the perpendicular lies beyond the 1st endpoint. If pp > length of ls,
-// then the perpendicular lies beyond the 2nd endpoint.
-
-                                       if (pp < 0.0)
-                                               dist = v1.length();
-                                       else if (pp > ls.length())
-                                               dist = v2.length();
-                                       else                                    // distance = ?Det?(ls, v1) / |ls|
-                                               dist = fabs((ls.x * v1.y - v1.x * ls.y) / ls.length());
-
-//The answer to the above looks like it might be found here:
-//
-//If the segment endpoints are s and e, and the point is p, then the test for the perpendicular
-//intercepting the segment is equivalent to insisting that the two dot products {s-e}.{s-p} and
-//{e-s}.{e-p} are both non-negative.  Perpendicular distance from the point to the segment is
-//computed by first computing the area of the triangle the three points form, then dividing by the
-//length of the segment.  Distances are done just by the Pythagorean theorem.  Twice the area of the
-//triangle formed by three points is the determinant of the following matrix:
-//
-//sx sy 1
-//ex ey 1
-//px py 1
-//
-//By translating the start point to the origin, this can be rewritten as:
-//By subtracting row 1 from all rows, you get the following:
-//[because sx = sy = 0. you could leave out the -sx/y terms below. because we subtracted
-// row 1 from all rows (including row 1) row 1 turns out to be zero. duh!]
-//
-//0         0         0
-//(ex - sx) (ey - sy) 0
-//(px - sx) (py - sy) 0
-//
-//which greatly simplifies the calculation of the determinant.
+       for(i=document.objects.begin(); i!=document.objects.end(); i++)
+       {
+               if (((Object *)(*i))->hovered)
+//             {
+//printf("GetHovered: adding object (%X) to hover... hp1=%s, hp2=%s, hl=%s\n", (*i), (((Line *)(*i))->hitPoint[0] ? "true" : "false"), (((Line *)(*i))->hitPoint[1] ? "true" : "false"), (((Line *)(*i))->hitObject ? "true" : "false"));
+                       v.push_back(*i);
+//             }
+       }
+}
 
-                                       if (dist < smallest)
-                                               smallest = dist, ptNextHighlight = pts.GetNext(i), ptHighlight = i;
-                               }
 
-                               if (ptNextHighlight != oldPtNextHighlight)
-                               {
-                                       oldPtNextHighlight = ptNextHighlight;
-                                       update();
-                               }
-                       }
-               }
+void DrawingView::resizeEvent(QResizeEvent * /*event*/)
+{
+       Global::screenSize = Vector(size().width(), size().height());
+       UpdateGridBackground();
+}
 
-               ptPrevious = event->pos();
-       }
 
-       event->accept();
+void DrawingView::ToolHandler(int mode, Point p)
+{
+       if (Global::tool == TTLine)
+               LineHandler(mode, p);
+       else if (Global::tool == TTCircle)
+               CircleHandler(mode, p);
+       else if (Global::tool == TTArc)
+               ArcHandler(mode, p);
+       else if (Global::tool == TTRotate)
+               RotateHandler(mode, p);
+       else if (Global::tool == TTMirror)
+               MirrorHandler(mode, p);
 }
 
-void DrawingView::mouseReleaseEvent(QMouseEvent * event)
+
+void DrawingView::ToolDraw(Painter * painter)
 {
-       if (event->button() == Qt::RightButton)
+       if (Global::tool == TTLine)
        {
-               ToolType newTool = toolPalette->FindSelectedTool();
-
-               // We only change the tool if a new one was actually selected. Otherwise, we do nothing.
-               if (newTool != TOOLNone)
+               if (Global::toolState == TSNone)
                {
-                       tool = newTool;
-
-                       if (tool == TOOLScroll || tool == TOOLZoom || tool == TOOLAddPoly
-                               || tool == TOOLDelPoly)
-                               ptHighlight = -1;
-
-                       if (tool == TOOLAddPoly)
-                               polyFirstPoint = true;
+                       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);
                }
-
-               toolPalette->setVisible(false);
-               setCursor(cur[tool]);
-               // Just in case we changed highlighting style with the new tool...
-               update();
        }
-       else if (event->button() == Qt::MidButton)
+       else if (Global::tool == TTCircle)
        {
-               setCursor(cur[tool]);                                           // Restore previous cursor
+               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 (event->button() == Qt::LeftButton)
+       else if (Global::tool == TTArc)
        {
-//             if (tool == TOOLScroll || tool == TOOLZoom)
-//                     ReleaseMouse();
-//this is prolly too much
-               ((TTEdit *)qApp)->charWnd->MakePathFromPoints(&pts);
-               ((TTEdit *)qApp)->charWnd->update();
-       }
-
-       event->accept();
+               if (Global::toolState == TSNone)
+               {
+                       painter->DrawHandle(toolPoint[0]);
+               }
+               else if (Global::toolState == TSPoint2)
+               {
+                       double length = Vector::Magnitude(toolPoint[0], toolPoint[1]);
+                       painter->SetBrush(QBrush(Qt::NoBrush));
+                       painter->DrawEllipse(toolPoint[0], length, length);
+                       painter->DrawLine(toolPoint[0], toolPoint[1]);
+                       painter->DrawHandle(toolPoint[1]);
+                       QString text = tr("Radius: %1 in.");
+                       informativeText = text.arg(length);
+               }
+               else if (Global::toolState == TSPoint3)
+               {
+                       double angle = Vector::Angle(toolPoint[0], toolPoint[2]);
+                       painter->DrawLine(toolPoint[0], toolPoint[2]);
+                       painter->SetBrush(QBrush(Qt::NoBrush));
+                       painter->DrawEllipse(toolPoint[0], toolPoint[1].x, toolPoint[1].x);
+                       painter->DrawHandle(toolPoint[0] + (Vector(cos(angle), sin(angle)) * toolPoint[1].x));
+                       QString text = tr("Angle start: %1") + QChar(0x00B0);
+                       informativeText = text.arg(RADIANS_TO_DEGREES * angle);
+               }
+               else
+               {
+                       double angle = Vector::Angle(toolPoint[0], toolPoint[3]);
+                       double span = angle - toolPoint[2].x;
+
+                       if (span < 0)
+                               span += TAU;
+
+                       painter->DrawLine(toolPoint[0], toolPoint[3]);
+                       painter->SetBrush(QBrush(Qt::NoBrush));
+                       painter->DrawEllipse(toolPoint[0], toolPoint[1].x, toolPoint[1].x);
+                       painter->SetPen(0xFF00FF, 2.0, LSSolid);
+                       painter->DrawArc(toolPoint[0], toolPoint[1].x, toolPoint[2].x, span);
+                       painter->DrawHandle(toolPoint[0] + (Vector(cos(angle), sin(angle)) * toolPoint[1].x));
+                       QString text = tr("Arc span: %1") + QChar(0x00B0);
+                       informativeText = text.arg(RADIANS_TO_DEGREES * span);
+               }
+       }
+       else if (Global::tool == TTRotate)
+       {
+               if ((Global::toolState == TSNone) || (Global::toolState == TSPoint1))
+                       painter->DrawHandle(toolPoint[0]);
+               else if ((Global::toolState == TSPoint2) && shiftDown)
+                       painter->DrawHandle(toolPoint[1]);
+               else
+               {
+                       if (toolPoint[0] == toolPoint[1])
+                               return;
+
+                       painter->DrawLine(toolPoint[0], toolPoint[1]);
+
+                       double absAngle = (Vector(toolPoint[1] - toolPoint[0]).Angle()) * RADIANS_TO_DEGREES;
+                       QString text = QChar(0x2221) + QObject::tr(": %1");
+                       informativeText = text.arg(absAngle);
+
+                       if (ctrlDown)
+                               informativeText += " (Copy)";
+               }
+       }
+       else if (Global::tool == TTMirror)
+       {
+               if ((Global::toolState == TSNone) || (Global::toolState == TSPoint1))
+                       painter->DrawHandle(toolPoint[0]);
+               else if ((Global::toolState == TSPoint2) && shiftDown)
+                       painter->DrawHandle(toolPoint[1]);
+               else
+               {
+                       if (toolPoint[0] == toolPoint[1])
+                               return;
+                       
+                       Point mirrorPoint = toolPoint[0] + Vector(toolPoint[1], toolPoint[0]);
+                       painter->DrawLine(mirrorPoint, toolPoint[1]);
+
+                       double absAngle = (Vector(toolPoint[1] - toolPoint[0]).Angle()) * RADIANS_TO_DEGREES;
+
+                       if (absAngle > 180.0)
+                               absAngle -= 180.0;
+
+                       QString text = QChar(0x2221) + QObject::tr(": %1");
+                       informativeText = text.arg(absAngle);
+
+                       if (ctrlDown)
+                               informativeText += " (Copy)";
+               }
+       }
+}
+
+
+void DrawingView::LineHandler(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
+               {
+                       Line * l = new Line(toolPoint[0], toolPoint[1]);
+                       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);
+                       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;
+               else
+                       toolPoint[3] = p;
+
+               break;
+       case ToolMouseUp:
+               if (Global::toolState == TSNone)
+               {
+                       // Prevent spurious line from drawing...
+                       toolPoint[1] = toolPoint[0];
+                       Global::toolState = TSPoint2;
+               }
+               else if (Global::toolState == TSPoint2)
+               {
+                       if (shiftDown)
+                       {
+                               // Key override is telling us to start circle at new center, not
+                               // continue the current one.
+                               toolPoint[0] = toolPoint[1];
+                               return;
+                       }
+
+                       // Set the radius in toolPoint[1].x
+                       toolPoint[1].x = Vector::Magnitude(toolPoint[0], toolPoint[1]);
+                       Global::toolState = TSPoint3;
+               }
+               else if (Global::toolState == TSPoint3)
+               {
+                       // Set the angle in toolPoint[2].x
+                       toolPoint[2].x = Vector::Angle(toolPoint[0], toolPoint[2]);
+                       Global::toolState = TSPoint4;
+               }
+               else
+               {
+                       double endAngle = Vector::Angle(toolPoint[0], toolPoint[3]);
+                       double span = endAngle - toolPoint[2].x;
+
+                       if (span < 0)
+                               span += TAU;
+
+                       Arc * arc = new Arc(toolPoint[0], toolPoint[1].x, toolPoint[2].x, span);
+                       arc->layer = Global::activeLayer;
+                       document.objects.push_back(arc);
+                       Global::toolState = TSNone;
+               }
+       }
+}
+
+
+void DrawingView::RotateHandler(int mode, Point p)
+{
+       switch (mode)
+       {
+       case ToolMouseDown:
+               if (Global::toolState == TSNone)
+               {
+                       toolPoint[0] = p;
+                       SavePointsFrom(select, toolScratch);
+                       Global::toolState = TSPoint1;
+               }
+               else if (Global::toolState == TSPoint1)
+                       toolPoint[0] = p;
+               else
+                       toolPoint[1] = p;
+
+               break;
+       case ToolMouseMove:
+               if ((Global::toolState == TSPoint1) || (Global::toolState == TSNone))
+                       toolPoint[0] = p;
+               else if (Global::toolState == TSPoint2)
+               {
+                       toolPoint[1] = p;
+
+                       if (shiftDown)
+                               return;
+
+                       double angle = Vector(toolPoint[0], toolPoint[1]).Angle();
+                       std::vector<void *>::iterator j = select.begin();
+                       std::vector<Object>::iterator i = toolScratch.begin();
+
+                       for(; i!=toolScratch.end(); i++, j++)
+                       {
+                               Object obj = *i;
+                               Point p1 = Geometry::RotatePointAroundPoint(obj.p[0], toolPoint[0], angle);
+                               Point p2 = Geometry::RotatePointAroundPoint(obj.p[1], toolPoint[0], angle);
+                               Object * obj2 = (Object *)(*j);
+                               obj2->p[0] = p1;
+                               obj2->p[1] = p2;
+
+                               if (obj.type == OTArc)
+                               {
+                                       obj2->angle[0] = obj.angle[0] + angle;
+
+                                       if (obj2->angle[0] > TAU)
+                                               obj2->angle[0] -= TAU;
+                               }
+                       }
+               }
+
+               break;
+       case ToolMouseUp:
+               if (Global::toolState == TSPoint1)
+               {
+                       Global::toolState = TSPoint2;
+                       // Prevent spurious line from drawing...
+                       toolPoint[1] = toolPoint[0];
+               }
+               else if ((Global::toolState == TSPoint2) && shiftDown)
+               {
+                       // Key override is telling us to make a new line, not continue the
+                       // previous one.
+                       toolPoint[0] = toolPoint[1];
+               }
+               else
+               {
+                       // Either we're finished with our rotate, or we're stamping a copy.
+                       if (ctrlDown)
+                       {
+                               // Stamp a copy of the selection at the current rotation & bail
+                               std::vector<void *> temp;
+                               CopyObjects(select, temp);
+                               ClearSelected(temp);
+                               AddObjectsTo(document.objects, temp);
+                               RestorePointsTo(select, toolScratch);
+                               return;
+                       }
+
+                       toolPoint[0] = p;
+                       Global::toolState = TSPoint1;
+                       SavePointsFrom(select, toolScratch);
+               }
+
+               break;
+       case ToolKeyDown:
+               // Reset the selection if shift held down...
+               if (shiftDown)
+                       RestorePointsTo(select, toolScratch);
+
+               break;
+       case ToolKeyUp:
+               // Reset selection when key is let up
+               if (!shiftDown)
+                       RotateHandler(ToolMouseMove, toolPoint[1]);
+
+               break;
+       case ToolCleanup:
+               RestorePointsTo(select, toolScratch);
+       }
 }
+
+
+void DrawingView::MirrorHandler(int mode, Point p)
+{
+       switch (mode)
+       {
+       case ToolMouseDown:
+               if (Global::toolState == TSNone)
+               {
+                       toolPoint[0] = p;
+                       SavePointsFrom(select, toolScratch);
+                       Global::toolState = TSPoint1;
+               }
+               else if (Global::toolState == TSPoint1)
+                       toolPoint[0] = p;
+               else
+                       toolPoint[1] = p;
+
+               break;
+       case ToolMouseMove:
+               if ((Global::toolState == TSPoint1) || (Global::toolState == TSNone))
+                       toolPoint[0] = p;
+               else if (Global::toolState == TSPoint2)
+               {
+                       toolPoint[1] = p;
+
+                       if (shiftDown)
+                               return;
+
+                       double angle = Vector(toolPoint[0], toolPoint[1]).Angle();
+                       std::vector<void *>::iterator j = select.begin();
+                       std::vector<Object>::iterator i = toolScratch.begin();
+
+                       for(; i!=toolScratch.end(); i++, j++)
+                       {
+                               Object obj = *i;
+                               Point p1 = Geometry::MirrorPointAroundLine(obj.p[0], toolPoint[0], toolPoint[1]);
+                               Point p2 = Geometry::MirrorPointAroundLine(obj.p[1], toolPoint[0], toolPoint[1]);
+                               Object * obj2 = (Object *)(*j);
+                               obj2->p[0] = p1;
+                               obj2->p[1] = p2;
+
+                               if (obj.type == OTArc)
+                               {
+                                       // This is 2*mirror angle - obj angle - obj span
+                                       obj2->angle[0] = (2.0 * angle) - obj.angle[0] - obj.angle[1];
+
+                                       if (obj2->angle[0] > TAU)
+                                               obj2->angle[0] -= TAU;
+                               }
+                       }
+               }
+
+               break;
+       case ToolMouseUp:
+               if (Global::toolState == TSPoint1)
+               {
+                       Global::toolState = TSPoint2;
+                       // Prevent spurious line from drawing...
+                       toolPoint[1] = toolPoint[0];
+               }
+               else if ((Global::toolState == TSPoint2) && shiftDown)
+               {
+                       // Key override is telling us to make a new line, not continue the
+                       // previous one.
+                       toolPoint[0] = toolPoint[1];
+               }
+               else
+               {
+                       // Either we're finished with our rotate, or we're stamping a copy.
+                       if (ctrlDown)
+                       {
+                               // Stamp a copy of the selection at the current rotation & bail
+                               std::vector<void *> temp;
+                               CopyObjects(select, temp);
+                               ClearSelected(temp);
+                               AddObjectsTo(document.objects, temp);
+                               RestorePointsTo(select, toolScratch);
+                               return;
+                       }
+
+                       toolPoint[0] = p;
+                       Global::toolState = TSPoint1;
+                       SavePointsFrom(select, toolScratch);
+               }
+
+               break;
+       case ToolKeyDown:
+               // Reset the selection if shift held down...
+               if (shiftDown)
+                       RestorePointsTo(select, toolScratch);
+
+               break;
+       case ToolKeyUp:
+               // Reset selection when key is let up
+               if (!shiftDown)
+                       MirrorHandler(ToolMouseMove, toolPoint[1]);
+
+               break;
+       case ToolCleanup:
+               RestorePointsTo(select, toolScratch);
+       }
+}
+
+
+void DrawingView::mousePressEvent(QMouseEvent * event)
+{
+       if (event->button() == Qt::LeftButton)
+       {
+               Vector point = Painter::QtToCartesianCoords(Vector(event->x(), event->y()));
+
+               // Handle tool processing, if any
+               if (Global::tool)
+               {
+                       if (hoveringIntersection)
+                               point = intersectionPoint;
+                       else if (Global::snapToGrid)
+                               point = SnapPointToGrid(point);
+
+                       //Also, may want to figure out if hovering over a snap point on an object,
+                       //snap to grid if not.
+                       // Snap to object point if valid...
+//                     if (Global::snapPointIsValid)
+//                             point = Global::snapPoint;
+                       
+                       ToolHandler(ToolMouseDown, point);
+                       return;
+               }
+
+               // Clear the selection only if CTRL isn't being held on click
+               if (!ctrlDown)
+                       ClearSelected(document.objects);
+//                     ClearSelection();
+
+               // If any objects are being hovered on click, add them to the selection
+               // & return
+               if (numHovered > 0)
+               {
+                       AddHoveredToSelection();
+                       update();       // needed??
+                       GetHovered(hover);      // prolly needed
+
+                       // Needed for grab & moving objects
+                       // We do it *after*... why? (doesn't seem to confer any advantage...)
+                       if (hoveringIntersection)
+                               oldPoint = intersectionPoint;
+                       else if (Global::snapToGrid)
+                               oldPoint = SnapPointToGrid(point);
+
+                       return;
+               }
+
+               // Didn't hit any object and not using a tool, so do a selection rectangle
+               Global::selectionInProgress = true;
+               Global::selection.setTopLeft(QPointF(point.x, point.y));
+               Global::selection.setBottomRight(QPointF(point.x, point.y));
+       }
+       else if (event->button() == Qt::MiddleButton)
+       {
+               scrollDrag = true;
+               oldPoint = Vector(event->x(), event->y());
+               // Should also change the mouse pointer as well...
+               setCursor(Qt::SizeAllCursor);
+       }
+}
+
+
+void DrawingView::mouseMoveEvent(QMouseEvent * event)
+{
+       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;
+
+       // Scrolling...
+       if (event->buttons() & Qt::MiddleButton)
+       {
+               point = Vector(event->x(), event->y());
+               // Since we're using Qt coords for scrolling, we have to adjust them
+               // here to conform to Cartesian coords, since the origin is using
+               // Cartesian. :-)
+               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();
+               update();
+               return;
+       }
+
+       // Handle object movement (left button down & over an object)
+       if ((event->buttons() & Qt::LeftButton) && numHovered && !Global::tool)
+       {
+               if (hoveringIntersection)
+                       point = intersectionPoint;
+               else if (Global::snapToGrid)
+                       point = SnapPointToGrid(point);
+
+               HandleObjectMovement(point);
+               update();
+               oldPoint = point;
+               return;
+       }
+
+       // Do object hit testing...
+       bool needUpdate = HitTestObjects(point);
+
+       // Check for multi-hover...
+       if (numHovered > 1)
+       {
+               GetHovered(hover);
+               Geometry::Intersects((Object *)hover[0], (Object *)hover[1]);
+               int numIntersecting = Global::numIntersectParams;
+               double t = Global::intersectParam[0];
+               double u = Global::intersectParam[1];
+
+               if (numIntersecting > 0)
+               {
+                       Vector v1 = Geometry::GetPointForParameter((Object *)hover[0], t);
+                       Vector v2 = Geometry::GetPointForParameter((Object *)hover[1], u);
+                       QString text = tr("Intersection t=%1 (%3, %4), u=%2 (%5, %6)");
+                       informativeText = text.arg(t).arg(u).arg(v1.x).arg(v1.y).arg(v2.x).arg(v2.y);
+
+                       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;
+               }
+       }
+
+//this doesn't work down here for some reason... :-P
+//could be because the object being moved is part of the intersection, and this is screwing things up. In which case, we need to exclude the moving object somehow from the hit test function...
+#if 0
+       // Handle object movement (left button down & over an object)
+       if ((event->buttons() & Qt::LeftButton) && numHovered && !Global::tool)
+       {
+               if (hoveringIntersection)
+                       point = intersectionPoint;
+               else if (Global::snapToGrid)
+                       point = SnapPointToGrid(point);
+
+               HandleObjectMovement(point);
+               update();
+               oldPoint = point;
+               return;
+       }
 #endif
+
+       // Do tool handling, if any are active...
+       if (Global::tool)
+       {
+               if (hoveringIntersection)
+                       point = intersectionPoint;
+               else if (Global::snapToGrid)
+                       point = SnapPointToGrid(point);
+
+               ToolHandler(ToolMouseMove, point);
+       }
+
+       // This is used to draw the tool crosshair...
+       oldPoint = point;
+
+       if (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;
+               }
+
+               if (Global::selectionInProgress)
+                       Global::selectionInProgress = false;
+
+               informativeText.clear();
+// Should we be doing this automagically? Hmm...
+               // Clear our vectors
+               select.clear();
+               hover.clear();
+
+               // Scoop 'em up
+               std::vector<void *>::iterator i;
+
+               for(i=document.objects.begin(); i!=document.objects.end(); i++)
+               {
+                       if (((Object *)(*i))->selected)
+                               select.push_back(*i);
+
+//hmm, this is no good, too late to do any good :-P
+//                     if ((*i)->hovered)
+//                             hover.push_back(*i);
+               }
+       }
+       else if (event->button() == Qt::MiddleButton)
+       {
+               scrollDrag = false;
+               setCursor(Qt::ArrowCursor);
+       }
+}
+
+
+void DrawingView::wheelEvent(QWheelEvent * event)
+{
+       double zoomFactor = 1.25;
+       QSize sizeWin = size();
+       Vector center(sizeWin.width() / 2.0, sizeWin.height() / 2.0);
+       center = Painter::QtToCartesianCoords(center);
+
+       // This is not centering for some reason. Need to figure out why. :-/
+       if (event->delta() > 0)
+       {
+               Vector newOrigin = center - ((center - Global::origin) / zoomFactor);
+               Global::origin = newOrigin;
+               Global::zoom *= zoomFactor;
+       }
+       else
+       {
+               Vector newOrigin = center + ((-center + Global::origin) * zoomFactor);
+               Global::origin = newOrigin;
+               Global::zoom /= zoomFactor;
+       }
+
+//     Global::gridSpacing = gridPixels / Painter::zoom;
+//     UpdateGridBackground();
+       SetGridSize(Global::gridSpacing * Global::zoom);
+       update();
+//     zoomIndicator->setText(QString("Grid: %1\", BU: Inch").arg(Global::gridSpacing));
+}
+
+
+void DrawingView::keyPressEvent(QKeyEvent * event)
+{
+       bool oldShift = shiftDown;
+       bool oldCtrl = ctrlDown;
+
+       if (event->key() == Qt::Key_Shift)
+               shiftDown = true;
+       else if (event->key() == Qt::Key_Control)
+               ctrlDown = true;
+
+       if ((oldShift != shiftDown) || (oldCtrl != ctrlDown))
+       {
+               if (Global::tool)
+                       ToolHandler(ToolKeyDown, Point(0, 0));
+
+               update();
+       }
+}
+
+
+void DrawingView::keyReleaseEvent(QKeyEvent * event)
+{
+       bool oldShift = shiftDown;
+       bool oldCtrl = ctrlDown;
+
+       if (event->key() == Qt::Key_Shift)
+               shiftDown = false;
+       else if (event->key() == Qt::Key_Control)
+               ctrlDown = false;
+
+       if ((oldShift != shiftDown) || (oldCtrl != ctrlDown))
+       {
+               if (Global::tool)
+                       ToolHandler(ToolKeyUp, Point(0, 0));
+
+               update();
+       }
+}
+
+
+//
+// This looks strange, but it's really quite simple: We want a point that's
+// more than half-way to the next grid point to snap there while conversely we
+// 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;
+}
+
+
+Rect DrawingView::GetObjectExtents(Object * obj)
+{
+       // Default to empty rect, if object checks below fail for some reason
+       Rect rect;
+
+       switch (obj->type)
+       {
+       case OTLine:
+       {
+               rect = Rect(obj->p[0], obj->p[1]);
+               break;
+       }
+       case OTCircle:
+       {
+               rect = Rect(obj->p[0], obj->p[0]);
+               rect.Expand(obj->radius[0]);
+               break;
+       }
+       case OTArc:
+       {
+               Arc * a = (Arc *)obj;
+
+               double start = a->angle[0];
+               double end = start + a->angle[1];
+               rect = Rect(Point(cos(start), sin(start)), Point(cos(end), sin(end)));
+
+               // If the end of the arc is before the beginning, add 360 degrees to it
+               if (end < start)
+                       end += TAU;
+
+               // Adjust the bounds depending on which axes are crossed
+               if ((start < QTR_TAU) && (end > QTR_TAU))
+                       rect.t = 1.0;
+
+               if ((start < HALF_TAU) && (end > HALF_TAU))
+                       rect.l = -1.0;
+
+               if ((start < THREE_QTR_TAU) && (end > THREE_QTR_TAU))
+                       rect.b = -1.0;
+
+               if ((start < TAU) && (end > TAU))
+                       rect.r = 1.0;
+
+               if ((start < (TAU + QTR_TAU)) && (end > (TAU + QTR_TAU)))
+                       rect.t = 1.0;
+
+               if ((start < (TAU + HALF_TAU)) && (end > (TAU + HALF_TAU)))
+                       rect.l = -1.0;
+
+               if ((start < (TAU + THREE_QTR_TAU)) && (end > (TAU + THREE_QTR_TAU)))
+                       rect.b = -1.0;
+
+               rect *= a->radius[0];
+               rect.Translate(a->p[0]);
+
+               break;
+       }
+       case OTContainer:
+       {
+               Container * c = (Container *)obj;
+               std::vector<void *>::iterator i = c->objects.begin();
+               rect = GetObjectExtents((Object *)*i);
+               i++;
+
+               for(; i!=c->objects.end(); i++)
+                       rect |= GetObjectExtents((Object *)*i);
+       }
+       default:
+               break;
+       }
+
+       return rect;
+}
+
+
+void DrawingView::CheckObjectBounds(void)
+{
+       std::vector<void *>::iterator i;
+
+       for(i=document.objects.begin(); i!=document.objects.end(); i++)
+       {
+               Object * obj = (Object *)(*i);
+               obj->selected = false;
+
+               switch (obj->type)
+               {
+               case OTLine:
+               {
+                       Line * l = (Line *)obj;
+
+                       if (Global::selection.contains(l->p[0].x, l->p[0].y) && Global::selection.contains(l->p[1].x, l->p[1].y))
+                               l->selected = true;
+
+                       break;
+               }
+               case OTCircle:
+               {
+                       Circle * c = (Circle *)obj;
+
+                       if (Global::selection.contains(c->p[0].x - c->radius[0], c->p[0].y - c->radius[0]) && Global::selection.contains(c->p[0].x + c->radius[0], c->p[0].y + c->radius[0]))
+                               c->selected = true;
+
+                       break;
+               }
+               case OTArc:
+               {
+                       Arc * a = (Arc *)obj;
+
+                       double start = a->angle[0];
+                       double end = start + a->angle[1];
+                       QPointF p1(cos(start), sin(start));
+                       QPointF p2(cos(end), sin(end));
+                       QRectF bounds(p1, p2);
+
+#if 1
+                       // Swap X/Y coordinates if they're backwards...
+                       if (bounds.left() > bounds.right())
+                       {
+                               double temp = bounds.left();
+                               bounds.setLeft(bounds.right());
+                               bounds.setRight(temp);
+                       }
+
+                       if (bounds.bottom() > bounds.top())
+                       {
+                               double temp = bounds.bottom();
+                               bounds.setBottom(bounds.top());
+                               bounds.setTop(temp);
+                       }
+#else
+                       // Doesn't work as advertised! For shame!
+                       bounds = bounds.normalized();
+#endif
+
+                       // If the end of the arc is before the beginning, add 360 degrees to it
+                       if (end < start)
+                               end += TAU;
+
+                       // Adjust the bounds depending on which axes are crossed
+                       if ((start < QTR_TAU) && (end > QTR_TAU))
+                               bounds.setTop(1.0);
+
+                       if ((start < HALF_TAU) && (end > HALF_TAU))
+                               bounds.setLeft(-1.0);
+
+                       if ((start < THREE_QTR_TAU) && (end > THREE_QTR_TAU))
+                               bounds.setBottom(-1.0);
+
+                       if ((start < TAU) && (end > TAU))
+                               bounds.setRight(1.0);
+
+                       if ((start < (TAU + QTR_TAU)) && (end > (TAU + QTR_TAU)))
+                               bounds.setTop(1.0);
+
+                       if ((start < (TAU + HALF_TAU)) && (end > (TAU + HALF_TAU)))
+                               bounds.setLeft(-1.0);
+
+                       if ((start < (TAU + THREE_QTR_TAU)) && (end > (TAU + THREE_QTR_TAU)))
+                               bounds.setBottom(-1.0);
+
+                       bounds.setTopLeft(QPointF(bounds.left() * a->radius[0], bounds.top() * a->radius[0]));
+                       bounds.setBottomRight(QPointF(bounds.right() * a->radius[0], bounds.bottom() * a->radius[0]));
+                       bounds.translate(a->p[0].x, a->p[0].y);
+
+                       if (Global::selection.contains(bounds))
+                               a->selected = true;
+
+                       break;
+               }
+               default:
+                       break;
+               }
+       }
+}
+
+
+bool DrawingView::HitTestObjects(Point point)
+{
+       std::vector<void *>::iterator i;
+       numHovered = 0;
+       bool needUpdate = false;
+
+       for(i=document.objects.begin(); i!=document.objects.end(); i++)
+       {
+               Object * obj = (Object *)(*i);
+
+               if (HitTest(obj, point))
+                       needUpdate = true;
+#if 0
+               switch (obj->type)
+               {
+               case OTLine:
+               {
+                       bool oldHP0 = obj->hitPoint[0], oldHP1 = obj->hitPoint[1], oldHO = obj->hitObject;
+                       obj->hitPoint[0] = obj->hitPoint[1] = obj->hitObject = false;
+                       Vector lineSegment = obj->p[1] - obj->p[0];
+                       Vector v1 = point - obj->p[0];
+                       Vector v2 = point - obj->p[1];
+                       double t = Geometry::ParameterOfLineAndPoint(obj->p[0], obj->p[1], point);
+                       double distance;
+
+                       if (t < 0.0)
+                               distance = v1.Magnitude();
+                       else if (t > 1.0)
+                               distance = v2.Magnitude();
+                       else
+                               // distance = ?Det?(ls, v1) / |ls|
+                               distance = fabs((lineSegment.x * v1.y - v1.x * lineSegment.y)
+                                       / lineSegment.Magnitude());
+
+                       if ((v1.Magnitude() * Global::zoom) < 8.0)
+                               obj->hitPoint[0] = true;
+                       else if ((v2.Magnitude() * Global::zoom) < 8.0)
+                               obj->hitPoint[1] = true;
+                       else if ((distance * Global::zoom) < 5.0)
+                               obj->hitObject = true;
+
+                       obj->hovered = (obj->hitPoint[0] || obj->hitPoint[1] || obj->hitObject ? true : false);
+
+                       if ((oldHP0 != obj->hitPoint[0]) || (oldHP1 != obj->hitPoint[1]) || (oldHO != obj->hitObject))
+                               needUpdate = true;
+
+                       break;
+               }
+               case OTCircle:
+               {
+                       bool oldHP = obj->hitPoint[0], oldHO = obj->hitObject;
+                       obj->hitPoint[0] = obj->hitObject = false;
+                       double length = Vector::Magnitude(obj->p[0], point);
+
+                       if ((length * Global::zoom) < 8.0)
+                               obj->hitPoint[0] = true;
+                       else if ((fabs(length - obj->radius[0]) * Global::zoom) < 2.0)
+                               obj->hitObject = true;
+
+                       obj->hovered = (obj->hitPoint[0] || obj->hitObject ? true : false);
+
+                       if ((oldHP != obj->hitPoint[0]) || (oldHO != obj->hitObject))
+                               needUpdate = true;
+
+                       break;
+               }
+               case OTArc:
+               {
+                       bool oldHP0 = obj->hitPoint[0], oldHP1 = obj->hitPoint[1], oldHP2 = obj->hitPoint[2], oldHO = obj->hitObject;
+                       obj->hitPoint[0] = obj->hitPoint[1] = obj->hitPoint[2] = obj->hitObject = false;
+                       double length = Vector::Magnitude(obj->p[0], point);
+                       double angle = Vector::Angle(obj->p[0], point);
+
+                       // Make sure we get the angle in the correct spot
+                       if (angle < obj->angle[0])
+                               angle += TAU;
+
+                       // Get the span that we're pointing at...
+                       double span = angle - obj->angle[0];
+
+                       // N.B.: Still need to hit test the arc start & arc span handles...
+                       double spanAngle = obj->angle[0] + obj->angle[1];
+                       Point handle1 = obj->p[0] + (Vector(cos(obj->angle[0]), sin(obj->angle[0])) * obj->radius[0]);
+                       Point handle2 = obj->p[0] + (Vector(cos(spanAngle), sin(spanAngle)) * obj->radius[0]);
+                       double length2 = Vector::Magnitude(point, handle1);
+                       double length3 = Vector::Magnitude(point, handle2);
+
+                       if ((length * Global::zoom) < 8.0)
+                               obj->hitPoint[0] = true;
+                       else if ((length2 * Global::zoom) < 8.0)
+                               obj->hitPoint[1] = true;
+                       else if ((length3 * Global::zoom) < 8.0)
+                               obj->hitPoint[2] = true;
+                       else if (((fabs(length - obj->radius[0]) * Global::zoom) < 2.0) && (span < obj->angle[1]))
+                               obj->hitObject = true;
+
+                       obj->hovered = (obj->hitPoint[0] || obj->hitPoint[1] || obj->hitPoint[2] || obj->hitObject ? true : false);
+
+                       if ((oldHP0 != obj->hitPoint[0]) || (oldHP1 != obj->hitPoint[1]) || (oldHP2 != obj->hitPoint[2]) || (oldHO != obj->hitObject))
+                               needUpdate = true;
+
+                       break;
+               }
+               case OTContainer:
+               {
+                       // Containers must be recursively tested...
+                       Container * c = (Container *)obj;
+                       std::vector<void *>::iterator i;
+
+                       for(i=c->objects.begin(); i!=c->objects.end(); i++)
+                       {
+                               
+                       }
+               }
+               default:
+                       break;
+               }
+#endif
+
+               if (obj->hovered)
+               {
+                       numHovered++;
+//printf("MouseMove: OBJECT HOVERED (numHovered = %i)\n", numHovered);
+                       emit(ObjectHovered(obj));
+               }
+       }
+
+       return needUpdate;
+}
+
+
+bool DrawingView::HitTest(Object * obj, Point point)
+{
+       bool needUpdate = false;
+
+       switch (obj->type)
+       {
+       case OTLine:
+       {
+               bool oldHP0 = obj->hitPoint[0], oldHP1 = obj->hitPoint[1], oldHO = obj->hitObject;
+               obj->hitPoint[0] = obj->hitPoint[1] = obj->hitObject = false;
+               Vector lineSegment = obj->p[1] - obj->p[0];
+               Vector v1 = point - obj->p[0];
+               Vector v2 = point - obj->p[1];
+               double t = Geometry::ParameterOfLineAndPoint(obj->p[0], obj->p[1], point);
+               double distance;
+
+               if (t < 0.0)
+                       distance = v1.Magnitude();
+               else if (t > 1.0)
+                       distance = v2.Magnitude();
+               else
+                       // distance = ?Det?(ls, v1) / |ls|
+                       distance = fabs((lineSegment.x * v1.y - v1.x * lineSegment.y)
+                               / lineSegment.Magnitude());
+
+               if ((v1.Magnitude() * Global::zoom) < 8.0)
+                       obj->hitPoint[0] = true;
+               else if ((v2.Magnitude() * Global::zoom) < 8.0)
+                       obj->hitPoint[1] = true;
+               else if ((distance * Global::zoom) < 5.0)
+                       obj->hitObject = true;
+
+               obj->hovered = (obj->hitPoint[0] || obj->hitPoint[1] || obj->hitObject ? true : false);
+
+               if ((oldHP0 != obj->hitPoint[0]) || (oldHP1 != obj->hitPoint[1]) || (oldHO != obj->hitObject))
+                       needUpdate = true;
+
+               break;
+       }
+       case OTCircle:
+       {
+               bool oldHP = obj->hitPoint[0], oldHO = obj->hitObject;
+               obj->hitPoint[0] = obj->hitObject = false;
+               double length = Vector::Magnitude(obj->p[0], point);
+
+               if ((length * Global::zoom) < 8.0)
+                       obj->hitPoint[0] = true;
+               else if ((fabs(length - obj->radius[0]) * Global::zoom) < 2.0)
+                       obj->hitObject = true;
+
+               obj->hovered = (obj->hitPoint[0] || obj->hitObject ? true : false);
+
+               if ((oldHP != obj->hitPoint[0]) || (oldHO != obj->hitObject))
+                       needUpdate = true;
+
+               break;
+       }
+       case OTArc:
+       {
+               bool oldHP0 = obj->hitPoint[0], oldHP1 = obj->hitPoint[1], oldHP2 = obj->hitPoint[2], oldHO = obj->hitObject;
+               obj->hitPoint[0] = obj->hitPoint[1] = obj->hitPoint[2] = obj->hitObject = false;
+               double length = Vector::Magnitude(obj->p[0], point);
+               double angle = Vector::Angle(obj->p[0], point);
+
+               // Make sure we get the angle in the correct spot
+               if (angle < obj->angle[0])
+                       angle += TAU;
+
+               // Get the span that we're pointing at...
+               double span = angle - obj->angle[0];
+
+               // N.B.: Still need to hit test the arc start & arc span handles...
+               double spanAngle = obj->angle[0] + obj->angle[1];
+               Point handle1 = obj->p[0] + (Vector(cos(obj->angle[0]), sin(obj->angle[0])) * obj->radius[0]);
+               Point handle2 = obj->p[0] + (Vector(cos(spanAngle), sin(spanAngle)) * obj->radius[0]);
+               double length2 = Vector::Magnitude(point, handle1);
+               double length3 = Vector::Magnitude(point, handle2);
+
+               if ((length * Global::zoom) < 8.0)
+                       obj->hitPoint[0] = true;
+               else if ((length2 * Global::zoom) < 8.0)
+                       obj->hitPoint[1] = true;
+               else if ((length3 * Global::zoom) < 8.0)
+                       obj->hitPoint[2] = true;
+               else if (((fabs(length - obj->radius[0]) * Global::zoom) < 2.0) && (span < obj->angle[1]))
+                       obj->hitObject = true;
+
+               obj->hovered = (obj->hitPoint[0] || obj->hitPoint[1] || obj->hitPoint[2] || obj->hitObject ? true : false);
+
+               if ((oldHP0 != obj->hitPoint[0]) || (oldHP1 != obj->hitPoint[1]) || (oldHP2 != obj->hitPoint[2]) || (oldHO != obj->hitObject))
+                       needUpdate = true;
+
+               break;
+       }
+       case OTContainer:
+       {
+               // Containers must be recursively tested...
+               Container * c = (Container *)obj;
+               c->hitObject = false;
+               c->hovered = false;
+               std::vector<void *>::iterator i;
+
+               for(i=c->objects.begin(); i!=c->objects.end(); i++)
+               {
+                       Object * cObj = (Object *)*i;
+
+                       if (HitTest(cObj, point))
+                               needUpdate = true;
+
+                       if (cObj->hitObject == true)
+                               c->hitObject = true;
+
+                       if (cObj->hovered == true)
+                               c->hovered = true;
+               }
+
+               break;
+       }
+       default:
+               break;
+       }
+
+       return needUpdate;
+}
+
+
+void DrawingView::HandleObjectMovement(Point point)
+{
+       Point delta = point - oldPoint;
+//printf("HOM: old = (%f,%f), new = (%f, %f), delta = (%f, %f)\n", oldPoint.x, oldPoint.y, point.x, point.y, delta.x, delta.y);
+       Object * obj = (Object *)hover[0];
+//printf("Object type = %i (size=%i), ", obj->type, hover.size());
+//printf("Object (%X) move: hp1=%s, hp2=%s, hl=%s\n", obj, (obj->hitPoint[0] ? "true" : "false"), (obj->hitPoint[1] ? "true" : "false"), (obj->hitObject ? "true" : "false"));
+
+       switch (obj->type)
+       {
+       case OTLine:
+               if (obj->hitPoint[0])
+                       obj->p[0] = point;
+               else if (obj->hitPoint[1])
+                       obj->p[1] = point;
+               else if (obj->hitObject)
+               {
+                       obj->p[0] += delta;
+                       obj->p[1] += delta;
+               }
+
+               break;
+       case OTCircle:
+               if (obj->hitPoint[0])
+                       obj->p[0] = point;
+               else if (obj->hitObject)
+               {
+//this doesn't work. we need to save this on mouse down for this to work correctly!
+//                     double oldRadius = obj->radius[0];
+                       obj->radius[0] = Vector::Magnitude(obj->p[0], point);
+
+                       QString text = QObject::tr("Radius: %1");//\nScale: %2%");
+                       informativeText = text.arg(obj->radius[0], 0, 'd', 4);//.arg(obj->radius[0] / oldRadius * 100.0, 0, 'd', 0);
+               }
+
+               break;
+       case OTArc:
+               if (obj->hitPoint[0])
+                       obj->p[0] = point;
+               else if (obj->hitPoint[1])
+               {
+                       // Change the Arc's span (handle #1)
+                       if (shiftDown)
+                       {
+                               double angle = Vector::Angle(obj->p[0], point);
+                               double delta = angle - obj->angle[0];
+
+                               if (delta < 0)
+                                       delta += TAU;
+
+                               obj->angle[1] -= delta;
+                               obj->angle[0] = angle;
+
+                               if (obj->angle[1] < 0)
+                                       obj->angle[1] += TAU;
+
+                               QString text = QObject::tr("Span: %1") + QChar(0x00B0) + QObject::tr("\n%2") + QChar(0x00B0) + QObject::tr(" - %3") + QChar(0x00B0);
+                               informativeText = text.arg(obj->angle[1] * RADIANS_TO_DEGREES, 0, 'd', 4).arg(obj->angle[0] * RADIANS_TO_DEGREES, 0, 'd', 2).arg((obj->angle[0] + obj->angle[1]) * RADIANS_TO_DEGREES, 0, 'd', 2);
+                               return;
+                       }
+
+                       double angle = Vector::Angle(obj->p[0], point);
+                       obj->angle[0] = angle;
+                       QString text = QObject::tr("Start angle: %1") + QChar(0x00B0);
+                       informativeText = text.arg(obj->angle[0] * RADIANS_TO_DEGREES, 0, 'd', 4);
+               }
+               else if (obj->hitPoint[2])
+               {
+                       // Change the Arc's span (handle #2)
+                       if (shiftDown)
+                       {
+                               double angle = Vector::Angle(obj->p[0], point);
+                               obj->angle[1] = angle - obj->angle[0];
+
+                               if (obj->angle[1] < 0)
+                                       obj->angle[1] += TAU;
+
+                               QString text = QObject::tr("Span: %1") + QChar(0x00B0) + QObject::tr("\n%2") + QChar(0x00B0) + QObject::tr(" - %3") + QChar(0x00B0);
+                               informativeText = text.arg(obj->angle[1] * RADIANS_TO_DEGREES, 0, 'd', 4).arg(obj->angle[0] * RADIANS_TO_DEGREES, 0, 'd', 2).arg((obj->angle[0] + obj->angle[1]) * RADIANS_TO_DEGREES, 0, 'd', 2);
+                               return;
+                       }
+
+                       double angle = Vector::Angle(obj->p[0], point);
+                       obj->angle[0] = angle - obj->angle[1];
+
+                       if (obj->angle[0] < 0)
+                               obj->angle[0] += TAU;
+
+                       QString text = QObject::tr("End angle: %1") + QChar(0x00B0);
+                       informativeText = text.arg((obj->angle[0] + obj->angle[1]) * RADIANS_TO_DEGREES, 0, 'd', 4);
+               }
+               else if (obj->hitObject)
+               {
+                       if (shiftDown)
+                       {
+                               return;
+                       }
+
+                       obj->radius[0] = Vector::Magnitude(obj->p[0], point);
+                       QString text = QObject::tr("Radius: %1");
+                       informativeText = text.arg(obj->radius[0], 0, 'd', 4);
+               }
+
+               break;
+       case OTContainer:
+               // This is shitty, but works for now until I can code up something
+               // nicer :-)
+               TranslateObject(obj, delta);
+
+               break;
+       default:
+               break;
+       }
+}
+