]> Shamusworld >> Repos - architektonas/blob - src/drawingview.cpp
Add ability to add Arcs to document.
[architektonas] / src / drawingview.cpp
1 // drawingview.cpp
2 //
3 // Part of the Architektonas Project
4 // (C) 2011 Underground Software
5 // See the README and GPLv3 files for licensing and warranty information
6 //
7 // JLH = James Hammons <jlhamm@acm.org>
8 //
9 // Who  When        What
10 // ---  ----------  -------------------------------------------------------------
11 // JLH  03/22/2011  Created this file
12 // JLH  09/29/2011  Added middle mouse button panning
13 //
14
15 // FIXED:
16 //
17 // - Redo rendering code to *not* use Qt's transform functions, as they are tied
18 //   to a left-handed system and we need a right-handed one. [DONE]
19 //
20 // STILL TO BE DONE:
21 //
22 // - Lots of stuff
23 //
24
25 // Uncomment this for debugging...
26 //#define DEBUG
27 //#define DEBUGFOO                              // Various tool debugging...
28 //#define DEBUGTP                               // Toolpalette debugging...
29
30 #include "drawingview.h"
31
32 #include <stdint.h>
33 #include "geometry.h"
34 #include "global.h"
35 #include "mathconstants.h"
36 #include "painter.h"
37 #include "structs.h"
38 #include "utils.h"
39
40
41 #define BACKGROUND_MAX_SIZE     512
42
43 // Class variable
44 //Container DrawingView::document(Vector(0, 0));
45
46
47 DrawingView::DrawingView(QWidget * parent/*= NULL*/): QWidget(parent),
48         // The value in the settings file will override this.
49         useAntialiasing(true), numSelected(0), numHovered(0), shiftDown(false),
50         ctrlDown(false),
51         gridBackground(BACKGROUND_MAX_SIZE, BACKGROUND_MAX_SIZE),
52         scale(1.0), offsetX(-10), offsetY(-10),// document(Vector(0, 0)),
53         gridPixels(0), collided(false)//, toolAction(NULL)
54 {
55 //      document.isTopLevelContainer = true;
56 //wtf? doesn't work except in c++11???  document = { 0 };
57         setBackgroundRole(QPalette::Base);
58         setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
59
60         Global::gridSpacing = 12.0;             // In base units (inch is default)
61
62 #if 0
63         Line * line = new Line(Vector(5, 5), Vector(50, 40), &document);
64         document.Add(line);
65         document.Add(new Line(Vector(50, 40), Vector(10, 83), &document));
66         document.Add(new Line(Vector(10, 83), Vector(17, 2), &document));
67         document.Add(new Circle(Vector(100, 100), 36, &document));
68         document.Add(new Circle(Vector(50, 150), 49, &document));
69         document.Add(new Arc(Vector(300, 300), 32, PI / 4.0, PI * 1.3, &document)),
70         document.Add(new Arc(Vector(200, 200), 60, PI / 2.0, PI * 1.5, &document));
71 #if 1
72         Dimension * dimension = new Dimension(Vector(0, 0), Vector(0, 0), DTLinear, &document);
73         line->SetDimensionOnLine(dimension);
74         document.Add(dimension);
75 #else
76         // Alternate way to do the above...
77         line->SetDimensionOnLine();
78 #endif
79 #else
80         Line * line = new Line;//(Vector(5, 5), Vector(50, 40), &document);
81         line->p[0] = Vector(5, 5);
82         line->p[1] = Vector(50, 40);
83         line->type = OTLine;
84         line->thickness = 2.0;
85         line->style = LSDash;
86         line->color = 0xFF7F00;
87         document.objects.push_back(line);
88         document.objects.push_back(new Line(Vector(50, 40), Vector(10, 83)));
89         document.objects.push_back(new Line(Vector(10, 83), Vector(17, 2)));
90         document.objects.push_back(new Circle(Vector(100, 100), 36));
91         document.objects.push_back(new Circle(Vector(50, 150), 49));
92         document.objects.push_back(new Arc(Vector(300, 300), 32, PI / 4.0, PI * 1.3)),
93         document.objects.push_back(new Arc(Vector(200, 200), 60, PI / 2.0, PI * 1.5));
94         document.objects.push_back(new Dimension(Vector(50, 40), Vector(5, 5)));
95         document.objects.push_back(new Text(Vector(10, 83), "Here is some awesome text!"));
96 #endif
97
98 /*
99 Here we set the grid size in pixels--12 in this case. Initially, we have our
100 zoom set to make this represent 12 inches at a zoom factor of 25%. (This is
101 arbitrary.) So, to be able to decouple the grid size from the zoom, we need
102 to be able to set the size of the background grid (which we do here at an
103 arbitrary 12 pixels) to anything we want (within reason, of course :-).
104
105 The drawing enforces the grid spacing through the drawing->gridSpacing variable.
106
107         drawing->gridSpacing = 12.0 / Global::zoom;
108
109 Global::zoom is the zoom factor for the drawing, and all mouse clicks are
110 translated to Cartesian coordinates through this. (Initially, Global::zoom is
111 set to 1.0. SCREEN_ZOOM is set to 1.0/4.0.)
112
113 Really, the 100% zoom level can be set at *any* zoom level, it's more of a
114 convenience function than any measure of absolutes. Doing things that way we
115 could rid ourselves of the whole SCREEN_ZOOM parameter and all the attendant
116 shittiness that comes with it.
117
118 However, it seems that SCREEN_ZOOM is used to make text and arrow sizes show up
119 a certain way, which means we should probably create something else in those
120 objects to take its place--like some kind of scale factor. This would seem to
121 imply that certain point sizes actually *do* tie things like fonts to absolute
122 sizes on the screen, but not necessarily because you could have an inch scale
123 with text that is quite small relative to other objects on the screen, which
124 currently you have to zoom in to see (and which blows up the text). Point sizes
125 in an application like this are a bit meaningless; even though an inch is an
126 inch regardless of the zoom level a piece of text can be larger or smaller than
127 this. Maybe this is the case for having a base unit and basing point sizes off
128 of that.
129
130 Here's what's been figured out. Global::zoom is simply the ratio of pixels to
131 base units. What that means is that if you have a 12px grid with a 6" grid size
132 (& base unit of "inches"), Global::zoom becomes 12px / 6" = 2.0 px/in.
133
134 Dimensions now have a "size" parameter to set their absolute size in relation
135 to the base unit. ATM, the arrows are drawn in pixels, but also scaled by
136 Global::zoom *and* size. Same with the dimension text; it's drawn at 10pt and
137 scaled the same way as the arrowheads.
138
139 Need a way to scale line widths as well. :-/ Shouldn't be too difficult, just
140 need a thickness parameter similar to the "size" param for dimensions. (And now
141 we do! :-)
142
143 */
144         SetGridSize(12);        // This is in pixels
145 }
146
147
148 #if 0
149 void DrawingView::SetToolActive(Action * action)
150 {
151         if (action != NULL)
152         {
153                 toolAction = action;
154                 connect(toolAction, SIGNAL(ObjectReady(Object *)), this,
155                         SLOT(AddNewObjectToDocument(Object *)));
156                 connect(toolAction, SIGNAL(NeedRefresh()), this, SLOT(HandleActionUpdate()));
157         }
158 }
159 #endif
160
161
162 void DrawingView::SetGridSize(uint32_t size)
163 {
164         // Sanity check
165         if (size == gridPixels)
166                 return;
167
168         // Recreate the background bitmap
169         gridPixels = size;
170         QPainter pmp(&gridBackground);
171         pmp.fillRect(0, 0, BACKGROUND_MAX_SIZE, BACKGROUND_MAX_SIZE, QColor(240, 240, 240));
172         pmp.setPen(QPen(QColor(210, 210, 255), 2.0, Qt::SolidLine));
173
174         for(int i=0; i<(BACKGROUND_MAX_SIZE-1); i+=gridPixels)
175         {
176                 pmp.drawLine(i, 0, i, BACKGROUND_MAX_SIZE - 1);
177                 pmp.drawLine(0, i, BACKGROUND_MAX_SIZE - 1, i);
178         }
179
180         pmp.end();
181
182         // Set up new BG brush & zoom level (pixels per base unit)
183 //      Painter::zoom = gridPixels / gridSpacing;
184         Global::zoom = gridPixels / Global::gridSpacing;
185         UpdateGridBackground();
186 }
187
188
189 void DrawingView::UpdateGridBackground(void)
190 {
191         // Transform the origin to Qt coordinates
192         Vector pixmapOrigin = Painter::CartesianToQtCoords(Vector());
193         int x = (int)pixmapOrigin.x;
194         int y = (int)pixmapOrigin.y;
195         // Use mod arithmetic to grab the correct swatch of background
196 /*
197 Negative numbers still screw it up... Need to think about what we're
198 trying to do here. The fact that it worked with 72 seems to have been pure luck.
199 It seems the problem is negative numbers: We can't let that happen.
200 When taking away the zero, it pops over 1 px at zero, then goes about 1/2 a
201 grid at x<0.
202
203 The bitmap looks like this:
204
205 +---+---+---+---+---
206 |   |   |   |   |
207 |   |   |   |   |
208 +---+---+---+---+---
209 |   |   |   |   |
210 |   |   |   |   |
211 |   |   |   |   |
212
213 @ x = 1, we want it to look like:
214
215 -+---+---+---+---+---
216  |   |   |   |   |
217  |   |   |   |   |
218 -+---+---+---+---+---
219  |   |   |   |   |
220  |   |   |   |   |
221  |   |   |   |   |
222
223 Which means we need to grab the sample from x = 3. @ x = -1:
224
225 ---+---+---+---+---
226    |   |   |   |
227    |   |   |   |
228 ---+---+---+---+---
229    |   |   |   |
230    |   |   |   |
231    |   |   |   |
232
233 Which means we need to grab the sample from x = 1. Which means we have to take
234 the mirror of the modulus of gridPixels.
235
236 Doing a mod of a negative number is problematic: 1st, the compiler converts the
237 negative number to an unsigned int, then it does the mod. Gets you wrong answers
238 most of the time, unless you use a power of 2. :-P So what we do here is just
239 take the modulus of the negation, which means we don't have to worry about
240 mirroring it later.
241
242 The positive case looks gruesome (and it is) but it boils down to this: We take
243 the modulus of the X coordinate, then mirror it by subtraction from the
244 maximum (in this case, gridPixels). This gives us a number in the range of 1 to
245 gridPixels. But we need the case where the result equalling gridPixels to be
246 zero; so we do another modulus operation on the result to achieve this.
247 */
248         if (x < 0)
249                 x = -x % gridPixels;
250         else
251                 x = (gridPixels - (x % gridPixels)) % gridPixels;
252
253         if (y < 0)
254                 y = -y % gridPixels;
255         else
256                 y = (gridPixels - (y % gridPixels)) % gridPixels;
257
258         // Here we grab a section of the bigger pixmap, so that the background
259         // *looks* like it's scrolling...
260         QPixmap pm = gridBackground.copy(x, y, gridPixels, gridPixels);
261         QPalette pal = palette();
262         pal.setBrush(backgroundRole(), QBrush(pm));
263         setAutoFillBackground(true);
264         setPalette(pal);
265 }
266
267
268 void DrawingView::SetCurrentLayer(int layer)
269 {
270         Global::currentLayer = layer;
271 //printf("DrawingView::CurrentLayer = %i\n", layer);
272 }
273
274
275 QPoint DrawingView::GetAdjustedMousePosition(QMouseEvent * event)
276 {
277         // This is undoing the transform, e.g. going from client coords to local coords.
278         // In essence, the height - y is height + (y * -1), the (y * -1) term doing the
279         // conversion of the y-axis from increasing bottom to top.
280         return QPoint(offsetX + event->x(), offsetY + (size().height() - event->y()));
281 }
282
283
284 QPoint DrawingView::GetAdjustedClientPosition(int x, int y)
285 {
286         // VOODOO ALERT (ON Y COMPONENT!!!!) (eh?)
287         // No voodoo here, it's just grouped wrong to see it. It should be:
288         // -offsetY + (size.height() + (y * -1.0)) <-- this is wrong, offsetY should be positive
289         return QPoint(-offsetX + x, (size().height() - (-offsetY + y)) * +1.0);
290 }
291
292
293 void DrawingView::paintEvent(QPaintEvent * /*event*/)
294 {
295         QPainter qtPainter(this);
296         Painter painter(&qtPainter);
297
298         if (useAntialiasing)
299                 qtPainter.setRenderHint(QPainter::Antialiasing);
300
301         Global::viewportHeight = size().height();
302
303         // Draw coordinate axes
304         painter.SetPen(QPen(Qt::blue, 1.0, Qt::DotLine));
305         painter.DrawLine(0, -16384, 0, 16384);
306         painter.DrawLine(-16384, 0, 16384, 0);
307
308         // Do object rendering...
309         RenderObjects(&painter, document.objects);
310
311         // Do tool rendering, if any...
312         if (Global::tool)
313         {
314                 painter.SetPen(QPen(QColor(200, 100, 0, 255), 1.0, Qt::DashLine));
315                 painter.DrawCrosshair(oldPoint);
316                 ToolDraw(&painter);
317         }
318
319         // Do selection rectangle rendering, if any
320         if (Global::selectionInProgress)
321         {
322                 painter.SetPen(QPen(QColor(255, 127, 0, 255)));
323                 painter.SetBrush(QBrush(QColor(255, 127, 0, 100)));
324                 painter.DrawRect(Global::selection);
325         }
326
327         if (!informativeText.isEmpty())
328                 painter.DrawInformativeText(informativeText);
329 }
330
331
332 //
333 // Renders objects in the passed in vector
334 //
335 void DrawingView::RenderObjects(Painter * painter, std::vector<void *> & v)
336 {
337         std::vector<void *>::iterator i;
338
339         for(i=v.begin(); i!=v.end(); i++)
340         {
341                 Object * obj = (Object *)(*i);
342                 float scaledThickness = Global::scale * obj->thickness;
343
344                 if ((Global::tool == TTRotate) && ctrlDown && obj->selected)
345                 {
346                         painter->SetPen(0x00FF00, 2.0, LSSolid);
347                 }
348                 else
349                 {
350                         painter->SetPen(obj->color, Global::zoom * scaledThickness, obj->style);
351                         painter->SetBrush(obj->color);
352
353                         if (obj->selected || obj->hitObject)
354                                 painter->SetPen(0xFF0000, Global::zoom * scaledThickness, LSDash);
355                 }
356
357                 switch (obj->type)
358                 {
359                 case OTLine:
360                         painter->DrawLine(obj->p[0], obj->p[1]);
361
362                         if (obj->hitPoint[0])
363                                 painter->DrawHandle(obj->p[0]);
364
365                         if (obj->hitPoint[1])
366                                 painter->DrawHandle(obj->p[1]);
367
368                         break;
369                 case OTCircle:
370                         painter->SetBrush(QBrush(Qt::NoBrush));
371                         painter->DrawEllipse(obj->p[0], obj->radius[0], obj->radius[0]);
372
373                         if (obj->hitPoint[0])
374                                 painter->DrawHandle(obj->p[0]);
375
376                         break;
377                 case OTArc:
378                         painter->DrawArc(obj->p[0], obj->radius[0], obj->angle[0], obj->angle[1]);
379                         break;
380                 case OTDimension:
381                 {
382                         Dimension * d = (Dimension *)obj;
383
384                         Vector v(d->p[0], d->p[1]);
385                         double angle = v.Angle();
386                         Vector unit = v.Unit();
387                         Vector linePt1 = d->p[0], linePt2 = d->p[1];
388                         Vector ortho;
389                         double x1, y1, length;
390
391                         if (d->subtype == DTLinearVert)
392                         {
393                                 if ((angle < 0) || (angle > PI))
394                                 {
395                                         x1 = (d->p[0].x > d->p[1].x ? d->p[0].x : d->p[1].x);
396                                         y1 = (d->p[0].y > d->p[1].y ? d->p[0].y : d->p[1].y);
397                                         ortho = Vector(1.0, 0);
398                                         angle = PI3_OVER_2;
399                                 }
400                                 else
401                                 {
402                                         x1 = (d->p[0].x > d->p[1].x ? d->p[1].x : d->p[0].x);
403                                         y1 = (d->p[0].y > d->p[1].y ? d->p[1].y : d->p[0].y);
404                                         ortho = Vector(-1.0, 0);
405                                         angle = PI_OVER_2;
406                                 }
407
408                                 linePt1.x = linePt2.x = x1;
409                                 length = fabs(d->p[0].y - d->p[1].y);
410                         }
411                         else if (d->subtype == DTLinearHorz)
412                         {
413                                 if ((angle < PI_OVER_2) || (angle > PI3_OVER_2))
414                                 {
415                                         x1 = (d->p[0].x > d->p[1].x ? d->p[0].x : d->p[1].x);
416                                         y1 = (d->p[0].y > d->p[1].y ? d->p[0].y : d->p[1].y);
417                                         ortho = Vector(0, 1.0);
418                                         angle = 0;
419                                 }
420                                 else
421                                 {
422                                         x1 = (d->p[0].x > d->p[1].x ? d->p[1].x : d->p[0].x);
423                                         y1 = (d->p[0].y > d->p[1].y ? d->p[1].y : d->p[0].y);
424                                         ortho = Vector(0, -1.0);
425                                         angle = PI;
426                                 }
427
428                                 linePt1.y = linePt2.y = y1;
429                                 length = fabs(d->p[0].x - d->p[1].x);
430                         }
431                         else if (d->subtype == DTLinear)
432                         {
433                                 angle = Vector(linePt1, linePt2).Angle();
434                                 ortho = Vector::Normal(linePt1, linePt2);
435                                 length = v.Magnitude();
436                         }
437
438                         unit = Vector(linePt1, linePt2).Unit();
439
440                         Point p1 = linePt1 + (ortho * 10.0 * scaledThickness);
441                         Point p2 = linePt2 + (ortho * 10.0 * scaledThickness);
442                         Point p3 = linePt1 + (ortho * 16.0 * scaledThickness);
443                         Point p4 = linePt2 + (ortho * 16.0 * scaledThickness);
444                         Point p5 = d->p[0] + (ortho * 4.0 * scaledThickness);
445                         Point p6 = d->p[1] + (ortho * 4.0 * scaledThickness);
446
447                 /*
448                 The numbers hardcoded into here, what are they?
449                 I believe they are pixels.
450                 */
451                         // Draw extension lines (if certain type)
452                         painter->DrawLine(p3, p5);
453                         painter->DrawLine(p4, p6);
454
455                         // Calculate whether or not the arrowheads are too crowded to put inside
456                         // the extension lines. 9.0 is the length of the arrowhead.
457                         double t = Geometry::ParameterOfLineAndPoint(linePt1, linePt2, linePt2 - (unit * 9.0 * scaledThickness));
458                 //printf("Dimension::Draw(): t = %lf\n", t);
459
460                         // On the screen, it's acting like this is actually 58%...
461                         // This is correct, we want it to happen at > 50%
462                         if (t > 0.58)
463                         {
464                                 // Draw main dimension line + arrowheads
465                                 painter->DrawLine(p1, p2);
466                                 painter->DrawArrowhead(p1, p2, scaledThickness);
467                                 painter->DrawArrowhead(p2, p1, scaledThickness);
468                         }
469                         else
470                         {
471                                 // Draw outside arrowheads
472                                 Point p7 = p1 - (unit * 9.0 * scaledThickness);
473                                 Point p8 = p2 + (unit * 9.0 * scaledThickness);
474                                 painter->DrawArrowhead(p1, p7, scaledThickness);
475                                 painter->DrawArrowhead(p2, p8, scaledThickness);
476                                 painter->DrawLine(p1, p1 - (unit * 14.0 * scaledThickness));
477                                 painter->DrawLine(p2, p2 + (unit * 14.0 * scaledThickness));
478                         }
479
480                         // Draw length of dimension line...
481                         painter->SetFont(QFont("Arial", 8.0 * Global::zoom * scaledThickness));
482                         Point ctr = p2 + (Vector(p2, p1) / 2.0);
483
484                 #if 0
485                         QString dimText = QString("%1\"").arg(Vector(endpoint - position).Magnitude());
486                 #else
487                         QString dimText;
488
489                         if (length < 12.0)
490                                 dimText = QString("%1\"").arg(length);
491                         else
492                         {
493                                 double feet = (double)((int)length / 12);
494                                 double inches = length - (feet * 12.0);
495
496                                 if (inches == 0)
497                                         dimText = QString("%1'").arg(feet);
498                                 else
499                                         dimText = QString("%1' %2\"").arg(feet).arg(inches);
500                         }
501                 #endif
502
503                         painter->DrawAngledText(ctr, angle, dimText, scaledThickness);
504
505                         break;
506                 }
507                 case OTText:
508                 {
509                         Text * t = (Text *)obj;
510                         painter->DrawTextObject(t->p[0], t->s.c_str(), scaledThickness);
511                         break;
512                 }
513                 default:
514                         break;
515                 }
516         }
517 }
518
519
520 void DrawingView::AddHoveredToSelection(void)
521 {
522         std::vector<void *>::iterator i;
523
524         for(i=document.objects.begin(); i!=document.objects.end(); i++)
525         {
526                 if (((Object *)(*i))->hovered)
527                         ((Object *)(*i))->selected = true;
528         }
529 }
530
531
532 void DrawingView::GetSelection(std::vector<void *> & v)
533 {
534         v.clear();
535         std::vector<void *>::iterator i;
536
537         for(i=document.objects.begin(); i!=document.objects.end(); i++)
538         {
539                 if (((Object *)(*i))->selected)
540                         v.push_back(*i);
541         }
542 }
543
544
545 void DrawingView::GetHovered(std::vector<void *> & v)
546 {
547         v.clear();
548         std::vector<void *>::iterator i;
549
550         for(i=document.objects.begin(); i!=document.objects.end(); i++)
551         {
552                 if (((Object *)(*i))->hovered)
553 //              {
554 //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"));
555                         v.push_back(*i);
556 //              }
557         }
558 }
559
560
561 void DrawingView::resizeEvent(QResizeEvent * /*event*/)
562 {
563         Global::screenSize = Vector(size().width(), size().height());
564         UpdateGridBackground();
565 }
566
567
568 void DrawingView::ToolHandler(int mode, Point p)
569 {
570         if (Global::tool == TTLine)
571                 LineHandler(mode, p);
572         else if (Global::tool == TTCircle)
573                 CircleHandler(mode, p);
574         else if (Global::tool == TTArc)
575                 ArcHandler(mode, p);
576         else if (Global::tool == TTRotate)
577                 RotateHandler(mode, p);
578 }
579
580
581 void DrawingView::ToolDraw(Painter * painter)
582 {
583         if (Global::tool == TTLine)
584         {
585                 if (Global::toolState == TSNone)
586                 {
587                         painter->DrawHandle(toolPoint[0]);
588                 }
589                 else if ((Global::toolState == TSPoint2) && shiftDown)
590                 {
591                         painter->DrawHandle(toolPoint[1]);
592                 }
593                 else
594                 {
595                         painter->DrawLine(toolPoint[0], toolPoint[1]);
596                         painter->DrawHandle(toolPoint[1]);
597
598                         Vector v(toolPoint[0], toolPoint[1]);
599                         double absAngle = v.Angle() * RADIANS_TO_DEGREES;
600                         double absLength = v.Magnitude();
601                         QString text = tr("Length: %1 in.\n") + QChar(0x2221) + tr(": %2");
602                         informativeText = text.arg(absLength).arg(absAngle);
603                 }
604         }
605         else if (Global::tool == TTCircle)
606         {
607                 if (Global::toolState == TSNone)
608                 {
609                         painter->DrawHandle(toolPoint[0]);
610                 }
611                 else if ((Global::toolState == TSPoint2) && shiftDown)
612                 {
613                         painter->DrawHandle(toolPoint[1]);
614                 }
615                 else
616                 {
617                         double length = Vector::Magnitude(toolPoint[0], toolPoint[1]);
618 //                      painter->DrawLine(toolPoint[0], toolPoint[1]);
619 //                      painter->DrawHandle(toolPoint[1]);
620                         painter->SetBrush(QBrush(Qt::NoBrush));
621                         painter->DrawEllipse(toolPoint[0], length, length);
622                         QString text = tr("Radius: %1 in.");//\n") + QChar(0x2221) + tr(": %2");
623                         informativeText = text.arg(length);//.arg(absAngle);
624                 }
625         }
626         else if (Global::tool == TTArc)
627         {
628                 if (Global::toolState == TSNone)
629                 {
630                         painter->DrawHandle(toolPoint[0]);
631                 }
632                 else if (Global::toolState == TSPoint2)
633                 {
634                         double length = Vector::Magnitude(toolPoint[0], toolPoint[1]);
635                         painter->SetBrush(QBrush(Qt::NoBrush));
636                         painter->DrawEllipse(toolPoint[0], length, length);
637                         painter->DrawLine(toolPoint[0], toolPoint[1]);
638                         painter->DrawHandle(toolPoint[1]);
639                         QString text = tr("Radius: %1 in.");
640                         informativeText = text.arg(length);
641                 }
642                 else if (Global::toolState == TSPoint3)
643                 {
644                         double angle = Vector::Angle(toolPoint[0], toolPoint[2]);
645                         painter->DrawLine(toolPoint[0], toolPoint[2]);
646                         painter->SetBrush(QBrush(Qt::NoBrush));
647                         painter->DrawEllipse(toolPoint[0], toolPoint[1].x, toolPoint[1].x);
648                         painter->DrawHandle(toolPoint[0] + (Vector(cos(angle), sin(angle)) * toolPoint[1].x));
649                         QString text = tr("Angle start: %1") + QChar(0x00B0);
650                         informativeText = text.arg(RADIANS_TO_DEGREES * angle);
651                 }
652                 else
653                 {
654                         double angle = Vector::Angle(toolPoint[0], toolPoint[3]);
655                         double span = angle - toolPoint[2].x;
656
657                         if (span < 0)
658                                 span += PI_TIMES_2;
659
660                         painter->DrawLine(toolPoint[0], toolPoint[3]);
661                         painter->SetBrush(QBrush(Qt::NoBrush));
662                         painter->DrawEllipse(toolPoint[0], toolPoint[1].x, toolPoint[1].x);
663                         painter->SetPen(0xFF00FF, 2.0, LSSolid);
664                         painter->DrawArc(toolPoint[0], toolPoint[1].x, toolPoint[2].x, span);
665                         painter->DrawHandle(toolPoint[0] + (Vector(cos(angle), sin(angle)) * toolPoint[1].x));
666                         QString text = tr("Arc span: %1") + QChar(0x00B0);
667                         informativeText = text.arg(RADIANS_TO_DEGREES * span);
668                 }
669         }
670         else if (Global::tool == TTRotate)
671         {
672                 if ((Global::toolState == TSNone) || (Global::toolState == TSPoint1))
673                         painter->DrawHandle(toolPoint[0]);
674                 else if ((Global::toolState == TSPoint2) && shiftDown)
675                         painter->DrawHandle(toolPoint[1]);
676                 else
677                 {
678                         if (toolPoint[0] == toolPoint[1])
679                                 return;
680                         
681                         painter->DrawLine(toolPoint[0], toolPoint[1]);
682                         // Likely we need a tool container for this... (now we do!)
683 #if 0
684                         if (ctrlDown)
685                         {
686                                 painter->SetPen(0x00FF00, 2.0, LSSolid);
687                                 overrideColor = true;
688                         }
689
690                         RenderObjects(painter, toolObjects);
691                         overrideColor = false;
692 #endif
693
694                         double absAngle = (Vector(toolPoint[1] - toolPoint[0]).Angle()) * RADIANS_TO_DEGREES;
695
696                         QString text = QChar(0x2221) + QObject::tr(": %1");
697                         informativeText = text.arg(absAngle);
698
699                         if (ctrlDown)
700                                 informativeText += " (Copy)";
701
702 //                      painter->DrawInformativeText(text);
703                 }
704         }
705 }
706
707
708 void DrawingView::LineHandler(int mode, Point p)
709 {
710         switch (mode)
711         {
712         case ToolMouseDown:
713                 if (Global::toolState == TSNone)
714                         toolPoint[0] = p;
715                 else
716                         toolPoint[1] = p;
717
718                 break;
719         case ToolMouseMove:
720                 if (Global::toolState == TSNone)
721                         toolPoint[0] = p;
722                 else
723                         toolPoint[1] = p;
724
725                 break;
726         case ToolMouseUp:
727                 if (Global::toolState == TSNone)
728                 {
729                         Global::toolState = TSPoint2;
730                         // Prevent spurious line from drawing...
731                         toolPoint[1] = toolPoint[0];
732                 }
733                 else if ((Global::toolState == TSPoint2) && shiftDown)
734                 {
735                         // Key override is telling us to make a new line, not continue the
736                         // previous one.
737                         toolPoint[0] = toolPoint[1];
738                 }
739                 else
740                 {
741                         Line * l = new Line(toolPoint[0], toolPoint[1]);
742                         document.objects.push_back(l);
743                         toolPoint[0] = toolPoint[1];
744                 }
745         }
746 }
747
748
749 void DrawingView::CircleHandler(int mode, Point p)
750 {
751         switch (mode)
752         {
753         case ToolMouseDown:
754                 if (Global::toolState == TSNone)
755                         toolPoint[0] = p;
756                 else
757                         toolPoint[1] = p;
758
759                 break;
760         case ToolMouseMove:
761                 if (Global::toolState == TSNone)
762                         toolPoint[0] = p;
763                 else
764                         toolPoint[1] = p;
765
766                 break;
767         case ToolMouseUp:
768                 if (Global::toolState == TSNone)
769                 {
770                         Global::toolState = TSPoint2;
771                         // Prevent spurious line from drawing...
772                         toolPoint[1] = toolPoint[0];
773                 }
774                 else if ((Global::toolState == TSPoint2) && shiftDown)
775                 {
776                         // Key override is telling us to make a new line, not continue the
777                         // previous one.
778                         toolPoint[0] = toolPoint[1];
779                 }
780                 else
781                 {
782                         double length = Vector::Magnitude(toolPoint[0], toolPoint[1]);
783                         Circle * c = new Circle(toolPoint[0], length);
784                         document.objects.push_back(c);
785                         toolPoint[0] = toolPoint[1];
786                         Global::toolState = TSNone;
787                 }
788         }
789 }
790
791
792 void DrawingView::ArcHandler(int mode, Point p)
793 {
794         switch (mode)
795         {
796         case ToolMouseDown:
797                 if (Global::toolState == TSNone)
798                         toolPoint[0] = p;
799                 else if (Global::toolState == TSPoint2)
800                         toolPoint[1] = p;
801                 else if (Global::toolState == TSPoint3)
802                         toolPoint[2] = p;
803                 else
804                         toolPoint[3] = p;
805
806                 break;
807         case ToolMouseMove:
808                 if (Global::toolState == TSNone)
809                         toolPoint[0] = p;
810                 else if (Global::toolState == TSPoint2)
811                         toolPoint[1] = p;
812                 else if (Global::toolState == TSPoint3)
813                         toolPoint[2] = p;
814                 else
815                         toolPoint[3] = p;
816
817                 break;
818         case ToolMouseUp:
819                 if (Global::toolState == TSNone)
820                 {
821                         // Prevent spurious line from drawing...
822                         toolPoint[1] = toolPoint[0];
823                         Global::toolState = TSPoint2;
824                 }
825                 else if (Global::toolState == TSPoint2)
826                 {
827                         if (shiftDown)
828                         {
829                                 // Key override is telling us to start circle at new center, not
830                                 // continue the current one.
831                                 toolPoint[0] = toolPoint[1];
832                                 return;
833                         }
834
835                         // Set the radius in toolPoint[1].x
836                         toolPoint[1].x = Vector::Magnitude(toolPoint[0], toolPoint[1]);
837                         Global::toolState = TSPoint3;
838                 }
839                 else if (Global::toolState == TSPoint3)
840                 {
841                         // Set the angle in toolPoint[2].x
842                         toolPoint[2].x = Vector::Angle(toolPoint[0], toolPoint[2]);
843                         Global::toolState = TSPoint4;
844                 }
845                 else
846                 {
847                         double endAngle = Vector::Angle(toolPoint[0], toolPoint[3]);
848                         double span = endAngle - toolPoint[2].x;
849
850                         if (span < 0)
851                                 span += PI_TIMES_2;
852
853                         Arc * arc = new Arc(toolPoint[0], toolPoint[1].x, toolPoint[2].x, span);
854                         document.objects.push_back(arc);
855                         Global::toolState = TSNone;
856                 }
857         }
858 }
859
860
861 void DrawingView::RotateHandler(int mode, Point p)
862 {
863         switch (mode)
864         {
865         case ToolMouseDown:
866                 if (Global::toolState == TSNone)
867                 {
868                         toolPoint[0] = p;
869                         SavePointsFrom(select, toolScratch);
870                         Global::toolState = TSPoint1;
871                 }
872                 else if (Global::toolState == TSPoint1)
873                         toolPoint[0] = p;
874                 else
875                         toolPoint[1] = p;
876
877                 break;
878         case ToolMouseMove:
879                 if ((Global::toolState == TSPoint1) || (Global::toolState == TSNone))
880                         toolPoint[0] = p;
881                 else if (Global::toolState == TSPoint2)
882                 {
883                         toolPoint[1] = p;
884
885                         if (shiftDown)
886                                 return;
887
888                         double angle = Vector(toolPoint[0], toolPoint[1]).Angle();
889                         std::vector<void *>::iterator j = select.begin();
890                         std::vector<Object>::iterator i = toolScratch.begin();
891
892                         for(; i!=toolScratch.end(); i++, j++)
893                         {
894                                 Object obj = *i;
895                                 Point p1 = Geometry::RotatePointAroundPoint(obj.p[0], toolPoint[0], angle);
896                                 Point p2 = Geometry::RotatePointAroundPoint(obj.p[1], toolPoint[0], angle);
897                                 Object * obj2 = (Object *)(*j);
898                                 obj2->p[0] = p1;
899                                 obj2->p[1] = p2;
900
901                                 if (obj.type == OTArc)
902                                 {
903                                         obj2->angle[0] = obj.angle[0] + angle;
904
905                                         if (obj2->angle[0] > PI_TIMES_2)
906                                                 obj2->angle[0] -= PI_TIMES_2;
907                                 }
908                         }
909                 }
910
911                 break;
912         case ToolMouseUp:
913                 if (Global::toolState == TSPoint1)
914                 {
915                         Global::toolState = TSPoint2;
916                         // Prevent spurious line from drawing...
917                         toolPoint[1] = toolPoint[0];
918                 }
919                 else if ((Global::toolState == TSPoint2) && shiftDown)
920                 {
921                         // Key override is telling us to make a new line, not continue the
922                         // previous one.
923                         toolPoint[0] = toolPoint[1];
924                 }
925                 else
926                 {
927                         // Either we're finished with our rotate, or we're stamping a copy.
928                         if (ctrlDown)
929                         {
930                                 // Stamp a copy of the selection at the current rotation & bail
931                                 std::vector<void *> temp;
932                                 CopyObjects(select, temp);
933                                 ClearSelected(temp);
934                                 AddObjectsTo(document.objects, temp);
935                                 RestorePointsTo(select, toolScratch);
936                                 return;
937                         }
938
939                         toolPoint[0] = p;
940                         Global::toolState = TSPoint1;
941                         SavePointsFrom(select, toolScratch);
942                 }
943
944                 break;
945         case ToolKeyDown:
946                 // Reset the selection if shift held down...
947                 if (shiftDown)
948                         RestorePointsTo(select, toolScratch);
949
950                 break;
951         case ToolKeyUp:
952                 // Reset selection when key is let up
953                 if (!shiftDown)
954                         RotateHandler(ToolMouseMove, toolPoint[1]);
955
956                 break;
957         case ToolCleanup:
958                 RestorePointsTo(select, toolScratch);
959         }
960 }
961
962
963 void DrawingView::mousePressEvent(QMouseEvent * event)
964 {
965         if (event->button() == Qt::LeftButton)
966         {
967                 Vector point = Painter::QtToCartesianCoords(Vector(event->x(), event->y()));
968
969                 // Handle tool processing, if any
970                 if (Global::tool)
971                 {
972                         if (Global::snapToGrid)
973                                 point = SnapPointToGrid(point);
974
975                         //Also, may want to figure out if hovering over a snap point on an object,
976                         //snap to grid if not.
977                         // Snap to object point if valid...
978 //                      if (Global::snapPointIsValid)
979 //                              point = Global::snapPoint;
980                         
981                         ToolHandler(ToolMouseDown, point);
982                         return;
983                 }
984
985                 // Clear the selection only if CTRL isn't being held on click
986                 if (!ctrlDown)
987                         ClearSelected(document.objects);
988 //                      ClearSelection();
989
990                 // If any objects are being hovered on click, add them to the selection
991                 // & return
992                 if (numHovered > 0)
993                 {
994                         AddHoveredToSelection();
995                         update();       // needed??
996                         GetHovered(hover);      // prolly needed
997
998                         // Needed for grab & moving objects
999                         // We do it *after*... why? (doesn't seem to confer any advantage...)
1000                         if (Global::snapToGrid)
1001                                 oldPoint = SnapPointToGrid(point);
1002
1003                         return;
1004                 }
1005
1006                 // Didn't hit any object and not using a tool, so do a selection rectangle
1007                 Global::selectionInProgress = true;
1008                 Global::selection.setTopLeft(QPointF(point.x, point.y));
1009                 Global::selection.setBottomRight(QPointF(point.x, point.y));
1010         }
1011         else if (event->button() == Qt::MiddleButton)
1012         {
1013                 scrollDrag = true;
1014                 oldPoint = Vector(event->x(), event->y());
1015                 // Should also change the mouse pointer as well...
1016                 setCursor(Qt::SizeAllCursor);
1017         }
1018 }
1019
1020
1021 void DrawingView::mouseMoveEvent(QMouseEvent * event)
1022 {
1023         Vector point = Painter::QtToCartesianCoords(Vector(event->x(), event->y()));
1024         Global::selection.setBottomRight(QPointF(point.x, point.y));
1025         // Only needs to be done here, as mouse down is always preceded by movement
1026         Global::snapPointIsValid = false;
1027
1028         // Scrolling...
1029         if (event->buttons() & Qt::MiddleButton)
1030         {
1031                 point = Vector(event->x(), event->y());
1032                 // Since we're using Qt coords for scrolling, we have to adjust them here to
1033                 // conform to Cartesian coords, since the origin is using Cartesian. :-)
1034                 Vector delta(oldPoint, point);
1035                 delta /= Global::zoom;
1036                 delta.y = -delta.y;
1037                 Global::origin -= delta;
1038
1039                 UpdateGridBackground();
1040                 update();
1041                 oldPoint = point;
1042                 return;
1043         }
1044
1045         // If we're doing a selection rect, see if any objects are engulfed by it
1046         // (implies left mouse button held down)
1047         if (Global::selectionInProgress)
1048         {
1049                 CheckObjectBounds();
1050                 update();
1051                 return;
1052         }
1053
1054         // Handle object movement (left button down & over an object)
1055         if ((event->buttons() & Qt::LeftButton) && numHovered && !Global::tool)
1056         {
1057                 if (Global::snapToGrid)
1058                         point = SnapPointToGrid(point);
1059
1060                 HandleObjectMovement(point);
1061                 update();
1062                 oldPoint = point;
1063                 return;
1064         }
1065
1066         // Do object hit testing...
1067         bool needUpdate = HitTestObjects(point);
1068
1069         // Do tool handling, if any are active...
1070         if (Global::tool)
1071         {
1072                 if (Global::snapToGrid)
1073                         point = SnapPointToGrid(point);
1074
1075                 ToolHandler(ToolMouseMove, point);
1076         }
1077
1078         // This is used to draw the tool crosshair...
1079         oldPoint = point;
1080
1081         if (needUpdate || Global::tool)
1082                 update();
1083 }
1084
1085
1086 void DrawingView::mouseReleaseEvent(QMouseEvent * event)
1087 {
1088         if (event->button() == Qt::LeftButton)
1089         {
1090 //We need to update especially if nothing collided and the state needs to change. !!! FIX !!!
1091 //could set it up to use the document's update function (assumes that all object updates
1092 //are being reported correctly:
1093 //              if (document.NeedsUpdate())
1094                 // Do an update if collided with at least *one* object in the document
1095 //              if (collided)
1096                         update();
1097
1098                 if (Global::tool)
1099                 {
1100                         Vector point = Painter::QtToCartesianCoords(Vector(event->x(), event->y()));
1101                         ToolHandler(ToolMouseUp, point);
1102                         return;
1103                 }
1104
1105                 if (Global::selectionInProgress)
1106                         Global::selectionInProgress = false;
1107
1108                 informativeText.clear();
1109 // Should we be doing this automagically? Hmm...
1110                 // Clear our vectors
1111                 select.clear();
1112                 hover.clear();
1113
1114                 // Scoop 'em up
1115                 std::vector<void *>::iterator i;
1116
1117                 for(i=document.objects.begin(); i!=document.objects.end(); i++)
1118                 {
1119                         if (((Object *)(*i))->selected)
1120                                 select.push_back(*i);
1121
1122 //hmm, this is no good, too late to do any good :-P
1123 //                      if ((*i)->hovered)
1124 //                              hover.push_back(*i);
1125                 }
1126         }
1127         else if (event->button() == Qt::MiddleButton)
1128         {
1129                 scrollDrag = false;
1130                 setCursor(Qt::ArrowCursor);
1131         }
1132 }
1133
1134
1135 void DrawingView::wheelEvent(QWheelEvent * event)
1136 {
1137         double zoomFactor = 1.25;
1138         QSize sizeWin = size();
1139         Vector center(sizeWin.width() / 2.0, sizeWin.height() / 2.0);
1140         center = Painter::QtToCartesianCoords(center);
1141
1142         // This is not centering for some reason. Need to figure out why. :-/
1143         if (event->delta() > 0)
1144         {
1145                 Vector newOrigin = center - ((center - Global::origin) / zoomFactor);
1146                 Global::origin = newOrigin;
1147                 Global::zoom *= zoomFactor;
1148         }
1149         else
1150         {
1151                 Vector newOrigin = center + ((-center + Global::origin) * zoomFactor);
1152                 Global::origin = newOrigin;
1153                 Global::zoom /= zoomFactor;
1154         }
1155
1156 //      Global::gridSpacing = gridPixels / Painter::zoom;
1157 //      UpdateGridBackground();
1158         SetGridSize(Global::gridSpacing * Global::zoom);
1159         update();
1160 //      zoomIndicator->setText(QString("Grid: %1\", BU: Inch").arg(Global::gridSpacing));
1161 }
1162
1163
1164 void DrawingView::keyPressEvent(QKeyEvent * event)
1165 {
1166         bool oldShift = shiftDown;
1167         bool oldCtrl = ctrlDown;
1168
1169         if (event->key() == Qt::Key_Shift)
1170                 shiftDown = true;
1171         else if (event->key() == Qt::Key_Control)
1172                 ctrlDown = true;
1173
1174         if ((oldShift != shiftDown) || (oldCtrl != ctrlDown))
1175         {
1176                 if (Global::tool)
1177                         ToolHandler(ToolKeyDown, Point(0, 0));
1178
1179                 update();
1180         }
1181 }
1182
1183
1184 void DrawingView::keyReleaseEvent(QKeyEvent * event)
1185 {
1186         bool oldShift = shiftDown;
1187         bool oldCtrl = ctrlDown;
1188
1189         if (event->key() == Qt::Key_Shift)
1190                 shiftDown = false;
1191         else if (event->key() == Qt::Key_Control)
1192                 ctrlDown = false;
1193
1194         if ((oldShift != shiftDown) || (oldCtrl != ctrlDown))
1195         {
1196                 if (Global::tool)
1197                         ToolHandler(ToolKeyUp, Point(0, 0));
1198
1199                 update();
1200         }
1201 }
1202
1203
1204 //
1205 // This looks strange, but it's really quite simple: We want a point that's
1206 // more than half-way to the next grid point to snap there while conversely we
1207 // want a point that's less than half-way to to the next grid point then snap
1208 // to the one before it. So we add half of the grid spacing to the point, then
1209 // divide by it so that we can remove the fractional part, then multiply it
1210 // back to get back to the correct answer.
1211 //
1212 Point DrawingView::SnapPointToGrid(Point point)
1213 {
1214         point += Global::gridSpacing / 2.0;             // *This* adds to Z!!!
1215         point /= Global::gridSpacing;
1216         point.x = floor(point.x);//need to fix this for negative numbers...
1217         point.y = floor(point.y);
1218         point.z = 0;                                    // Make *sure* Z doesn't go anywhere!!!
1219         point *= Global::gridSpacing;
1220         return point;
1221 }
1222
1223
1224 void DrawingView::CheckObjectBounds(void)
1225 {
1226         std::vector<void *>::iterator i;
1227         numSelected = 0;
1228
1229         for(i=document.objects.begin(); i!=document.objects.end(); i++)
1230         {
1231                 Object * obj = (Object *)(*i);
1232                 obj->selected = false;
1233
1234                 switch (obj->type)
1235                 {
1236                 case OTLine:
1237                 {
1238                         Line * l = (Line *)obj;
1239
1240                         if (Global::selection.contains(l->p[0].x, l->p[0].y) && Global::selection.contains(l->p[1].x, l->p[1].y))
1241                                 l->selected = true;
1242
1243                         break;
1244                 }
1245                 case OTCircle:
1246                 {
1247                         Circle * c = (Circle *)obj;
1248
1249                         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]))
1250                                 c->selected = true;
1251
1252                         break;
1253                 }
1254                 case OTArc:
1255                 {
1256                         Arc * a = (Arc *)obj;
1257
1258                         double start = a->angle[0];
1259                         double end = start + a->angle[1];
1260                         QPointF p1(cos(start), sin(start));
1261                         QPointF p2(cos(end), sin(end));
1262                         QRectF bounds(p1, p2);
1263
1264 #if 1
1265                         // Swap X/Y coordinates if they're backwards...
1266                         if (bounds.left() > bounds.right())
1267                         {
1268                                 double temp = bounds.left();
1269                                 bounds.setLeft(bounds.right());
1270                                 bounds.setRight(temp);
1271                         }
1272
1273                         if (bounds.bottom() > bounds.top())
1274                         {
1275                                 double temp = bounds.bottom();
1276                                 bounds.setBottom(bounds.top());
1277                                 bounds.setTop(temp);
1278                         }
1279 #else
1280                         // Doesn't work as advertised! For shame!
1281                         bounds = bounds.normalized();
1282 #endif
1283
1284                         // If the end of the arc is before the beginning, add 360 degrees to it
1285                         if (end < start)
1286                                 end += 2.0 * PI;
1287
1288                         // Adjust the bounds depending on which axes are crossed
1289                         if ((start < PI_OVER_2) && (end > PI_OVER_2))
1290                                 bounds.setTop(1.0);
1291
1292                         if ((start < PI) && (end > PI))
1293                                 bounds.setLeft(-1.0);
1294
1295                         if ((start < (PI + PI_OVER_2)) && (end > (PI + PI_OVER_2)))
1296                                 bounds.setBottom(-1.0);
1297
1298                         if ((start < (2.0 * PI)) && (end > (2.0 * PI)))
1299                                 bounds.setRight(1.0);
1300
1301                         if ((start < ((2.0 * PI) + PI_OVER_2)) && (end > ((2.0 * PI) + PI_OVER_2)))
1302                                 bounds.setTop(1.0);
1303
1304                         if ((start < (3.0 * PI)) && (end > (3.0 * PI)))
1305                                 bounds.setLeft(-1.0);
1306
1307                         if ((start < ((3.0 * PI) + PI_OVER_2)) && (end > ((3.0 * PI) + PI_OVER_2)))
1308                                 bounds.setBottom(-1.0);
1309
1310                         bounds.setTopLeft(QPointF(bounds.left() * a->radius[0], bounds.top() * a->radius[0]));
1311                         bounds.setBottomRight(QPointF(bounds.right() * a->radius[0], bounds.bottom() * a->radius[0]));
1312                         bounds.translate(a->p[0].x, a->p[0].y);
1313
1314                         if (Global::selection.contains(bounds))
1315                                 a->selected = true;
1316
1317                         break;
1318                 }
1319                 default:
1320                         break;
1321                 }
1322
1323                 if (obj->selected)
1324                         numSelected++;
1325         }
1326 }
1327
1328
1329 bool DrawingView::HitTestObjects(Point point)
1330 {
1331         std::vector<void *>::iterator i;
1332         numHovered = 0;
1333         bool needUpdate = false;
1334
1335         for(i=document.objects.begin(); i!=document.objects.end(); i++)
1336         {
1337                 Object * obj = (Object *)(*i);
1338
1339                 switch (obj->type)
1340                 {
1341                 case OTLine:
1342                 {
1343                         bool oldHP0 = obj->hitPoint[0], oldHP1 = obj->hitPoint[1], oldHO = obj->hitObject;
1344                         obj->hitPoint[0] = obj->hitPoint[1] = obj->hitObject = false;
1345                         Vector lineSegment = obj->p[1] - obj->p[0];
1346                         Vector v1 = point - obj->p[0];
1347                         Vector v2 = point - obj->p[1];
1348                         double t = Geometry::ParameterOfLineAndPoint(obj->p[0], obj->p[1], point);
1349                         double distance;
1350
1351                         if (t < 0.0)
1352                                 distance = v1.Magnitude();
1353                         else if (t > 1.0)
1354                                 distance = v2.Magnitude();
1355                         else
1356                                 // distance = ?Det?(ls, v1) / |ls|
1357                                 distance = fabs((lineSegment.x * v1.y - v1.x * lineSegment.y)
1358                                         / lineSegment.Magnitude());
1359
1360                         if ((v1.Magnitude() * Global::zoom) < 8.0)
1361                                 obj->hitPoint[0] = true;
1362                         else if ((v2.Magnitude() * Global::zoom) < 8.0)
1363                                 obj->hitPoint[1] = true;
1364                         else if ((distance * Global::zoom) < 5.0)
1365                                 obj->hitObject = true;
1366
1367                         obj->hovered = (obj->hitPoint[0] || obj->hitPoint[1] || obj->hitObject ? true : false);
1368
1369                         if ((oldHP0 != obj->hitPoint[0]) || (oldHP1 != obj->hitPoint[1]) || (oldHO != obj->hitObject))
1370                                 needUpdate = true;
1371
1372                         break;
1373                 }
1374                 case OTCircle:
1375                 {
1376                         bool oldHP = obj->hitPoint[0], oldHO = obj->hitObject;
1377                         obj->hitPoint[0] = obj->hitObject = false;
1378                         double length = Vector::Magnitude(obj->p[0], point);
1379
1380                         if ((length * Global::zoom) < 8.0)
1381                                 obj->hitPoint[0] = true;
1382                         else if ((fabs(length - obj->radius[0]) * Global::zoom) < 2.0)
1383                                 obj->hitObject = true;
1384
1385                         obj->hovered = (obj->hitPoint[0] || obj->hitObject ? true : false);
1386
1387                         if ((oldHP != obj->hitPoint[0]) || (oldHO != obj->hitObject))
1388                                 needUpdate = true;
1389
1390                         break;
1391                 }
1392                 case OTArc:
1393                 {
1394                         bool oldHP = obj->hitPoint[0], oldHO = obj->hitObject;
1395                         obj->hitPoint[0] = obj->hitObject = false;
1396                         double length = Vector::Magnitude(obj->p[0], point);
1397                         double angle = Vector::Angle(obj->p[0], point);
1398
1399                         // Make sure we get the angle in the correct spot
1400                         if (angle < obj->angle[0])
1401                                 angle += PI_TIMES_2;
1402
1403                         // Get the span that we're pointing at...
1404                         double span = angle - obj->angle[0];
1405
1406                         // N.B.: Still need to hit test the arc start & arc span handles...
1407
1408                         if ((length * Global::zoom) < 8.0)
1409                                 obj->hitPoint[0] = true;
1410                         else if (((fabs(length - obj->radius[0]) * Global::zoom) < 2.0) && (span < obj->angle[1]))
1411                                 obj->hitObject = true;
1412
1413                         obj->hovered = (obj->hitPoint[0] || obj->hitObject ? true : false);
1414
1415                         if ((oldHP != obj->hitPoint[0]) || (oldHO != obj->hitObject))
1416                                 needUpdate = true;
1417
1418                         break;
1419                 }
1420                 default:
1421                         break;
1422                 }
1423
1424                 if (obj->hovered)
1425 //              {
1426                         numHovered++;
1427 //printf("MouseMove: OBJECT HOVERED (numHovered = %i)\n", numHovered);
1428 //              }
1429         }
1430
1431         return needUpdate;
1432 }
1433
1434
1435 void DrawingView::HandleObjectMovement(Point point)
1436 {
1437         Point delta = point - oldPoint;
1438 //printf("HOM: old = (%f,%f), new = (%f, %f), delta = (%f, %f)\n", oldPoint.x, oldPoint.y, point.x, point.y, delta.x, delta.y);
1439         Object * obj = (Object *)hover[0];
1440 //printf("Object type = %i (size=%i), ", obj->type, hover.size());
1441 //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"));
1442
1443         switch (obj->type)
1444         {
1445         case OTLine:
1446                 if (obj->hitPoint[0])
1447                         obj->p[0] = point;
1448                 else if (obj->hitPoint[1])
1449                         obj->p[1] = point;
1450                 else if (obj->hitObject)
1451                 {
1452                         obj->p[0] += delta;
1453                         obj->p[1] += delta;
1454                 }
1455
1456                 break;
1457         case OTCircle:
1458                 if (obj->hitPoint[0])
1459                         obj->p[0] = point;
1460                 else if (obj->hitObject)
1461                 {
1462 //this doesn't work. we need to save this on mouse down for this to work correctly!
1463 //                      double oldRadius = obj->radius[0];
1464                         obj->radius[0] = Vector::Magnitude(obj->p[0], point);
1465
1466                         QString text = QObject::tr("Radius: %1");//\nScale: %2%");
1467                         informativeText = text.arg(obj->radius[0], 0, 'd', 4);//.arg(obj->radius[0] / oldRadius * 100.0, 0, 'd', 0);
1468                 }
1469
1470                 break;
1471         default:
1472                 break;
1473         }
1474 }
1475
1476
1477
1478 #if 0
1479         // This returns true if we've moved over an object...
1480         if (document.PointerMoved(point)) // <-- This
1481         // This is where the object would do automagic dragging & shit. Since we don't
1482         // do that anymore, we need a strategy to handle it.
1483         {
1484
1485 /*
1486 Now objects handle mouse move snapping as well. The code below mainly works only
1487 for tools; we need to fix it so that objects work as well...
1488
1489 There's a problem with the object point snapping in that it's dependent on the
1490 order of the objects in the document. Most likely this is because it counts the
1491 selected object last and thus fucks up the algorithm. Need to fix this...
1492
1493
1494 */
1495                 // Do object snapping here. Grid snapping on mouse down is done in the
1496                 // objects themselves, only because we have to hit test the raw point,
1497                 // not the snapped point. There has to be a better way...!
1498                 if (document.penultimateObjectHovered)
1499                 {
1500                         // Two objects are hovered, see if we have an intersection point
1501                         if ((document.lastObjectHovered->type == OTLine) && (document.penultimateObjectHovered->type == OTLine))
1502                         {
1503                                 double t;
1504                                 int n = Geometry::Intersects((Line *)document.lastObjectHovered, (Line *)document.penultimateObjectHovered, &t);
1505
1506                                 if (n == 1)
1507                                 {
1508                                         Global::snapPoint = document.lastObjectHovered->GetPointAtParameter(t);
1509                                         Global::snapPointIsValid = true;
1510                                 }
1511                         }
1512                         else if ((document.lastObjectHovered->type == OTCircle) && (document.penultimateObjectHovered->type == OTCircle))
1513                         {
1514                                 Point p1, p2;
1515                                 int n = Geometry::Intersects((Circle *)document.lastObjectHovered, (Circle *)document.penultimateObjectHovered, 0, 0, 0, 0, &p1, &p2);
1516
1517                                 if (n == 1)
1518                                 {
1519                                         Global::snapPoint = p1;
1520                                         Global::snapPointIsValid = true;
1521                                 }
1522                                 else if (n == 2)
1523                                 {
1524                                         double d1 = Vector(point, p1).Magnitude();
1525                                         double d2 = Vector(point, p2).Magnitude();
1526
1527                                         if (d1 < d2)
1528                                                 Global::snapPoint = p1;
1529                                         else
1530                                                 Global::snapPoint = p2;
1531
1532                                         Global::snapPointIsValid = true;
1533                                 }
1534                         }
1535                 }
1536 //              else
1537 //              {
1538                         // Otherwise, it was a single object hovered...
1539 //              }
1540         }
1541
1542         if (toolAction)
1543         {
1544                 if (Global::snapToGrid)
1545                         point = Global::SnapPointToGrid(point);
1546
1547                 // We always snap to object points, and they take precendence over
1548                 // grid points...
1549                 if (Global::snapPointIsValid)
1550                         point = Global::snapPoint;
1551
1552                 toolAction->MouseMoved(point);
1553         }
1554 #else
1555 #endif
1556