]> Shamusworld >> Repos - architektonas/blob - src/drawingview.cpp
Miscellaneous fixes/updates:
[architektonas] / src / drawingview.cpp
1 //
2 // drawingview.cpp
3 //
4 // Part of the Architektonas Project
5 // (C) 2011-2020 Underground Software
6 // See the README and GPLv3 files for licensing and warranty information
7 //
8 // JLH = James Hammons <jlhamm@acm.org>
9 //
10 // Who  When        What
11 // ---  ----------  ------------------------------------------------------------
12 // JLH  03/22/2011  Created this file
13 // JLH  09/29/2011  Added middle mouse button panning
14 //
15
16 // FIXED:
17 //
18 // - Redo rendering code to *not* use Qt's transform functions, as they are tied
19 //   to a left-handed system and we need a right-handed one. [DONE]
20 // - Fixed length tool doesn't work on lines [DONE]
21 //
22 // STILL TO BE DONE:
23 //
24 // - Lots of stuff
25 // - Layer locking (hiding works)
26 // - Fixed angle tool doesn't work on lines
27 // - Make it so "dirty" flag reflects drawing state
28 //
29
30 // Uncomment this for debugging...
31 //#define DEBUG
32 //#define DEBUGFOO                              // Various tool debugging...
33 //#define DEBUGTP                               // Toolpalette debugging...
34
35 #include "drawingview.h"
36
37 #include <stdint.h>
38 #include "geometry.h"
39 #include "global.h"
40 #include "mathconstants.h"
41 #include "painter.h"
42 #include "penwidget.h"
43 #include "structs.h"
44 #include "utils.h"
45
46 #define BACKGROUND_MAX_SIZE     512
47
48 DrawingView::DrawingView(QWidget * parent/*= NULL*/): QWidget(parent),
49         // The value in the settings file will override this.
50         useAntialiasing(true), numHovered(0), shiftDown(false),
51         ctrlDown(false), altDown(false),
52         gridBackground(BACKGROUND_MAX_SIZE, BACKGROUND_MAX_SIZE),
53         scale(1.0), offsetX(-10), offsetY(-10), supressSelected(false),
54         document(true),
55         gridPixels(0), collided(false), scrollDrag(false), hoverPointValid(false),
56         hoveringIntersection(false), dragged(NULL), draggingObject(false),
57         angleSnap(false), dirty(false)
58 {
59 //wtf? doesn't work except in c++11???  document = { 0 };
60         setBackgroundRole(QPalette::Base);
61         setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
62
63         curMarker = QCursor(QPixmap(":/res/cursor-marker.png"), 1, 18);
64         curDropper = QCursor(QPixmap(":/res/cursor-dropper.png"), 1, 20);
65
66         Global::gridSpacing = 12.0;             // In base units (inch is default)
67
68         Line * line = new Line(Vector(5, 5), Vector(50, 40), 2.0, 0xFF7F00, LSDash);
69         document.Add(line);
70         document.Add(new Line(Vector(50, 40), Vector(10, 83)));
71         document.Add(new Line(Vector(10, 83), Vector(17, 2)));
72         document.Add(new Circle(Vector(100, 100), 36));
73         document.Add(new Circle(Vector(50, 150), 49));
74         document.Add(new Arc(Vector(300, 300), 32, TAU / 8.0, TAU * 0.65)),
75         document.Add(new Arc(Vector(200, 200), 60, TAU / 4.0, TAU * 0.75));
76         document.Add(new Text(Vector(10, 83), "Here is some awesome text!"));
77
78         AddDimensionTo(line);
79
80 /*
81 Here we set the grid size in pixels--12 in this case. Initially, we have our
82 zoom set to make this represent 12 inches at a zoom factor of 25%. (This is
83 arbitrary.) So, to be able to decouple the grid size from the zoom, we need
84 to be able to set the size of the background grid (which we do here at an
85 arbitrary 12 pixels) to anything we want (within reason, of course :-).
86
87 The drawing enforces the grid spacing through the drawing->gridSpacing variable.
88
89         drawing->gridSpacing = 12.0 / Global::zoom;
90
91 Global::zoom is the zoom factor for the drawing, and all mouse clicks are
92 translated to Cartesian coordinates through this. (Initially, Global::zoom is
93 set to 1.0. SCREEN_ZOOM is set to 1.0/4.0.)
94
95 Really, the 100% zoom level can be set at *any* zoom level, it's more of a
96 convenience function than any measure of absolutes. Doing things that way we
97 could rid ourselves of the whole SCREEN_ZOOM parameter and all the attendant
98 shittiness that comes with it.
99
100 However, it seems that SCREEN_ZOOM is used to make text and arrow sizes show up
101 a certain way, which means we should probably create something else in those
102 objects to take its place--like some kind of scale factor. This would seem to
103 imply that certain point sizes actually *do* tie things like fonts to absolute
104 sizes on the screen, but not necessarily because you could have an inch scale
105 with text that is quite small relative to other objects on the screen, which
106 currently you have to zoom in to see (and which blows up the text). Point sizes
107 in an application like this are a bit meaningless; even though an inch is an
108 inch regardless of the zoom level a piece of text can be larger or smaller than
109 this. Maybe this is the case for having a base unit and basing point sizes off
110 of that.
111
112 Here's what's been figured out. Global::zoom is simply the ratio of pixels to
113 base units. What that means is that if you have a 12px grid with a 6" grid size
114 (& base unit of "inches"), Global::zoom becomes 12px / 6" = 2.0 px/in.
115
116 Dimensions now have a "size" parameter to set their absolute size in relation
117 to the base unit. ATM, the arrows are drawn in pixels, but also scaled by
118 Global::zoom *and* size. Same with the dimension text; it's drawn at 10pt and
119 scaled the same way as the arrowheads.
120
121 Need a way to scale line widths as well. :-/ Shouldn't be too difficult, just
122 need a thickness parameter similar to the "size" param for dimensions. (And now
123 we do! :-)
124 */
125 }
126
127 void DrawingView::DrawBackground(Painter * painter)
128 {
129         Point ul = Painter::QtToCartesianCoords(Vector(0, 0));
130         Point br = Painter::QtToCartesianCoords(Vector(Global::screenSize.x, Global::screenSize.y));
131
132         painter->SetBrush(0xF0F0F0);
133         painter->SetPen(0xF0F0F0, 1, 1);
134         painter->DrawRect(QRectF(QPointF(ul.x, ul.y), QPointF(br.x, br.y)));
135
136         double spacing = Global::gridSpacing;
137
138         if (spacing < 1.0)
139                 spacing = 1.0;
140
141         double leftx = floor(ul.x / spacing) * spacing;
142         double bottomy = floor(br.y / spacing) * spacing;
143
144         double w = (br.x - ul.x) + Global::gridSpacing + 1.0;
145         double h = (ul.y - br.y) + Global::gridSpacing + 1.0;
146
147         Vector start(leftx, bottomy), size(w, h);
148
149         if (Global::gridSpacing <= 0.015625)
150                 DrawSubGrid(painter, 0xFFD2D2, 0.015625, start, size);
151
152         if (Global::gridSpacing <= 0.03125)
153                 DrawSubGrid(painter, 0xFFD2D2, 0.03125, start, size);
154
155         if (Global::gridSpacing <= 0.0625)
156                 DrawSubGrid(painter, 0xB8ECFF, 0.0625, start, size);
157
158         if (Global::gridSpacing <= 0.125)
159                 DrawSubGrid(painter, 0xB8ECFF, 0.125, start, size);
160
161         if (Global::gridSpacing <= 0.25)
162                 DrawSubGrid(painter, 0xDBDBFF, 0.25, start, size);
163
164         if (Global::gridSpacing <= 0.5)
165                 DrawSubGrid(painter, 0xDBDBFF, 0.5, start, size);
166
167         painter->SetPen(QPen(QColor(0xD2, 0xD2, 0xFF), 2.0, Qt::SolidLine));
168
169         for(double i=0; i<=w; i+=spacing)
170                 painter->DrawVLine(leftx + i);
171
172         for(double i=0; i<=h; i+=spacing)
173                 painter->DrawHLine(bottomy + i);
174 }
175
176 void DrawingView::DrawSubGrid(Painter * painter, uint32_t color, double step, Vector start, Vector size)
177 {
178         painter->SetPen(color, 1, 1);
179
180         for(double i=-step; i<=size.x; i+=step*2.0)
181                 painter->DrawVLine(start.x + i);
182
183         for(double i=-step; i<=size.y; i+=step*2.0)
184                 painter->DrawHLine(start.y + i);
185 }
186
187 //
188 // Basically, we just make a single pass through the Container. If the layer #
189 // is less than the layer # being deleted, then do nothing. If the layer # is
190 // equal to the layer # being deleted, then delete the object. If the layer #
191 // is greater than the layer # being deleted, then set the layer # to its layer
192 // # - 1.
193 //
194 void DrawingView::DeleteCurrentLayer(int layer)
195 {
196 //printf("DrawingView::DeleteCurrentLayer(): currentLayer = %i\n", layer);
197         VPVectorIter i = document.objects.begin();
198
199         while (i != document.objects.end())
200         {
201                 Object * obj = (Object *)(*i);
202
203                 if (obj->layer < layer)
204                         i++;
205                 else if (obj->layer == layer)
206                 {
207                         document.objects.erase(i);
208                         delete obj;
209                 }
210                 else
211                 {
212                         obj->layer--;
213                         i++;
214                 }
215         }
216
217         // We've just done a destructive action, so update the screen!
218         update();
219 }
220
221 void DrawingView::HandleLayerToggle(void)
222 {
223         // A layer's visibility was toggled, so update the screen...
224         update();
225 }
226
227 //
228 // A layer was moved up or down in the layer list, so we have to swap the
229 // document's object's layer numbers in the layers that were swapped.
230 //
231 void DrawingView::HandleLayerSwap(int layer1, int layer2)
232 {
233 //printf("DrawingView: Swapping layers %i and %i.\n", layer1, layer2);
234         HandleLayerSwap(layer1, layer2, document.objects);
235 }
236
237 /*
238 We can roll this into the main one above, by having the LayerWidget's emit() call sending NULL for the VPVector, which we can test for and set to document.objects to grab the top layer.  Or, keep it a top level call and a recursive call.  Which is worse?  :-P
239 */
240 void DrawingView::HandleLayerSwap(int layer1, int layer2, VPVector & v)
241 {
242         for(VPVectorIter i=v.begin(); i!=v.end(); i++)
243         {
244                 Object * obj = (Object *)(*i);
245
246                 if (obj->layer == layer1)
247                         obj->layer = layer2;
248                 else if (obj->layer == layer2)
249                         obj->layer = layer1;
250
251                 if (obj->type == OTContainer)
252                         HandleLayerSwap(layer1, layer2, ((Container *)obj)->objects);
253         }
254 }
255
256 void DrawingView::HandlePenWidth(float width)
257 {
258         for(VPVectorIter i=select.begin(); i!=select.end(); i++)
259         {
260                 Object * obj = (Object *)(*i);
261                 obj->thickness = width;
262         }
263
264         supressSelected = true;
265         update();
266 }
267
268 void DrawingView::HandlePenStyle(int style)
269 {
270         for(VPVectorIter i=select.begin(); i!=select.end(); i++)
271         {
272                 Object * obj = (Object *)(*i);
273                 obj->style = style;
274         }
275
276         supressSelected = true;
277         update();
278 }
279
280 void DrawingView::HandlePenColor(uint32_t color)
281 {
282         for(VPVectorIter i=select.begin(); i!=select.end(); i++)
283         {
284                 Object * obj = (Object *)(*i);
285                 obj->color = color;
286         }
287
288         supressSelected = true;
289         update();
290 }
291
292 void DrawingView::HandlePenStamp(QAction * action)
293 {
294         PenWidget * pw = (PenWidget *)action->parentWidget();
295         pw->dropperAction->setChecked(false);
296         Global::penDropper = false;
297         Global::penStamp = action->isChecked();
298
299         if (Global::penStamp)
300                 setCursor(curMarker);
301         else
302                 setCursor(Qt::ArrowCursor);
303
304         if (Global::penStamp == false)
305                 ClearSelected(document.objects);
306
307         update();
308 }
309
310 void DrawingView::HandlePenDropper(QAction * action)
311 {
312         PenWidget * pw = (PenWidget *)action->parentWidget();
313         pw->stampAction->setChecked(false);
314         Global::penStamp = false;
315         Global::penDropper = action->isChecked();
316
317         if (Global::penDropper)
318                 setCursor(curDropper);
319         else
320                 setCursor(Qt::ArrowCursor);
321
322         update();
323 }
324
325 QPoint DrawingView::GetAdjustedMousePosition(QMouseEvent * event)
326 {
327         // This is undoing the transform, e.g. going from client coords to local
328         // coords. In essence, the height - y is height + (y * -1), the (y * -1)
329         // term doing the conversion of the y-axis from increasing bottom to top.
330         return QPoint(offsetX + event->x(), offsetY + (size().height() - event->y()));
331 }
332
333 QPoint DrawingView::GetAdjustedClientPosition(int x, int y)
334 {
335         // VOODOO ALERT (ON Y COMPONENT!!!!) (eh?)
336         // No voodoo here, it's just grouped wrong to see it. It should be:
337         // -offsetY + (size.height() + (y * -1.0)) <-- this is wrong, offsetY should be positive [why? we use -offsetX after all]
338         return QPoint(-offsetX + x, (size().height() - (-offsetY + y)) * +1.0);
339 }
340
341 void DrawingView::focusOutEvent(QFocusEvent * /*event*/)
342 {
343 //      printf("DrawingView::focusOutEvent()...\n");
344         // Make sure all modkeys being held are marked as released when the app
345         // loses focus (N.B.: This only works because the app sets the focus policy
346         // of this object to something other than Qt::NoFocus)
347         shiftDown = ctrlDown = altDown = false;
348         scrollDrag = false;
349         setCursor(Qt::ArrowCursor);
350 }
351
352 void DrawingView::focusInEvent(QFocusEvent * /*event*/)
353 {
354         if (Global::penStamp)
355                 setCursor(curMarker);
356         else if (Global::penDropper)
357                 setCursor(curDropper);
358 //FocusOut already set this...
359 //      else
360 //              setCursor(Qt::ArrowCursor);
361 }
362
363 void DrawingView::paintEvent(QPaintEvent * /*event*/)
364 {
365         QPainter qtPainter(this);
366         Painter painter(&qtPainter);
367
368         if (useAntialiasing)
369                 qtPainter.setRenderHint(QPainter::Antialiasing);
370
371         Global::viewportHeight = size().height();
372
373         DrawBackground(&painter);
374
375         // Draw coordinate axes
376         painter.SetPen(QPen(Qt::blue, 1.0, Qt::DotLine));
377         painter.DrawLine(0, -16384, 0, 16384);
378         painter.DrawLine(-16384, 0, 16384, 0);
379
380         // Do object rendering...
381         for(int i=0; i<Global::numLayers; i++)
382         {
383                 if (Global::layerHidden[i] == false)
384                         RenderObjects(&painter, document.objects, i);
385         }
386
387         // Do tool rendering, if any...
388         if (Global::tool)
389         {
390                 painter.SetPen(QPen(QColor(200, 100, 0, 255), 1.0, Qt::DashLine));
391                 painter.DrawCrosshair(oldPoint);
392                 ToolDraw(&painter);
393         }
394
395         // Do selection rectangle rendering, if any
396         if (Global::selectionInProgress)
397         {
398                 painter.SetPen(QPen(QColor(255, 127, 0, 255)));
399                 painter.SetBrush(QBrush(QColor(255, 127, 0, 100)));
400                 painter.DrawRect(Global::selection);
401         }
402
403         if (hoveringIntersection)
404                 painter.DrawHandle(intersectionPoint);
405
406         if (hoverPointValid)
407                 painter.DrawHandle(hoverPoint);
408
409         if (!informativeText.isEmpty())
410                 painter.DrawInformativeText(informativeText);
411 }
412
413 //
414 // Renders objects in the passed in vector
415 //
416 /*
417 N.B.: Since we have "hoverPointValid" drawing regular object handles above,
418       we can probably do away with a lot of them that are being done down below.
419       !!! FIX !!!
420       [Well, it seems to work OK *except* when you move one of the points, then you get to see nothing. Is it worth fixing there to get rid of problems here? Have to investigate...]
421 */
422 void DrawingView::RenderObjects(Painter * painter, VPVector & v, int layer, bool ignoreLayer/*= false*/)
423 {
424         for(VPVectorIter i=v.begin(); i!=v.end(); i++)
425         {
426                 Object * obj = (Object *)(*i);
427                 float scaledThickness = Global::scale * obj->thickness;
428
429                 // If the object isn't on the current layer being drawn, skip it
430                 if (!ignoreLayer && (obj->layer != layer))
431                         continue;
432
433                 if ((Global::tool == TTRotate) && ctrlDown && obj->selected)
434                 {
435                         painter->SetPen(0x00FF00, 2.0, LSSolid);
436                 }
437                 else
438                 {
439                         painter->SetPen(obj->color, Global::zoom * scaledThickness, obj->style);
440                         painter->SetBrush(obj->color);
441
442                         // penStamp supresses object highlighting, so that changes can be seen.
443                         if (supressSelected || Global::penStamp)
444                         {
445                                 if (obj->hitObject)
446                                 {
447                                         painter->SetPen(Global::penColor, Global::zoom * Global::scale * Global::penWidth, Global::penStyle);
448                                         painter->SetBrush(Global::penColor);
449                                 }
450                         }
451                         else if (obj->selected || obj->hitObject)
452                                 painter->SetPen(0xFF0000, Global::zoom * scaledThickness, LSDash);
453                 }
454
455                 switch (obj->type)
456                 {
457                 case OTLine:
458                         painter->DrawLine(obj->p[0], obj->p[1]);
459
460                         if (obj->hitPoint[0])
461                                 painter->DrawHandle(obj->p[0]);
462
463                         if (obj->hitPoint[1])
464                                 painter->DrawHandle(obj->p[1]);
465
466                         if (obj->hitObject)
467                                 painter->DrawSmallHandle(Geometry::Midpoint((Line *)obj));
468
469                         break;
470
471                 case OTCircle:
472                         painter->SetBrush(QBrush(Qt::NoBrush));
473                         painter->DrawEllipse(obj->p[0], obj->radius[0], obj->radius[0]);
474
475                         if (obj->hitPoint[0])
476                                 painter->DrawHandle(obj->p[0]);
477
478                         break;
479
480                 case OTArc:
481                         painter->DrawArc(obj->p[0], obj->radius[0], obj->angle[0], obj->angle[1]);
482
483                         if (obj->hitPoint[0])
484                                 painter->DrawHandle(obj->p[0]);
485
486                         if (obj->hitPoint[1])
487                                 painter->DrawHandle(obj->p[0] + (Vector(cos(obj->angle[0]), sin(obj->angle[0])) * obj->radius[0]));
488
489                         if (obj->hitPoint[2])
490                                 painter->DrawHandle(obj->p[0] + (Vector(cos(obj->angle[0] + obj->angle[1]), sin(obj->angle[0] + obj->angle[1])) * obj->radius[0]));
491
492                         break;
493
494                 case OTDimension:
495                 {
496                         Dimension * d = (Dimension *)obj;
497
498                         Vector v(d->p[0], d->p[1]);
499                         double angle = v.Angle();
500                         Vector unit = v.Unit();
501                         d->lp[0] = d->p[0], d->lp[1] = d->p[1];
502                         Vector ortho;
503                         double x1, y1, length;
504
505                         if (d->subtype == DTLinearVert)
506                         {
507                                 if ((angle < 0) || (angle > HALF_TAU))
508                                 {
509                                         x1 = (d->p[0].x > d->p[1].x ? d->p[0].x : d->p[1].x);
510                                         y1 = (d->p[0].y > d->p[1].y ? d->p[0].y : d->p[1].y);
511                                         ortho = Vector(1.0, 0);
512                                         angle = THREE_QTR_TAU;
513                                 }
514                                 else
515                                 {
516                                         x1 = (d->p[0].x > d->p[1].x ? d->p[1].x : d->p[0].x);
517                                         y1 = (d->p[0].y > d->p[1].y ? d->p[1].y : d->p[0].y);
518                                         ortho = Vector(-1.0, 0);
519                                         angle = QTR_TAU;
520                                 }
521
522                                 d->lp[0].x = d->lp[1].x = x1;
523                                 length = fabs(d->p[0].y - d->p[1].y);
524                         }
525                         else if (d->subtype == DTLinearHorz)
526                         {
527                                 if ((angle < QTR_TAU) || (angle > THREE_QTR_TAU))
528                                 {
529                                         x1 = (d->p[0].x > d->p[1].x ? d->p[0].x : d->p[1].x);
530                                         y1 = (d->p[0].y > d->p[1].y ? d->p[0].y : d->p[1].y);
531                                         ortho = Vector(0, 1.0);
532                                         angle = 0;
533                                 }
534                                 else
535                                 {
536                                         x1 = (d->p[0].x > d->p[1].x ? d->p[1].x : d->p[0].x);
537                                         y1 = (d->p[0].y > d->p[1].y ? d->p[1].y : d->p[0].y);
538                                         ortho = Vector(0, -1.0);
539                                         angle = HALF_TAU;
540                                 }
541
542                                 d->lp[0].y = d->lp[1].y = y1;
543                                 length = fabs(d->p[0].x - d->p[1].x);
544                         }
545                         else if (d->subtype == DTLinear)
546                         {
547                                 angle = Vector(d->lp[0], d->lp[1]).Angle();
548                                 ortho = Vector::Normal(d->lp[0], d->lp[1]);
549                                 length = v.Magnitude();
550                         }
551
552                         unit = Vector(d->lp[0], d->lp[1]).Unit();
553
554                         Point p1 = d->lp[0] + (ortho * (d->offset + (10.0 * scaledThickness)));
555                         Point p2 = d->lp[1] + (ortho * (d->offset + (10.0 * scaledThickness)));
556                         Point p3 = d->lp[0] + (ortho * (d->offset + (16.0 * scaledThickness)));
557                         Point p4 = d->lp[1] + (ortho * (d->offset + (16.0 * scaledThickness)));
558                         Point p5 = d->p[0] + (ortho * 4.0 * scaledThickness);
559                         Point p6 = d->p[1] + (ortho * 4.0 * scaledThickness);
560
561                 /*
562                 The numbers hardcoded into here, what are they?
563                 I believe they are pixels.
564                 */
565                         // Draw extension lines (if certain type)
566                         painter->DrawLine(p3, p5);
567                         painter->DrawLine(p4, p6);
568
569                         // Calculate whether or not the arrowheads are too crowded to put
570                         // inside the extension lines. 9.0 is the length of the arrowhead.
571                         double t = Geometry::ParameterOfLineAndPoint(d->lp[0], d->lp[1], d->lp[1] - (unit * 9.0 * scaledThickness));
572
573                         // On the screen, it's acting like this is actually 58%...
574                         // This is correct, we want it to happen at > 50%
575                         if (t > 0.58)
576                         {
577                                 // Draw main dimension line + arrowheads
578                                 painter->DrawLine(p1, p2);
579                                 painter->DrawArrowhead(p1, p2, scaledThickness);
580                                 painter->DrawArrowhead(p2, p1, scaledThickness);
581                         }
582                         else
583                         {
584                                 // Draw outside arrowheads
585                                 Point p7 = p1 - (unit * 9.0 * scaledThickness);
586                                 Point p8 = p2 + (unit * 9.0 * scaledThickness);
587                                 painter->DrawArrowhead(p1, p7, scaledThickness);
588                                 painter->DrawArrowhead(p2, p8, scaledThickness);
589                                 painter->DrawLine(p1, p1 - (unit * 14.0 * scaledThickness));
590                                 painter->DrawLine(p2, p2 + (unit * 14.0 * scaledThickness));
591                         }
592
593                         // Draw length of dimension line...
594                         painter->SetFont(QFont("Arial", 8.0 * Global::zoom * scaledThickness));
595                         Point ctr = p2 + (Vector(p2, p1) / 2.0);
596
597                         QString dimText;
598
599                         if (length < 12.0)
600                                 dimText = QString("%1\"").arg(length);
601                         else
602                         {
603                                 double feet = (double)((int)length / 12);
604                                 double inches = length - (feet * 12.0);
605
606                                 if (inches == 0)
607                                         dimText = QString("%1'").arg(feet);
608                                 else
609                                         dimText = QString("%1' %2\"").arg(feet).arg(inches);
610                         }
611
612 /*
613 Where is the text offset?  It looks like it's drawing in the center, but obviously it isn't.  It isn't here, it's in Painter::DrawAngledText().
614 */
615                         painter->DrawAngledText(ctr, angle, dimText, scaledThickness);
616
617                         if (d->hitObject)
618                         {
619                                 Point hp1 = (p1 + p2) / 2.0;
620                                 Point hp2 = (p1 + hp1) / 2.0;
621                                 Point hp3 = (hp1 + p2) / 2.0;
622
623                                 if (d->hitPoint[2])
624                                 {
625                                         painter->SetPen(QPen(Qt::magenta, 1.0, Qt::SolidLine));
626                                         painter->SetBrush(QBrush(QColor(Qt::magenta)));
627                                         painter->DrawArrowHandle(hp1, ortho.Angle() + HALF_TAU);
628                                         painter->SetPen(QPen(Qt::magenta, 2.0, Qt::DotLine));
629                                 }
630
631                                 painter->DrawHandle(hp1);
632                                 painter->SetPen(QPen(Qt::blue, 1.0 * Global::zoom * scaledThickness, Qt::SolidLine));
633
634                                 if (d->hitPoint[3])
635                                 {
636                                         painter->SetPen(QPen(Qt::magenta, 1.0, Qt::SolidLine));
637                                         painter->SetBrush(QBrush(QColor(Qt::magenta)));
638                                         painter->DrawArrowToLineHandle(hp2, (d->subtype == DTLinearVert ? v.Angle() - QTR_TAU : (v.Angle() < HALF_TAU ? HALF_TAU : 0)));
639                                         painter->SetPen(QPen(Qt::magenta, 2.0, Qt::DotLine));
640                                 }
641
642                                 painter->DrawHandle(hp2);
643                                 painter->SetPen(QPen(Qt::blue, 1.0 * Global::zoom * scaledThickness, Qt::SolidLine));
644
645                                 if (d->hitPoint[4])
646                                 {
647                                         painter->SetPen(QPen(Qt::magenta, 1.0, Qt::SolidLine));
648                                         painter->SetBrush(QBrush(QColor(Qt::magenta)));
649                                         painter->DrawArrowToLineHandle(hp3, (d->subtype == DTLinearHorz ? v.Angle() - QTR_TAU : (v.Angle() > HALF_TAU && v.Angle() < THREE_QTR_TAU ? THREE_QTR_TAU : QTR_TAU)));
650                                         painter->SetPen(QPen(Qt::magenta, 2.0, Qt::DotLine));
651                                 }
652
653                                 painter->DrawHandle(hp3);
654                         }
655
656                         if (obj->hitPoint[0])
657                                 painter->DrawHandle(obj->p[0]);
658
659                         if (obj->hitPoint[1])
660                                 painter->DrawHandle(obj->p[1]);
661
662                         break;
663                 }
664
665                 case OTText:
666                 {
667                         Text * t = (Text *)obj;
668
669                         if (t->measured == false)
670                         {
671                                 t->extents = painter->MeasureTextObject(t->s.c_str(), scaledThickness);
672                                 t->measured = true;
673                         }
674
675                         painter->DrawTextObject(t->p[0], t->s.c_str(), scaledThickness, t->angle[0]);
676                         break;
677                 }
678
679                 case OTSpline:
680                 {
681                         break;
682                 }
683
684                 case OTPolygon:
685                 {
686                         break;
687                 }
688
689                 case OTPolyline:
690                 {
691                         break;
692                 }
693
694                 case OTContainer:
695                 {
696                         // Containers require recursive rendering...
697                         Container * c = (Container *)obj;
698 //printf("About to render container: # objs=%i, layer=%i\n", (*c).objects.size(), layer);
699                         RenderObjects(painter, (*c).objects, layer, ignoreLayer);
700
701 //printf("Container extents: <%lf, %lf>, <%lf, %lf>\nsize: %i\n", r.l, r.t, r.r, r.b, c->objects.size());
702                         // Containers also have special indicators showing they are selected
703                         if (c->selected || c->hitObject)
704                         {
705 //                              Rect r = GetObjectExtents(obj);
706 //                              painter->DrawRectCorners(r);
707                                 painter->DrawRectCorners(Rect(c->p[0], c->p[1]));
708                         }
709
710                         break;
711                 }
712
713                 default:
714                         break;
715                 }
716         }
717
718         supressSelected = false;
719 }
720
721 //
722 // This toggles the selection being hovered (typically, only 1 object)
723 //
724 void DrawingView::AddHoveredToSelection(void)
725 {
726         for(VPVectorIter i=document.objects.begin(); i!=document.objects.end(); i++)
727         {
728                 if (((Object *)(*i))->hovered)
729 //                      ((Object *)(*i))->selected = true;
730                         ((Object *)(*i))->selected = !((Object *)(*i))->selected;
731         }
732 }
733
734 VPVector DrawingView::GetSelection(void)
735 {
736         VPVector v;
737
738         for(VPVectorIter i=document.objects.begin(); i!=document.objects.end(); i++)
739         {
740                 if (((Object *)(*i))->selected)
741                         v.push_back(*i);
742         }
743
744         return v;
745 }
746
747 //
748 // When testing for hovered intersections, we need to be able to exclude some
749 // objects which have funky characteristics or handles; so we allow for that
750 // here.
751 //
752 VPVector DrawingView::GetHovered(bool exclude/*= false*/)
753 {
754         VPVector v;
755
756         for(VPVectorIter i=document.objects.begin(); i!=document.objects.end(); i++)
757         {
758                 Object * obj = (Object *)(*i);
759
760                 if (obj->hovered)
761                 {
762                         if (exclude
763                                 && ((obj->type == OTDimension)
764                                         || ((obj->type == OTCircle) && (obj->hitPoint[0] == true))
765                                         || ((obj->type == OTArc) && (obj->hitPoint[0] == true))
766                                         || (draggingObject && (obj == dragged))))
767                                 continue;
768
769                         v.push_back(*i);
770                 }
771         }
772
773         return v;
774 }
775
776 void DrawingView::resizeEvent(QResizeEvent * /*event*/)
777 {
778         Global::screenSize = Vector(size().width(), size().height());
779 }
780
781 void DrawingView::ToolHandler(int mode, Point p)
782 {
783         // Drop angle snap until it's needed
784         angleSnap = false;
785
786         if (Global::tool == TTLine)
787                 LineHandler(mode, p);
788         else if (Global::tool == TTCircle)
789                 CircleHandler(mode, p);
790         else if (Global::tool == TTArc)
791                 ArcHandler(mode, p);
792         else if (Global::tool == TTRotate)
793                 RotateHandler(mode, p);
794         else if (Global::tool == TTMirror)
795                 MirrorHandler(mode, p);
796         else if (Global::tool == TTDimension)
797                 DimensionHandler(mode, p);
798         else if (Global::tool == TTTriangulate)
799                 TriangulateHandler(mode, p);
800         else if (Global::tool == TTTrim)
801                 TrimHandler(mode, p);
802         else if (Global::tool == TTParallel)
803                 ParallelHandler(mode, p);
804 }
805
806 void DrawingView::ToolDraw(Painter * painter)
807 {
808         if (Global::tool == TTLine)
809         {
810                 if (Global::toolState == TSNone)
811                 {
812                         painter->DrawHandle(toolPoint[0]);
813                 }
814                 else if ((Global::toolState == TSPoint2) && shiftDown)
815                 {
816                         painter->DrawHandle(toolPoint[1]);
817                 }
818                 else
819                 {
820                         painter->DrawLine(toolPoint[0], toolPoint[1]);
821                         painter->DrawHandle(toolPoint[1]);
822
823                         Vector v(toolPoint[0], toolPoint[1]);
824                         double absAngle = v.Angle() * RADIANS_TO_DEGREES;
825                         double absLength = v.Magnitude();
826                         QString text = tr("Length: %1 in.\n") + QChar(0x2221) + tr(": %2");
827                         informativeText = text.arg(absLength).arg(absAngle);
828                 }
829         }
830         else if (Global::tool == TTCircle)
831         {
832                 if (Global::toolState == TSNone)
833                 {
834                         painter->DrawHandle(toolPoint[0]);
835                 }
836                 else if ((Global::toolState == TSPoint2) && shiftDown)
837                 {
838                         painter->DrawHandle(toolPoint[1]);
839                 }
840                 else
841                 {
842                         painter->DrawCross(toolPoint[0]);
843                         double length = Vector::Magnitude(toolPoint[0], toolPoint[1]);
844                         painter->SetBrush(QBrush(Qt::NoBrush));
845                         painter->DrawEllipse(toolPoint[0], length, length);
846                         QString text = tr("Radius: %1 in.");
847                         informativeText = text.arg(length);
848                 }
849         }
850         else if (Global::tool == TTArc)
851         {
852                 if (Global::toolState == TSNone)
853                 {
854                         painter->DrawHandle(toolPoint[0]);
855                 }
856                 else if (Global::toolState == TSPoint2)
857                 {
858                         double length = Vector::Magnitude(toolPoint[0], toolPoint[1]);
859                         painter->SetBrush(QBrush(Qt::NoBrush));
860                         painter->DrawEllipse(toolPoint[0], length, length);
861                         painter->DrawLine(toolPoint[0], toolPoint[1]);
862                         painter->DrawHandle(toolPoint[1]);
863                         QString text = tr("Radius: %1 in.");
864                         informativeText = text.arg(length);
865                 }
866                 else if (Global::toolState == TSPoint3)
867                 {
868                         double angle = Vector::Angle(toolPoint[0], toolPoint[2]);
869                         painter->DrawLine(toolPoint[0], toolPoint[2]);
870                         painter->SetBrush(QBrush(Qt::NoBrush));
871                         painter->DrawEllipse(toolPoint[0], toolPoint[1].x, toolPoint[1].x);
872                         painter->DrawHandle(toolPoint[0] + (Vector(cos(angle), sin(angle)) * toolPoint[1].x));
873                         QString text = tr("Angle start: %1") + QChar(0x00B0);
874                         informativeText = text.arg(RADIANS_TO_DEGREES * angle);
875                 }
876                 else
877                 {
878                         double angle = Vector::Angle(toolPoint[0], toolPoint[3]);
879                         double span = angle - toolPoint[2].x;
880
881                         if (span < 0)
882                                 span += TAU;
883
884                         painter->DrawLine(toolPoint[0], toolPoint[3]);
885                         painter->SetBrush(QBrush(Qt::NoBrush));
886                         painter->DrawEllipse(toolPoint[0], toolPoint[1].x, toolPoint[1].x);
887                         painter->SetPen(0xFF00FF, 2.0, LSSolid);
888                         painter->DrawArc(toolPoint[0], toolPoint[1].x, toolPoint[2].x, span);
889                         painter->DrawHandle(toolPoint[0] + (Vector(cos(angle), sin(angle)) * toolPoint[1].x));
890                         QString text = tr("Arc span: %1") + QChar(0x00B0);
891                         informativeText = text.arg(RADIANS_TO_DEGREES * span);
892                 }
893         }
894         else if (Global::tool == TTRotate)
895         {
896                 if ((Global::toolState == TSNone) || (Global::toolState == TSPoint1))
897                         painter->DrawHandle(toolPoint[0]);
898                 else if ((Global::toolState == TSPoint2) && shiftDown)
899                         painter->DrawHandle(toolPoint[1]);
900                 else
901                 {
902                         if (toolPoint[0] == toolPoint[1])
903                                 return;
904
905                         painter->DrawLine(toolPoint[0], toolPoint[1]);
906
907                         double absAngle = (Vector(toolPoint[1] - toolPoint[0]).Angle()) * RADIANS_TO_DEGREES;
908                         QString text = QChar(0x2221) + QObject::tr(": %1");
909                         informativeText = text.arg(absAngle);
910
911                         if (ctrlDown)
912                                 informativeText += " (Copy)";
913                 }
914         }
915         else if (Global::tool == TTMirror)
916         {
917                 if ((Global::toolState == TSNone) || (Global::toolState == TSPoint1))
918                         painter->DrawHandle(toolPoint[0]);
919                 else if ((Global::toolState == TSPoint2) && shiftDown)
920                         painter->DrawHandle(toolPoint[1]);
921                 else
922                 {
923                         if (toolPoint[0] == toolPoint[1])
924                                 return;
925
926                         Point mirrorPoint = toolPoint[0] + Vector(toolPoint[1], toolPoint[0]);
927                         painter->DrawLine(mirrorPoint, toolPoint[1]);
928
929                         double absAngle = (Vector(toolPoint[1] - toolPoint[0]).Angle()) * RADIANS_TO_DEGREES;
930
931                         if (absAngle > 180.0)
932                                 absAngle -= 180.0;
933
934                         QString text = QChar(0x2221) + QObject::tr(": %1");
935                         informativeText = text.arg(absAngle);
936
937                         if (ctrlDown)
938                                 informativeText += " (Copy)";
939                 }
940         }
941         if (Global::tool == TTDimension)
942         {
943                 if (Global::toolState == TSNone)
944                 {
945                         painter->DrawHandle(toolPoint[0]);
946                 }
947                 else if ((Global::toolState == TSPoint2) && shiftDown)
948                 {
949                         painter->DrawHandle(toolPoint[1]);
950                 }
951                 else
952                 {
953                         painter->DrawLine(toolPoint[0], toolPoint[1]);
954                         painter->DrawHandle(toolPoint[1]);
955
956                         Vector v(toolPoint[0], toolPoint[1]);
957                         double absAngle = v.Angle() * RADIANS_TO_DEGREES;
958                         double absLength = v.Magnitude();
959                         QString text = tr("Length: %1 in.\n") + QChar(0x2221) + tr(": %2");
960                         informativeText = text.arg(absLength).arg(absAngle);
961                 }
962         }
963 }
964
965 void DrawingView::LineHandler(int mode, Point p)
966 {
967         switch (mode)
968         {
969         case ToolMouseDown:
970 /*              toolObj[0] = NULL;
971
972                 // Check to see if we can do a circle tangent snap
973                 if (numHovered == 1)
974                 {
975                         VPVector hover = GetHovered();
976                         Object * obj = (Object *)hover[0];
977
978                         // Save for later if the object clicked was a circle (need to check that it wasn't the center clicked on, because that will fuck up connecting centers of circles with lines... and now we do! :-)
979                         if ((obj->type == OTCircle) && (obj->hitPoint[0] == false))
980                                 toolObj[0] = obj;
981                 }*/
982
983                 if (Global::toolState == TSNone)
984                         toolPoint[0] = p;
985                 else
986                         toolPoint[1] = p;
987
988                 break;
989
990         case ToolMouseMove:
991                 if (Global::toolState == TSNone)
992                         toolPoint[0] = p;
993                 else
994                 {
995                         toolPoint[1] = p;
996 /*                      bool isCircle = false;
997
998                         if (numHovered == 1)
999                         {
1000                                 VPVector hover = GetHovered();
1001                                 Object * obj = (Object *)hover[0];
1002
1003                                 if ((obj->type == OTCircle) && (obj->hitPoint[0] == false))
1004                                 {
1005                                         isCircle = true;
1006                                         toolObj[1] = obj;
1007                                 }
1008                         }
1009
1010                         // Adjust initial point if it's on a circle (tangent point)
1011                         if (toolObj[0] != NULL)
1012                         {
1013                                 if (isCircle)
1014                                 {
1015                                         Geometry::FindTangents(toolObj[0], toolObj[1]);
1016
1017                                         if (Global::numIntersectPoints > 0)
1018                                         {
1019                                                 toolPoint[0] = Global::intersectPoint[0];
1020                                                 toolPoint[1] = Global::intersectPoint[1];
1021                                         }
1022                                 }
1023                                 else
1024                                 {
1025                                         Geometry::FindTangents(toolObj[0], p);
1026
1027                                         if (Global::numIntersectPoints > 0)
1028                                                 toolPoint[0] = Global::intersectPoint[0];
1029                                 }
1030                         }
1031                         else
1032                         {
1033                                 if (isCircle)
1034                                 {
1035                                         Geometry::FindTangents(toolObj[1], toolPoint[0]);
1036
1037                                         if (Global::numIntersectPoints > 0)
1038                                                 toolPoint[1] = Global::intersectPoint[0];
1039                                 }
1040                         }*/
1041                 }
1042
1043                 break;
1044
1045         case ToolMouseUp:
1046                 if (Global::toolState == TSNone)
1047                 {
1048                         Global::toolState = TSPoint2;
1049                         // Prevent spurious line from drawing...
1050                         toolPoint[1] = toolPoint[0];
1051                 }
1052                 else if ((Global::toolState == TSPoint2) && shiftDown)
1053                 {
1054                         // Key override is telling us to make a new line, not continue the
1055                         // previous one.
1056                         toolPoint[0] = toolPoint[1];
1057                 }
1058                 else
1059                 {
1060                         Line * l = new Line(toolPoint[0], toolPoint[1], Global::penWidth, Global::penColor, Global::penStyle);
1061                         l->layer = Global::activeLayer;
1062                         document.objects.push_back(l);
1063                         toolPoint[0] = toolPoint[1];
1064                 }
1065         }
1066 }
1067
1068 void DrawingView::CircleHandler(int mode, Point p)
1069 {
1070         switch (mode)
1071         {
1072         case ToolMouseDown:
1073                 if (Global::toolState == TSNone)
1074                         toolPoint[0] = p;
1075                 else
1076                         toolPoint[1] = p;
1077
1078                 break;
1079
1080         case ToolMouseMove:
1081                 if (Global::toolState == TSNone)
1082                         toolPoint[0] = p;
1083                 else
1084                         toolPoint[1] = p;
1085
1086                 break;
1087
1088         case ToolMouseUp:
1089                 if (Global::toolState == TSNone)
1090                 {
1091                         Global::toolState = TSPoint2;
1092                         // Prevent spurious line from drawing...
1093                         toolPoint[1] = toolPoint[0];
1094                 }
1095                 else if ((Global::toolState == TSPoint2) && shiftDown)
1096                 {
1097                         // Key override is telling us to make a new line, not continue the
1098                         // previous one.
1099                         toolPoint[0] = toolPoint[1];
1100                 }
1101                 else
1102                 {
1103                         double length = Vector::Magnitude(toolPoint[0], toolPoint[1]);
1104                         Circle * c = new Circle(toolPoint[0], length, Global::penWidth, Global::penColor, Global::penStyle);
1105                         c->layer = Global::activeLayer;
1106                         document.objects.push_back(c);
1107                         toolPoint[0] = toolPoint[1];
1108                         Global::toolState = TSNone;
1109                 }
1110         }
1111 }
1112
1113 void DrawingView::ArcHandler(int mode, Point p)
1114 {
1115         switch (mode)
1116         {
1117         case ToolMouseDown:
1118                 if (Global::toolState == TSNone)
1119                         toolPoint[0] = p;
1120                 else if (Global::toolState == TSPoint2)
1121                         toolPoint[1] = p;
1122                 else if (Global::toolState == TSPoint3)
1123                         toolPoint[2] = p;
1124                 else
1125                         toolPoint[3] = p;
1126
1127                 break;
1128
1129         case ToolMouseMove:
1130                 if (Global::toolState == TSNone)
1131                         toolPoint[0] = p;
1132                 else if (Global::toolState == TSPoint2)
1133                         toolPoint[1] = p;
1134                 else if (Global::toolState == TSPoint3)
1135                 {
1136                         toolPoint[2] = p;
1137                         angleSnap = true;
1138                 }
1139                 else
1140                 {
1141                         toolPoint[3] = p;
1142                         angleSnap = true;
1143                 }
1144
1145                 break;
1146
1147         case ToolMouseUp:
1148                 if (Global::toolState == TSNone)
1149                 {
1150                         // Prevent spurious line from drawing...
1151                         toolPoint[1] = toolPoint[0];
1152                         Global::toolState = TSPoint2;
1153                 }
1154                 else if (Global::toolState == TSPoint2)
1155                 {
1156                         if (shiftDown)
1157                         {
1158                                 // Key override is telling us to start arc at new center, not
1159                                 // continue the current one.
1160                                 toolPoint[0] = toolPoint[1];
1161                                 return;
1162                         }
1163
1164                         // Set the radius in toolPoint[1].x
1165                         toolPoint[1].x = Vector::Magnitude(toolPoint[0], toolPoint[1]);
1166                         Global::toolState = TSPoint3;
1167                 }
1168                 else if (Global::toolState == TSPoint3)
1169                 {
1170                         // Set the angle in toolPoint[2].x
1171                         toolPoint[2].x = Vector::Angle(toolPoint[0], toolPoint[2]);
1172                         Global::toolState = TSPoint4;
1173                 }
1174                 else
1175                 {
1176                         double endAngle = Vector::Angle(toolPoint[0], toolPoint[3]);
1177                         double span = endAngle - toolPoint[2].x;
1178
1179                         if (span < 0)
1180                                 span += TAU;
1181
1182                         Arc * arc = new Arc(toolPoint[0], toolPoint[1].x, toolPoint[2].x, span, Global::penWidth, Global::penColor, Global::penStyle);
1183                         arc->layer = Global::activeLayer;
1184                         document.objects.push_back(arc);
1185                         Global::toolState = TSNone;
1186                 }
1187         }
1188 }
1189
1190 void DrawingView::RotateHandler(int mode, Point p)
1191 {
1192         switch (mode)
1193         {
1194         case ToolMouseDown:
1195                 if (Global::toolState == TSNone)
1196                 {
1197                         toolPoint[0] = p;
1198 //                      SavePointsFrom(select, toolScratch);
1199                         CopyObjects(select, toolScratch2);
1200                         Global::toolState = TSPoint1;
1201                 }
1202                 else if (Global::toolState == TSPoint1)
1203                         toolPoint[0] = p;
1204                 else
1205                         toolPoint[1] = p;
1206
1207                 break;
1208
1209         case ToolMouseMove:
1210                 if ((Global::toolState == TSPoint1) || (Global::toolState == TSNone))
1211                         toolPoint[0] = p;
1212                 else if (Global::toolState == TSPoint2)
1213                 {
1214                         toolPoint[1] = p;
1215
1216                         if (shiftDown)
1217                                 return;
1218
1219                         angleSnap = true;
1220                         double angle = Vector(toolPoint[0], toolPoint[1]).Angle();
1221                         VPVectorIter j = select.begin();
1222 //                      std::vector<Object>::iterator i = toolScratch.begin();
1223                         VPVectorIter i = toolScratch2.begin();
1224
1225 //                      for(; i!=toolScratch.end(); i++, j++)
1226                         for(; i!=toolScratch2.end(); i++, j++)
1227                         {
1228 //                              Object objT = *i;
1229 //                              Point p1 = Geometry::RotatePointAroundPoint(objT.p[0], toolPoint[0], angle);
1230 //                              Point p2 = Geometry::RotatePointAroundPoint(objT.p[1], toolPoint[0], angle);
1231                                 Object * objT = (Object *)(*i);
1232                                 Object * objS = (Object *)(*j);
1233
1234                                 Point p1 = Geometry::RotatePointAroundPoint(objT->p[0], toolPoint[0], angle);
1235                                 Point p2 = Geometry::RotatePointAroundPoint(objT->p[1], toolPoint[0], angle);
1236
1237                                 objS->p[0] = p1;
1238                                 objS->p[1] = p2;
1239
1240 //                              if (objT.type == OTArc || objT.type == OTText)
1241                                 if (objT->type == OTArc || objT->type == OTText)
1242                                 {
1243 //                                      objS->angle[0] = objT.angle[0] + angle;
1244                                         objS->angle[0] = objT->angle[0] + angle;
1245
1246                                         if (objS->angle[0] > TAU)
1247                                                 objS->angle[0] -= TAU;
1248                                 }
1249 //                              else if (objT.type == OTContainer)
1250                                 else if (objT->type == OTContainer)
1251                                 {
1252                                         // OK, this doesn't work because toolScratch only has points and nothing in the containers... [ACTUALLY... toolScratch is is a vector of type Object... which DOESN'T have an objects vector in it...]
1253 //                                      Container * c = (Container *)&objT;
1254                                         Container * c = (Container *)objT;
1255                                         Container * c2 = (Container *)objS;
1256                                         VPVectorIter l = c->objects.begin();
1257                                         // TODO: Rotate items in the container
1258                                         // TODO: Make this recursive
1259                                         for(VPVectorIter k=c2->objects.begin(); k!=c2->objects.end(); k++, l++)
1260                                         {
1261                                                 Object * obj3 = (Object *)(*k);
1262                                                 Object * obj4 = (Object *)(*l);
1263
1264                                                 p1 = Geometry::RotatePointAroundPoint(obj4->p[0], toolPoint[0], angle);
1265                                                 p2 = Geometry::RotatePointAroundPoint(obj4->p[1], toolPoint[0], angle);
1266
1267                                                 obj3->p[0] = p1;
1268                                                 obj3->p[1] = p2;
1269 //                                              obj3->angle[0] = objT.angle[0] + angle;
1270                                                 obj3->angle[0] = obj4->angle[0] + angle;
1271
1272                                                 if (obj3->angle[0] > TAU)
1273                                                         obj3->angle[0] -= TAU;
1274                                         }
1275
1276                                         Rect r = GetObjectExtents(objS);
1277                                         c2->p[0] = r.TopLeft();
1278                                         c2->p[1] = r.BottomRight();
1279                                 }
1280                         }
1281                 }
1282
1283                 break;
1284
1285         case ToolMouseUp:
1286                 if (Global::toolState == TSPoint1)
1287                 {
1288                         Global::toolState = TSPoint2;
1289                         // Prevent spurious line from drawing...
1290                         toolPoint[1] = toolPoint[0];
1291                 }
1292                 else if ((Global::toolState == TSPoint2) && shiftDown)
1293                 {
1294                         // Key override is telling us to make a new line, not continue the
1295                         // previous one.
1296                         toolPoint[0] = toolPoint[1];
1297                 }
1298                 else
1299                 {
1300                         // Either we're finished with our rotate, or we're stamping a copy.
1301                         if (ctrlDown)
1302                         {
1303                                 // Stamp a copy of the selection at the current rotation & bail
1304                                 VPVector temp;
1305                                 CopyObjects(select, temp);
1306                                 ClearSelected(temp);
1307                                 AddObjectsTo(document.objects, temp);
1308 //                              RestorePointsTo(select, toolScratch);
1309                                 RestorePointsTo(select, toolScratch2);
1310                                 return;
1311                         }
1312
1313                         toolPoint[0] = p;
1314                         Global::toolState = TSPoint1;
1315 //                      SavePointsFrom(select, toolScratch);
1316                         DeleteContents(toolScratch2);
1317                         CopyObjects(select, toolScratch2);
1318                 }
1319
1320                 break;
1321
1322         case ToolKeyDown:
1323                 // Reset the selection if shift held down...
1324                 if (shiftDown)
1325 //                      RestorePointsTo(select, toolScratch);
1326                         RestorePointsTo(select, toolScratch2);
1327
1328                 break;
1329
1330         case ToolKeyUp:
1331                 // Reset selection when key is let up
1332                 if (!shiftDown)
1333                         RotateHandler(ToolMouseMove, toolPoint[1]);
1334
1335                 break;
1336
1337         case ToolCleanup:
1338 //              RestorePointsTo(select, toolScratch);
1339                 RestorePointsTo(select, toolScratch2);
1340                 DeleteContents(toolScratch2);
1341         }
1342 }
1343
1344 void DrawingView::MirrorHandler(int mode, Point p)
1345 {
1346         switch (mode)
1347         {
1348         case ToolMouseDown:
1349                 if (Global::toolState == TSNone)
1350                 {
1351                         toolPoint[0] = p;
1352                         SavePointsFrom(select, toolScratch);
1353                         Global::toolState = TSPoint1;
1354                 }
1355                 else if (Global::toolState == TSPoint1)
1356                         toolPoint[0] = p;
1357                 else
1358                         toolPoint[1] = p;
1359
1360                 break;
1361
1362         case ToolMouseMove:
1363                 if ((Global::toolState == TSPoint1) || (Global::toolState == TSNone))
1364                         toolPoint[0] = p;
1365                 else if (Global::toolState == TSPoint2)
1366                 {
1367                         toolPoint[1] = p;
1368
1369                         if (shiftDown)
1370                                 return;
1371
1372                         angleSnap = true;
1373                         double angle = Vector(toolPoint[0], toolPoint[1]).Angle();
1374                         VPVectorIter j = select.begin();
1375                         std::vector<Object>::iterator i = toolScratch.begin();
1376
1377                         for(; i!=toolScratch.end(); i++, j++)
1378                         {
1379                                 Object obj = *i;
1380                                 Point p1 = Geometry::MirrorPointAroundLine(obj.p[0], toolPoint[0], toolPoint[1]);
1381                                 Point p2 = Geometry::MirrorPointAroundLine(obj.p[1], toolPoint[0], toolPoint[1]);
1382                                 Object * obj2 = (Object *)(*j);
1383                                 obj2->p[0] = p1;
1384                                 obj2->p[1] = p2;
1385
1386 /*
1387 N.B.: When mirroring an arc thru a horizontal axis, this causes the arc to have
1388       a negative start angle which makes it impossible to interact with.
1389       !!! FIX !!!
1390 */
1391                                 if (obj.type == OTArc)
1392                                 {
1393                                         // This is 2*mirror angle - obj angle - obj span
1394                                         obj2->angle[0] = (2.0 * angle) - obj.angle[0] - obj.angle[1];
1395
1396                                         if (obj2->angle[0] > TAU)
1397                                                 obj2->angle[0] -= TAU;
1398                                 }
1399                         }
1400                 }
1401
1402                 break;
1403
1404         case ToolMouseUp:
1405                 if (Global::toolState == TSPoint1)
1406                 {
1407                         Global::toolState = TSPoint2;
1408                         // Prevent spurious line from drawing...
1409                         toolPoint[1] = toolPoint[0];
1410                 }
1411                 else if ((Global::toolState == TSPoint2) && shiftDown)
1412                 {
1413                         // Key override is telling us to make a new line, not continue the
1414                         // previous one.
1415                         toolPoint[0] = toolPoint[1];
1416                 }
1417                 else
1418                 {
1419                         // Either we're finished with our rotate, or we're stamping a copy.
1420                         if (ctrlDown)
1421                         {
1422                                 // Stamp a copy of the selection at the current rotation & bail
1423                                 VPVector temp;
1424                                 CopyObjects(select, temp);
1425                                 ClearSelected(temp);
1426                                 AddObjectsTo(document.objects, temp);
1427                                 RestorePointsTo(select, toolScratch);
1428                                 return;
1429                         }
1430
1431                         toolPoint[0] = p;
1432                         Global::toolState = TSPoint1;
1433                         SavePointsFrom(select, toolScratch);
1434                 }
1435
1436                 break;
1437
1438         case ToolKeyDown:
1439                 // Reset the selection if shift held down...
1440                 if (shiftDown)
1441                         RestorePointsTo(select, toolScratch);
1442
1443                 break;
1444
1445         case ToolKeyUp:
1446                 // Reset selection when key is let up
1447                 if (!shiftDown)
1448                         MirrorHandler(ToolMouseMove, toolPoint[1]);
1449
1450                 break;
1451
1452         case ToolCleanup:
1453                 RestorePointsTo(select, toolScratch);
1454         }
1455 }
1456
1457 void DrawingView::DimensionHandler(int mode, Point p)
1458 {
1459         switch (mode)
1460         {
1461         case ToolMouseDown:
1462                 if (Global::toolState == TSNone)
1463                         toolPoint[0] = p;
1464                 else
1465                         toolPoint[1] = p;
1466
1467                 break;
1468
1469         case ToolMouseMove:
1470                 if (Global::toolState == TSNone)
1471                         toolPoint[0] = p;
1472                 else
1473                         toolPoint[1] = p;
1474
1475                 break;
1476
1477         case ToolMouseUp:
1478                 if (Global::toolState == TSNone)
1479                 {
1480                         Global::toolState = TSPoint2;
1481                         // Prevent spurious line from drawing...
1482                         toolPoint[1] = toolPoint[0];
1483                 }
1484                 else if ((Global::toolState == TSPoint2) && shiftDown)
1485                 {
1486                         // Key override is telling us to make a new line, not continue the
1487                         // previous one.
1488                         toolPoint[0] = toolPoint[1];
1489                 }
1490                 else
1491                 {
1492                         Dimension * d = new Dimension(toolPoint[0], toolPoint[1], DTLinear);
1493                         d->layer = Global::activeLayer;
1494                         document.objects.push_back(d);
1495                         Global::toolState = TSNone;
1496                 }
1497         }
1498 }
1499
1500 void DrawingView::TriangulateHandler(int mode, Point/*p*/)
1501 {
1502         switch (mode)
1503         {
1504         case ToolMouseDown:
1505         {
1506                 // Skip if nothing hovered...
1507                 if (numHovered != 1)
1508                         break;
1509
1510                 VPVector hover = GetHovered();
1511                 Object * obj = (Object *)hover[0];
1512
1513                 // Skip if it's not a line...
1514                 if (obj->type != OTLine)
1515                         break;
1516
1517                 if (Global::toolState == TSNone)
1518                         toolObj[0] = obj;
1519                 else if (Global::toolState == TSPoint2)
1520                         toolObj[1] = obj;
1521                 else
1522                         toolObj[2] = obj;
1523
1524                 break;
1525         }
1526 #if 0
1527         case ToolMouseMove:
1528                 if (Global::toolState == TSNone)
1529                         toolPoint[0] = p;
1530                 else if (Global::toolState == TSPoint2)
1531                         toolPoint[1] = p;
1532                 else if (Global::toolState == TSPoint3)
1533                 {
1534                         toolPoint[2] = p;
1535                         angleSnap = true;
1536                 }
1537                 else
1538                 {
1539                         toolPoint[3] = p;
1540                         angleSnap = true;
1541                 }
1542
1543                 break;
1544 #endif
1545         case ToolMouseUp:
1546                 if (Global::toolState == TSNone)
1547                 {
1548                         Global::toolState = TSPoint2;
1549                 }
1550                 else if (Global::toolState == TSPoint2)
1551                 {
1552 /*                      if (shiftDown)
1553                         {
1554                                 // Key override is telling us to start arc at new center, not
1555                                 // continue the current one.
1556                                 toolPoint[0] = toolPoint[1];
1557                                 return;
1558                         }*/
1559
1560                         Global::toolState = TSPoint3;
1561                 }
1562                 else
1563                 {
1564                         double len2 = Vector::Magnitude(toolObj[1]->p[0], toolObj[1]->p[1]);
1565                         double len3 = Vector::Magnitude(toolObj[2]->p[0], toolObj[2]->p[1]);
1566
1567                         Circle c1(toolObj[0]->p[0], len2);
1568                         Circle c2(toolObj[0]->p[1], len3);
1569
1570                         Geometry::CheckCircleToCircleIntersection((Object *)&c1, (Object *)&c2);
1571
1572                         // Only move lines if the triangle formed by them is not degenerate
1573                         if (Global::numIntersectPoints > 0)
1574                         {
1575                                 toolObj[1]->p[0] = toolObj[0]->p[0];
1576                                 toolObj[1]->p[1] = Global::intersectPoint[0];
1577
1578                                 toolObj[2]->p[0] = Global::intersectPoint[0];
1579                                 toolObj[2]->p[1] = toolObj[0]->p[1];
1580                         }
1581
1582                         Global::toolState = TSNone;
1583                 }
1584         }
1585 }
1586
1587 void DrawingView::TrimHandler(int mode, Point p)
1588 {
1589 /*
1590 N.B.: this code is lifted straight out of the old oo code.  needs to be updated.
1591       Also: trim tool should ignore snap.
1592 */
1593         switch (mode)
1594         {
1595         case ToolMouseDown:
1596         {
1597 #if 0
1598                 Object * toTrim = doc->lastObjectHovered;
1599
1600                 if (toTrim == NULL)
1601                         return;
1602
1603                 Vector v(((Line *)toTrim)->position, ((Line *)toTrim)->endpoint);
1604
1605                 // Check to see which case we have...
1606                 // We're trimming point #1...
1607                 if (t == 0)
1608                 {
1609                         ((Line *)toTrim)->position = ((Line *)toTrim)->position + (v * u);
1610                 }
1611                 else if (u == 1.0)
1612                 {
1613                         ((Line *)toTrim)->endpoint = ((Line *)toTrim)->position + (v * t);
1614                 }
1615                 else
1616                 {
1617                         Point p1 = ((Line *)toTrim)->position + (v * t);
1618                         Point p2 = ((Line *)toTrim)->position + (v * u);
1619                         Point p3 = ((Line *)toTrim)->endpoint;
1620                         ((Line *)toTrim)->endpoint = p1;
1621                         Line * line = new Line(p2, p3);
1622                         emit ObjectReady(line);
1623                 }
1624
1625                 doc->lastObjectHovered = NULL;
1626 #endif
1627         }
1628                 break;
1629
1630         case ToolMouseMove:
1631         {
1632 #if 0
1633                 Object * toTrim = doc->lastObjectHovered;
1634                 t = 0, u = 1.0;
1635
1636                 if (toTrim == NULL)
1637                         return;
1638
1639                 if (toTrim->type != OTLine)
1640                         return;
1641
1642                 double pointHoveredT = Geometry::ParameterOfLineAndPoint(((Line *)toTrim)->position, ((Line *)toTrim)->endpoint, point);
1643
1644                 std::vector<Object *>::iterator i;
1645
1646                 for(i=doc->objects.begin(); i!=doc->objects.end(); i++)
1647                 {
1648                         // Can't trim against yourself... :-P
1649                         if (*i == toTrim)
1650                                 continue;
1651
1652                         Object * trimAgainst = *i;
1653                         double t1;//, u1;
1654
1655                         if ((toTrim->type != OTLine) || (trimAgainst->type != OTLine))
1656                                 continue;
1657
1658                         int intersects = Geometry::Intersects((Line *)toTrim, (Line *)trimAgainst, &t1);//, &u1);
1659
1660                         if (intersects)
1661                         {
1662                                 // Now what? We don't know which side to trim!
1663                                 // ... now we do, we know which side of the Line we're on!
1664                                 if ((t1 > t) && (t1 < pointHoveredT))
1665                                         t = t1;
1666
1667                                 if ((t1 < u) && (t1 > pointHoveredT))
1668                                         u = t1;
1669                         }
1670                 }
1671 #endif
1672                 // Bail out if nothing hovered...
1673                 if (numHovered != 1)
1674                 {
1675                         toolObj[0] = NULL;
1676                         return;
1677                 }
1678
1679                 VPVector hover = GetHovered();
1680                 Object * obj = (Object *)hover[0];
1681
1682                 // Skip if it's not a line...
1683                 if (obj->type != OTLine)
1684                 {
1685                         toolObj[0] = NULL;
1686                         return;
1687                 }
1688
1689                 toolObj[0] = obj;
1690                 double hoveredParam = Geometry::ParameterOfLineAndPoint(obj->p[0], obj->p[1], p);
1691
1692                 // Currently only deal with line against line trimming, can expand to
1693                 // others as well (line/circle, circle/circle, line/arc, etc)
1694                 VPVectorIter i;
1695                 for(i=document.objects.begin(); i!=document.objects.end(); i++)
1696                 {
1697                         obj = (Object *)(*i);
1698
1699                         if (obj == toolObj[0])
1700                                 continue;
1701                         else if (obj->type != OTLine)
1702                                 continue;
1703
1704                         Geometry::CheckLineToLineIntersection(toolObj[0], obj);
1705
1706                         if (Global::numIntersectParams > 0)
1707                         {
1708                                 // Mark the line segment somehow (the side the mouse is on) so that it can be drawn & trimmed when we hit ToolMouseDown.
1709                         }
1710                 }
1711         }
1712                 break;
1713
1714         case ToolMouseUp:
1715                 break;
1716
1717         case ToolKeyDown:
1718                 break;
1719
1720         case ToolKeyUp:
1721                 break;
1722
1723         case ToolCleanup:
1724                 break;
1725         }
1726 }
1727
1728 void DrawingView::ParallelHandler(int mode, Point /*p*/)
1729 {
1730         switch (mode)
1731         {
1732         case ToolMouseDown:
1733                 break;
1734
1735         case ToolMouseMove:
1736                 break;
1737
1738         case ToolMouseUp:
1739                 break;
1740
1741         case ToolKeyDown:
1742                 break;
1743
1744         case ToolKeyUp:
1745                 break;
1746
1747         case ToolCleanup:
1748                 break;
1749         }
1750 }
1751
1752 void DrawingView::mousePressEvent(QMouseEvent * event)
1753 {
1754         if (event->button() == Qt::LeftButton)
1755         {
1756 //printf("mousePressEvent::Qt::LeftButton numHovered=%li\n", numHovered);
1757                 Vector point = Painter::QtToCartesianCoords(Vector(event->x(), event->y()));
1758
1759                 // Handle tool processing, if any
1760                 if (Global::tool)
1761                 {
1762                         if (hoveringIntersection)
1763                                 point = intersectionPoint;
1764                         else if (hoverPointValid)
1765                                 point = hoverPoint;
1766                         else if (Global::snapToGrid)
1767                                 point = SnapPointToGrid(point);
1768
1769                         //Also, may want to figure out if hovering over a snap point on an
1770                         //object, snap to grid if not.
1771                         // Snap to object point if valid...
1772 //                      if (Global::snapPointIsValid)
1773 //                              point = Global::snapPoint;
1774
1775                         ToolHandler(ToolMouseDown, point);
1776                         return;
1777                 }
1778
1779                 // Clear the selection only if CTRL isn't being held on click
1780                 if (!ctrlDown)
1781                         ClearSelected(document.objects);
1782 //                      ClearSelection();
1783
1784                 // If any objects are being hovered on click, add them to the selection
1785                 // & return
1786                 if (numHovered > 0)
1787                 {
1788                         AddHoveredToSelection();
1789                         update();       // needed??
1790 //                      GetHovered(hover);      // prolly needed
1791                         VPVector hover2 = GetHovered();
1792                         dragged = (Object *)hover2[0];
1793                         draggingObject = true;
1794 //printf("mousePressEvent::numHovered > 0 (hover2[0]=$%llx, type=%s)\n", dragged, objName[dragged->type]);
1795
1796                         // Alert the pen widget
1797 // Maybe do this with an eyedropper tool on the pen bar?  [YES]
1798 //                      emit ObjectSelected(dragged);
1799                         if (Global::penDropper)
1800                         {
1801                                 Global::penColor = dragged->color;
1802                                 Global::penWidth = dragged->thickness;
1803                                 Global::penStyle = dragged->style;
1804                                 emit ObjectSelected(dragged);
1805                                 ClearSelected(document.objects);
1806                                 return;
1807                         }
1808
1809                         if (Global::penStamp)
1810                         {
1811                                 dragged->color = Global::penColor;
1812                                 dragged->thickness = Global::penWidth;
1813                                 dragged->style = Global::penStyle;
1814                                 return;
1815                         }
1816
1817                         // See if anything is using just a straight click on a handle
1818                         if (HandleObjectClicked())
1819                         {
1820                                 draggingObject = false;
1821                                 update();
1822                                 return;
1823                         }
1824
1825                         // Needed for grab & moving objects
1826                         // We do it *after*... why? (doesn't seem to confer any advantage...)
1827                         if (hoveringIntersection)
1828                                 oldPoint = intersectionPoint;
1829                         else if (hoverPointValid)
1830                                 oldPoint = hoverPoint;
1831                         else if (Global::snapToGrid)
1832                                 oldPoint = SnapPointToGrid(point);
1833
1834                         // Needed for fixed length handling
1835                         if (Global::fixedLength)
1836                         {
1837                                 if (dragged->type == OTLine)
1838                                 {
1839                                         dragged->length = Vector::Magnitude(dragged->p[0], dragged->p[1]);
1840                                 }
1841                         }
1842
1843                         if (dragged->type == OTCircle)
1844                         {
1845                                 // Save for informative text, uh, er, informing
1846                                 dragged->length = dragged->radius[0];
1847                         }
1848
1849                         return;
1850                 }
1851
1852                 // Didn't hit any object and not using a tool, so do a selection
1853                 // rectangle
1854                 Global::selectionInProgress = true;
1855                 Global::selection.setTopLeft(QPointF(point.x, point.y));
1856                 Global::selection.setBottomRight(QPointF(point.x, point.y));
1857                 select = GetSelection();
1858         }
1859         else if (event->button() == Qt::MiddleButton)
1860         {
1861                 scrollDrag = true;
1862                 oldPoint = Vector(event->x(), event->y());
1863                 // Should also change the mouse pointer as well...
1864                 setCursor(Qt::SizeAllCursor);
1865         }
1866 }
1867
1868 void DrawingView::mouseMoveEvent(QMouseEvent * event)
1869 {
1870         // It seems that wheelEvent() triggers this for some reason...
1871         if (scrollWheelSeen)
1872         {
1873                 scrollWheelSeen = false;
1874                 return;
1875         }
1876
1877         Vector point = Painter::QtToCartesianCoords(Vector(event->x(), event->y()));
1878         Global::selection.setBottomRight(QPointF(point.x, point.y));
1879         // Only needs to be done here, as mouse down is always preceded by movement
1880         Global::snapPointIsValid = false;
1881         hoveringIntersection = false;
1882         oldScrollPoint = Vector(event->x(), event->y());
1883
1884         // Scrolling...
1885         if ((event->buttons() & Qt::MiddleButton) || scrollDrag)
1886         {
1887                 point = Vector(event->x(), event->y());
1888                 // Since we're using Qt coords for scrolling, we have to adjust them
1889                 // here to conform to Cartesian coords, since the origin is using
1890                 // Cartesian. :-)
1891                 Vector delta(oldPoint, point);
1892                 delta /= Global::zoom;
1893                 delta.y = -delta.y;
1894                 Global::origin -= delta;
1895
1896 //              UpdateGridBackground();
1897                 update();
1898                 oldPoint = point;
1899                 return;
1900         }
1901
1902         // If we're doing a selection rect, see if any objects are engulfed by it
1903         // (implies left mouse button held down)
1904         if (Global::selectionInProgress)
1905         {
1906                 CheckObjectBounds();
1907
1908                 // Make sure previously selected objects stay selected (CTRL held)
1909                 for(VPVectorIter i=select.begin(); i!=select.end(); i++)
1910                 {
1911                         // Make sure *not* to select items on hidden layers
1912                         if (Global::layerHidden[((Object *)(*i))->layer] == false)
1913                                 ((Object *)(*i))->selected = true;
1914                 }
1915
1916                 update();
1917                 return;
1918         }
1919
1920         // Do object hit testing...
1921         bool needUpdate = HitTestObjects(point);
1922         VPVector hover2 = GetHovered(true); // Exclude dimension objects and circle centers (probably need to add arc centers too) from hover (also dragged objects...)
1923 #if 0
1924 {
1925 if (needUpdate)
1926 {
1927         printf("mouseMoveEvent:: numHovered=%li, hover2.size()=%li\n", numHovered, hover2.size());
1928
1929         if (hover2.size() > 0)
1930                 printf("                 (hover2[0]=$%llX, type=%s)\n", hover2[0], objName[((Object *)hover2[0])->type]);
1931 }
1932 }
1933 #endif
1934
1935         // Check for multi-hover...
1936         if (hover2.size() > 1)
1937         {
1938 //need to check for case where hover is over 2 circles and a 3rd's center (no longer a problem, I think)...
1939                 Object * obj1 = (Object *)hover2[0], * obj2 = (Object *)hover2[1];
1940
1941                 Geometry::Intersects(obj1, obj2);
1942                 int numIntersecting = Global::numIntersectParams;
1943                 double t = Global::intersectParam[0];
1944                 double u = Global::intersectParam[1];
1945
1946                 if (numIntersecting > 0)
1947                 {
1948                         Vector v1 = Geometry::GetPointForParameter(obj1, t);
1949                         Vector v2 = Geometry::GetPointForParameter(obj2, u);
1950                         QString text = tr("Intersection t=%1 (%3, %4), u=%2 (%5, %6)");
1951                         informativeText = text.arg(t).arg(u).arg(v1.x).arg(v1.y).arg(v2.x).arg(v2.y);
1952
1953                         hoveringIntersection = true;
1954                         intersectionPoint = v1;
1955                 }
1956
1957                 numIntersecting = Global::numIntersectPoints;
1958
1959                 if (numIntersecting > 0)
1960                 {
1961                         Vector v1 = Global::intersectPoint[0];
1962
1963                         if (numIntersecting == 2)
1964                         {
1965                                 Vector v2 = Global::intersectPoint[1];
1966
1967                                 if (Vector::Magnitude(v2, point) < Vector::Magnitude(v1, point))
1968                                         v1 = v2;
1969                         }
1970
1971                         QString text = tr("Intersection <%1, %2>");
1972                         informativeText = text.arg(v1.x).arg(v1.y);
1973                         hoveringIntersection = true;
1974                         intersectionPoint = v1;
1975                 }
1976         }
1977         else if (hover2.size() == 1)
1978         {
1979                 Object * obj = (Object *)hover2[0];
1980
1981                 if (obj->type == OTLine)
1982                 {
1983 /*
1984 Not sure that this is the best way to handle this, but it works(TM)...
1985 */
1986                         Point midpoint = Geometry::Midpoint((Line *)obj);
1987                         Vector v1 = Vector::Magnitude(midpoint, point);
1988
1989                         if ((v1.Magnitude() * Global::zoom) < 8.0)
1990                         {
1991                                 QString text = tr("Midpoint <%1, %2>");
1992                                 informativeText = text.arg(midpoint.x).arg(midpoint.y);
1993                                 hoverPointValid = true;
1994                                 hoverPoint = midpoint;
1995                                 needUpdate = true;
1996                         }
1997                 }
1998                 else if (obj->type == OTCircle)
1999                 {
2000                         if ((draggingObject && (dragged->type == OTLine)) && (dragged->hitPoint[0] || dragged->hitPoint[1]))
2001                         {
2002                                 Point p = (dragged->hitPoint[0] ? dragged->p[1] : dragged->p[0]);
2003                                 Geometry::FindTangents(obj, p);
2004
2005                                 if (Global::numIntersectPoints > 0)
2006                                 {
2007                                         hoveringIntersection = true;
2008                                         intersectionPoint = Geometry::NearestTo(point, Global::intersectPoint[0], Global::intersectPoint[1]);
2009                                 }
2010                         }
2011                         else if ((Global::tool == TTLine) && (Global::toolState == TSPoint2))
2012                         {
2013                                 Geometry::FindTangents(obj, toolPoint[0]);
2014
2015                                 if (Global::numIntersectPoints > 0)
2016                                 {
2017                                         hoveringIntersection = true;
2018                                         intersectionPoint = Geometry::NearestTo(point, Global::intersectPoint[0], Global::intersectPoint[1]);
2019                                 }
2020                         }
2021                 }
2022         }
2023
2024         // Handle object movement (left button down & over an object)
2025         if ((event->buttons() & Qt::LeftButton) && draggingObject && !Global::tool)
2026         {
2027                 if (hoveringIntersection)
2028                         point = intersectionPoint;
2029                 else if (hoverPointValid)
2030                         point = hoverPoint;
2031                 else if (Global::snapToGrid)
2032                         point = SnapPointToGrid(point);
2033
2034                 HandleObjectMovement(point);
2035                 update();
2036                 oldPoint = point;
2037                 return;
2038         }
2039
2040         // Do tool handling, if any are active...
2041         if (Global::tool)
2042         {
2043                 if (hoveringIntersection)
2044                         point = intersectionPoint;
2045                 else if (hoverPointValid)
2046                         point = hoverPoint;
2047                 else if (Global::snapToGrid)
2048                 {
2049                         if (angleSnap)
2050                                 point = SnapPointToAngle(point);
2051                         else
2052                                 point = SnapPointToGrid(point);
2053                 }
2054
2055                 ToolHandler(ToolMouseMove, point);
2056         }
2057
2058         // This is used to draw the tool crosshair...
2059         oldPoint = point;
2060
2061         if (needUpdate || Global::tool)
2062                 update();
2063 }
2064
2065 void DrawingView::mouseReleaseEvent(QMouseEvent * event)
2066 {
2067         if (event->button() == Qt::LeftButton)
2068         {
2069 //We need to update especially if nothing collided and the state needs to change. !!! FIX !!!
2070 //could set it up to use the document's update function (assumes that all object
2071 //updates are being reported correctly:
2072 //              if (document.NeedsUpdate())
2073                 // Do an update if collided with at least *one* object in the document
2074 //              if (collided)
2075                         update();
2076
2077                 if (Global::tool)
2078                 {
2079                         Vector point = Painter::QtToCartesianCoords(Vector(event->x(), event->y()));
2080                         ToolHandler(ToolMouseUp, point);
2081                         return;
2082                 }
2083
2084 //              if (Global::selectionInProgress)
2085                         Global::selectionInProgress = false;
2086
2087                 informativeText.clear();
2088 // Should we be doing this automagically? Hmm...
2089                 // Clear our vectors
2090 //              select.clear();
2091 ////            hover.clear();
2092
2093                 // Scoop 'em up (do we need to??? Seems we do, because keyboard movement uses it.  Also, tools use it too.  But we can move it out of here)
2094                 select = GetSelection();
2095
2096                 draggingObject = false;
2097         }
2098         else if (event->button() == Qt::MiddleButton)
2099         {
2100                 scrollDrag = false;
2101
2102                 if (Global::penStamp)
2103                         setCursor(curMarker);
2104                 else if (Global::penDropper)
2105                         setCursor(curDropper);
2106                 else
2107                         setCursor(Qt::ArrowCursor);
2108
2109                 // Need to convert this, since it's in Qt coordinates (for wheelEvent)
2110                 oldPoint = Painter::QtToCartesianCoords(oldPoint);
2111         }
2112 }
2113
2114 void DrawingView::wheelEvent(QWheelEvent * event)
2115 {
2116         double zoomFactor = 1.20;
2117         scrollWheelSeen = true;
2118
2119         if (event->angleDelta().y() < 0)
2120         {
2121                 if (Global::zoom > 400.0)
2122                         return;
2123
2124                 Global::zoom *= zoomFactor;
2125         }
2126         else
2127         {
2128                 if (Global::zoom < 0.125)
2129                         return;
2130
2131                 Global::zoom /= zoomFactor;
2132         }
2133
2134         Point np = Painter::QtToCartesianCoords(oldScrollPoint);
2135         Global::origin += (oldPoint - np);
2136
2137         emit(NeedZoomUpdate());
2138 }
2139
2140 void DrawingView::keyPressEvent(QKeyEvent * event)
2141 {
2142         bool oldShift = shiftDown;
2143         bool oldCtrl = ctrlDown;
2144         bool oldAlt = altDown;
2145
2146         if (event->key() == Qt::Key_Shift)
2147                 shiftDown = true;
2148         else if (event->key() == Qt::Key_Control)
2149                 ctrlDown = true;
2150         else if (event->key() == Qt::Key_Alt)
2151                 altDown = true;
2152
2153         // If there's a change in any of the modifier key states, pass it on to
2154         // the current tool's handler
2155         if ((oldShift != shiftDown) || (oldCtrl != ctrlDown))
2156         {
2157                 if (Global::tool)
2158                         ToolHandler(ToolKeyDown, Point(0, 0));
2159
2160                 update();
2161         }
2162
2163         if (oldAlt != altDown)
2164         {
2165                 scrollDrag = true;
2166                 setCursor(Qt::SizeAllCursor);
2167                 oldPoint = oldScrollPoint;
2168         }
2169
2170         if (select.size() > 0)
2171         {
2172                 if (event->key() == Qt::Key_Up)
2173                 {
2174                         TranslateObjects(select, Point(0, +1.0));
2175                         update();
2176                 }
2177                 else if (event->key() == Qt::Key_Down)
2178                 {
2179                         TranslateObjects(select, Point(0, -1.0));
2180                         update();
2181                 }
2182                 else if (event->key() == Qt::Key_Right)
2183                 {
2184                         TranslateObjects(select, Point(+1.0, 0));
2185                         update();
2186                 }
2187                 else if (event->key() == Qt::Key_Left)
2188                 {
2189                         TranslateObjects(select, Point(-1.0, 0));
2190                         update();
2191                 }
2192         }
2193 }
2194
2195 void DrawingView::keyReleaseEvent(QKeyEvent * event)
2196 {
2197         bool oldShift = shiftDown;
2198         bool oldCtrl = ctrlDown;
2199         bool oldAlt = altDown;
2200
2201         if (event->key() == Qt::Key_Shift)
2202                 shiftDown = false;
2203         else if (event->key() == Qt::Key_Control)
2204                 ctrlDown = false;
2205         else if (event->key() == Qt::Key_Alt)
2206                 altDown = false;
2207
2208         if ((oldShift != shiftDown) || (oldCtrl != ctrlDown))
2209         {
2210                 if (Global::tool)
2211                         ToolHandler(ToolKeyUp, Point(0, 0));
2212
2213                 update();
2214         }
2215
2216         if (oldAlt != altDown)
2217         {
2218                 scrollDrag = false;
2219
2220                 if (Global::penStamp)
2221                         setCursor(curMarker);
2222                 else if (Global::penDropper)
2223                         setCursor(curDropper);
2224                 else
2225                         setCursor(Qt::ArrowCursor);
2226         }
2227 }
2228
2229 //
2230 // This looks strange, but it's really quite simple: We want a point that's
2231 // more than half-way to the next grid point to snap there while conversely we
2232 // want a point that's less than half-way to to the next grid point then snap
2233 // to the one before it. So we add half of the grid spacing to the point, then
2234 // divide by it so that we can remove the fractional part, then multiply it
2235 // back to get back to the correct answer.
2236 //
2237 Point DrawingView::SnapPointToGrid(Point point)
2238 {
2239         point += Global::gridSpacing / 2.0;             // *This* adds to Z!!!
2240         point /= Global::gridSpacing;
2241         point.x = floor(point.x);//need to fix this for negative numbers...
2242         point.y = floor(point.y);
2243         point.z = 0;                                    // Make *sure* Z doesn't go anywhere!!!
2244         point *= Global::gridSpacing;
2245         return point;
2246 }
2247
2248 Point DrawingView::SnapPointToAngle(Point point)
2249 {
2250         // Snap to a single digit angle (using toolpoint #1 as the center)
2251         double angle = Vector::Angle(toolPoint[0], point);
2252         double length = Vector::Magnitude(toolPoint[0], point);
2253
2254         // Convert from radians to degrees
2255         double degAngle = angle * RADIANS_TO_DEGREES;
2256         double snapAngle = (double)((int)(degAngle + 0.5));
2257
2258         Vector v;
2259         v.SetAngleAndLength(snapAngle * DEGREES_TO_RADIANS, length);
2260         point = toolPoint[0] + v;
2261
2262         return point;
2263 }
2264
2265 Rect DrawingView::GetObjectExtents(Object * obj)
2266 {
2267         // Default to empty rect, if object checks below fail for some reason
2268         Rect rect;
2269
2270         switch (obj->type)
2271         {
2272         case OTLine:
2273         case OTDimension:
2274         {
2275                 rect = Rect(obj->p[0], obj->p[1]);
2276                 break;
2277         }
2278
2279         case OTCircle:
2280         {
2281                 rect = Rect(obj->p[0], obj->p[0]);
2282                 rect.Expand(obj->radius[0]);
2283                 break;
2284         }
2285
2286         case OTArc:
2287         {
2288                 Arc * a = (Arc *)obj;
2289
2290                 double start = a->angle[0];
2291                 double end = start + a->angle[1];
2292                 rect = Rect(Point(cos(start), sin(start)), Point(cos(end), sin(end)));
2293
2294                 // If the end of the arc is before the beginning, add 360 degrees to it
2295                 if (end < start)
2296                         end += TAU;
2297
2298                 // Adjust the bounds depending on which axes are crossed
2299                 if ((start < QTR_TAU) && (end > QTR_TAU))
2300                         rect.t = 1.0;
2301
2302                 if ((start < HALF_TAU) && (end > HALF_TAU))
2303                         rect.l = -1.0;
2304
2305                 if ((start < THREE_QTR_TAU) && (end > THREE_QTR_TAU))
2306                         rect.b = -1.0;
2307
2308                 if ((start < TAU) && (end > TAU))
2309                         rect.r = 1.0;
2310
2311                 if ((start < (TAU + QTR_TAU)) && (end > (TAU + QTR_TAU)))
2312                         rect.t = 1.0;
2313
2314                 if ((start < (TAU + HALF_TAU)) && (end > (TAU + HALF_TAU)))
2315                         rect.l = -1.0;
2316
2317                 if ((start < (TAU + THREE_QTR_TAU)) && (end > (TAU + THREE_QTR_TAU)))
2318                         rect.b = -1.0;
2319
2320                 rect *= a->radius[0];
2321                 rect.Translate(a->p[0]);
2322                 break;
2323         }
2324
2325         case OTText:
2326         {
2327                 Text * t = (Text *)obj;
2328                 rect = Rect(t->p[0], Point(t->p[0].x + t->extents.Width(), t->p[0].y - t->extents.Height()));
2329                 break;
2330         }
2331
2332         case OTContainer:
2333         {
2334                 Container * c = (Container *)obj;
2335                 VPVectorIter i = c->objects.begin();
2336                 rect = GetObjectExtents((Object *)*i);
2337                 i++;
2338
2339                 for(; i!=c->objects.end(); i++)
2340                         rect |= GetObjectExtents((Object *)*i);
2341         }
2342
2343         default:
2344                 break;
2345         }
2346
2347         return rect;
2348 }
2349
2350 void DrawingView::CheckObjectBounds(void)
2351 {
2352         VPVectorIter i;
2353
2354         for(i=document.objects.begin(); i!=document.objects.end(); i++)
2355         {
2356                 Object * obj = (Object *)(*i);
2357                 obj->selected = false;
2358
2359                 switch (obj->type)
2360                 {
2361                 case OTLine:
2362                 case OTDimension: // N.B.: We don't check this properly...
2363                 {
2364                         Line * l = (Line *)obj;
2365
2366                         if (Global::selection.contains(l->p[0].x, l->p[0].y) && Global::selection.contains(l->p[1].x, l->p[1].y))
2367                                 l->selected = true;
2368
2369                         break;
2370                 }
2371
2372                 case OTCircle:
2373                 {
2374                         Circle * c = (Circle *)obj;
2375
2376                         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]))
2377                                 c->selected = true;
2378
2379                         break;
2380                 }
2381
2382                 case OTArc:
2383                 {
2384                         Arc * a = (Arc *)obj;
2385
2386                         double start = a->angle[0];
2387                         double end = start + a->angle[1];
2388                         QPointF p1(cos(start), sin(start));
2389                         QPointF p2(cos(end), sin(end));
2390                         QRectF bounds(p1, p2);
2391
2392 #if 1
2393                         // Swap X/Y coordinates if they're backwards...
2394                         if (bounds.left() > bounds.right())
2395                         {
2396                                 double temp = bounds.left();
2397                                 bounds.setLeft(bounds.right());
2398                                 bounds.setRight(temp);
2399                         }
2400
2401                         if (bounds.bottom() > bounds.top())
2402                         {
2403                                 double temp = bounds.bottom();
2404                                 bounds.setBottom(bounds.top());
2405                                 bounds.setTop(temp);
2406                         }
2407 #else
2408                         // Doesn't work as advertised! For shame!
2409                         bounds = bounds.normalized();
2410 #endif
2411
2412                         // If the end of the arc is before the beginning, add 360 degrees
2413                         // to it
2414                         if (end < start)
2415                                 end += TAU;
2416
2417                         // Adjust the bounds depending on which axes are crossed
2418                         if ((start < QTR_TAU) && (end > QTR_TAU))
2419                                 bounds.setTop(1.0);
2420
2421                         if ((start < HALF_TAU) && (end > HALF_TAU))
2422                                 bounds.setLeft(-1.0);
2423
2424                         if ((start < THREE_QTR_TAU) && (end > THREE_QTR_TAU))
2425                                 bounds.setBottom(-1.0);
2426
2427                         if ((start < TAU) && (end > TAU))
2428                                 bounds.setRight(1.0);
2429
2430                         if ((start < (TAU + QTR_TAU)) && (end > (TAU + QTR_TAU)))
2431                                 bounds.setTop(1.0);
2432
2433                         if ((start < (TAU + HALF_TAU)) && (end > (TAU + HALF_TAU)))
2434                                 bounds.setLeft(-1.0);
2435
2436                         if ((start < (TAU + THREE_QTR_TAU)) && (end > (TAU + THREE_QTR_TAU)))
2437                                 bounds.setBottom(-1.0);
2438
2439                         bounds.setTopLeft(QPointF(bounds.left() * a->radius[0], bounds.top() * a->radius[0]));
2440                         bounds.setBottomRight(QPointF(bounds.right() * a->radius[0], bounds.bottom() * a->radius[0]));
2441                         bounds.translate(a->p[0].x, a->p[0].y);
2442
2443                         if (Global::selection.contains(bounds))
2444                                 a->selected = true;
2445
2446                         break;
2447                 }
2448
2449                 case OTText:
2450                 {
2451                         Text * t = (Text *)obj;
2452                         Rect r(obj->p[0], Point(t->p[0].x + t->extents.Width(), t->p[0].y - t->extents.Height()));
2453
2454                         if (Global::selection.contains(r.l, r.t) && Global::selection.contains(r.r, r.b))
2455                                 t->selected = true;
2456
2457                         break;
2458                 }
2459
2460                 case OTContainer:
2461                 {
2462                         Container * c = (Container *)obj;
2463
2464                         if (Global::selection.contains(c->p[0].x, c->p[0].y) && Global::selection.contains(c->p[1].x, c->p[1].y))
2465                                 c->selected = true;
2466
2467                         break;
2468                 }
2469
2470 //              default:
2471 //                      break;
2472                 }
2473         }
2474 }
2475
2476 bool DrawingView::HitTestObjects(Point point)
2477 {
2478         VPVectorIter i;
2479         numHovered = 0;
2480         bool needUpdate = false;
2481         hoverPointValid = false;
2482
2483         for(i=document.objects.begin(); i!=document.objects.end(); i++)
2484         {
2485                 Object * obj = (Object *)(*i);
2486
2487                 // If we're seeing the object we're dragging, skip it
2488                 if (draggingObject && (obj == dragged))
2489                         continue;
2490
2491                 if (HitTest(obj, point) == true)
2492                         needUpdate = true;
2493
2494                 if (obj->hovered)
2495                 {
2496                         numHovered++;
2497 //printf("MouseMove: OBJECT HOVERED (numHovered = %i)\n", numHovered);
2498                         emit ObjectHovered(obj);
2499                 }
2500         }
2501
2502         return needUpdate;
2503 }
2504
2505 bool DrawingView::HitTest(Object * obj, Point point)
2506 {
2507         bool needUpdate = false;
2508
2509         switch (obj->type)
2510         {
2511         case OTLine:
2512         {
2513                 bool oldHP0 = obj->hitPoint[0], oldHP1 = obj->hitPoint[1], oldHO = obj->hitObject;
2514                 obj->hitPoint[0] = obj->hitPoint[1] = obj->hitObject = false;
2515                 Vector lineSegment = obj->p[1] - obj->p[0];
2516                 Vector v1 = point - obj->p[0];
2517                 Vector v2 = point - obj->p[1];
2518                 double t = Geometry::ParameterOfLineAndPoint(obj->p[0], obj->p[1], point);
2519                 double distance;
2520
2521                 if (t < 0.0)
2522                         distance = v1.Magnitude();
2523                 else if (t > 1.0)
2524                         distance = v2.Magnitude();
2525                 else
2526                         // distance = ?Det?(ls, v1) / |ls|
2527                         distance = fabs((lineSegment.x * v1.y - v1.x * lineSegment.y)
2528                                 / lineSegment.Magnitude());
2529
2530                 if ((v1.Magnitude() * Global::zoom) < 8.0)
2531                 {
2532                         obj->hitPoint[0] = true;
2533                         hoverPoint = obj->p[0];
2534                         hoverPointValid = true;
2535                 }
2536                 else if ((v2.Magnitude() * Global::zoom) < 8.0)
2537                 {
2538                         obj->hitPoint[1] = true;
2539                         hoverPoint = obj->p[1];
2540                         hoverPointValid = true;
2541                 }
2542                 else if ((distance * Global::zoom) < 5.0)
2543                         obj->hitObject = true;
2544
2545                 obj->hovered = (obj->hitPoint[0] || obj->hitPoint[1] || obj->hitObject ? true : false);
2546
2547                 if ((oldHP0 != obj->hitPoint[0]) || (oldHP1 != obj->hitPoint[1]) || (oldHO != obj->hitObject))
2548                         needUpdate = true;
2549
2550                 break;
2551         }
2552
2553         case OTCircle:
2554         {
2555                 bool oldHP = obj->hitPoint[0], oldHO = obj->hitObject;
2556                 obj->hitPoint[0] = obj->hitObject = false;
2557                 double length = Vector::Magnitude(obj->p[0], point);
2558
2559                 if ((length * Global::zoom) < 8.0)
2560                 {
2561                         obj->hitPoint[0] = true;
2562                         hoverPoint = obj->p[0];
2563                         hoverPointValid = true;
2564                 }
2565                 else if ((fabs(length - obj->radius[0]) * Global::zoom) < 2.0)
2566                         obj->hitObject = true;
2567
2568                 obj->hovered = (obj->hitPoint[0] || obj->hitObject ? true : false);
2569
2570                 if ((oldHP != obj->hitPoint[0]) || (oldHO != obj->hitObject))
2571                         needUpdate = true;
2572
2573                 break;
2574         }
2575
2576         case OTArc:
2577         {
2578                 bool oldHP0 = obj->hitPoint[0], oldHP1 = obj->hitPoint[1], oldHP2 = obj->hitPoint[2], oldHO = obj->hitObject;
2579                 obj->hitPoint[0] = obj->hitPoint[1] = obj->hitPoint[2] = obj->hitObject = false;
2580                 double length = Vector::Magnitude(obj->p[0], point);
2581                 double angle = Vector::Angle(obj->p[0], point);
2582
2583                 // Make sure we get the angle in the correct spot
2584                 if (angle < obj->angle[0])
2585                         angle += TAU;
2586
2587                 // Get the span that we're pointing at...
2588                 double span = angle - obj->angle[0];
2589
2590                 // N.B.: Still need to hit test the arc start & arc span handles...
2591                 double spanAngle = obj->angle[0] + obj->angle[1];
2592                 Point handle1 = obj->p[0] + (Vector(cos(obj->angle[0]), sin(obj->angle[0])) * obj->radius[0]);
2593                 Point handle2 = obj->p[0] + (Vector(cos(spanAngle), sin(spanAngle)) * obj->radius[0]);
2594                 double length2 = Vector::Magnitude(point, handle1);
2595                 double length3 = Vector::Magnitude(point, handle2);
2596
2597                 if ((length * Global::zoom) < 8.0)
2598                 {
2599                         obj->hitPoint[0] = true;
2600                         hoverPoint = obj->p[0];
2601                         hoverPointValid = true;
2602                 }
2603                 else if ((length2 * Global::zoom) < 8.0)
2604                 {
2605                         obj->hitPoint[1] = true;
2606                         hoverPoint = handle1;
2607                         hoverPointValid = true;
2608                 }
2609                 else if ((length3 * Global::zoom) < 8.0)
2610                 {
2611                         obj->hitPoint[2] = true;
2612                         hoverPoint = handle2;
2613                         hoverPointValid = true;
2614                 }
2615                 else if (((fabs(length - obj->radius[0]) * Global::zoom) < 2.0) && (span < obj->angle[1]))
2616                         obj->hitObject = true;
2617
2618                 obj->hovered = (obj->hitPoint[0] || obj->hitPoint[1] || obj->hitPoint[2] || obj->hitObject ? true : false);
2619
2620                 if ((oldHP0 != obj->hitPoint[0]) || (oldHP1 != obj->hitPoint[1]) || (oldHP2 != obj->hitPoint[2]) || (oldHO != obj->hitObject))
2621                         needUpdate = true;
2622
2623                 break;
2624         }
2625
2626         case OTDimension:
2627         {
2628                 bool oldHP0 = obj->hitPoint[0], oldHP1 = obj->hitPoint[1], oldHP2 = obj->hitPoint[2], oldHP3 = obj->hitPoint[3], oldHP4 = obj->hitPoint[4], oldHO = obj->hitObject;
2629                 obj->hitPoint[0] = obj->hitPoint[1] = obj->hitPoint[2] = obj->hitPoint[3] = obj->hitPoint[4] = obj->hitObject = false;
2630
2631                 Dimension * d = (Dimension *)obj;
2632
2633                 Vector orthogonal = Vector::Normal(d->lp[0], d->lp[1]);
2634                 // Get our line parallel to our points
2635                 float scaledThickness = Global::scale * obj->thickness;
2636 #if 1
2637                 Point p1 = d->lp[0] + (orthogonal * (d->offset + (10.0 * scaledThickness)));
2638                 Point p2 = d->lp[1] + (orthogonal * (d->offset + (10.0 * scaledThickness)));
2639 #else
2640                 Point p1 = d->lp[0] + (orthogonal * (10.0 + d->offset) * scaledThickness);
2641                 Point p2 = d->lp[1] + (orthogonal * (10.0 + d->offset) * scaledThickness);
2642 #endif
2643                 Point p3(p1, point);
2644
2645                 Vector v1(d->p[0], point);
2646                 Vector v2(d->p[1], point);
2647                 Vector lineSegment(p1, p2);
2648                 double t = Geometry::ParameterOfLineAndPoint(p1, p2, point);
2649                 double distance;
2650                 Point midpoint = (p1 + p2) / 2.0;
2651                 Point hFSPoint = Point(midpoint, point);
2652                 Point hCS1Point = Point((p1 + midpoint) / 2.0, point);
2653                 Point hCS2Point = Point((midpoint + p2) / 2.0, point);
2654
2655                 if (t < 0.0)
2656                         distance = v1.Magnitude();
2657                 else if (t > 1.0)
2658                         distance = v2.Magnitude();
2659                 else
2660                         // distance = ?Det?(ls, v1) / |ls|
2661                         distance = fabs((lineSegment.x * p3.y - p3.x * lineSegment.y)
2662                                 / lineSegment.Magnitude());
2663
2664                 if ((v1.Magnitude() * Global::zoom) < 8.0)
2665                         obj->hitPoint[0] = true;
2666                 else if ((v2.Magnitude() * Global::zoom) < 8.0)
2667                         obj->hitPoint[1] = true;
2668                 else if ((distance * Global::zoom) < 5.0)
2669                         obj->hitObject = true;
2670
2671                 if ((hFSPoint.Magnitude() * Global::zoom) < 8.0)
2672                         obj->hitPoint[2] = true;
2673                 else if ((hCS1Point.Magnitude() * Global::zoom) < 8.0)
2674                         obj->hitPoint[3] = true;
2675                 else if ((hCS2Point.Magnitude() * Global::zoom) < 8.0)
2676                         obj->hitPoint[4] = true;
2677
2678                 obj->hovered = (obj->hitPoint[0] || obj->hitPoint[1] || obj->hitPoint[2] || obj->hitPoint[3] || obj->hitPoint[4] || obj->hitObject ? true : false);
2679
2680                 if ((oldHP0 != obj->hitPoint[0]) || (oldHP1 != obj->hitPoint[1]) || (oldHP2 != obj->hitPoint[2]) || (oldHP3 != obj->hitPoint[3]) || (oldHP4 != obj->hitPoint[4]) || (oldHO != obj->hitObject))
2681                         needUpdate = true;
2682
2683                 break;
2684         }
2685
2686         case OTText:
2687         {
2688                 Text * t = (Text *)obj;
2689                 bool oldHO = obj->hitObject;
2690                 obj->hitObject = false;
2691
2692                 Rect r(obj->p[0], Point(obj->p[0].x + t->extents.Width(), obj->p[0].y - t->extents.Height()));
2693 //printf("Text: p=<%lf, %lf>, w/h=%lf, %lf [lrtb=%lf, %lf, %lf, %lf]\n", obj->p[0].x, obj->p[0].y, t->extents.Width(), t->extents.Height(), t->extents.l, t->extents.r, t->extents.t, t->extents.b);
2694
2695                 if (r.Contains(point))
2696                         obj->hitObject = true;
2697
2698                 obj->hovered = (obj->hitObject ? true : false);
2699
2700                 if (oldHO != obj->hitObject)
2701                         needUpdate = true;
2702
2703                 break;
2704         }
2705
2706         case OTContainer:
2707         {
2708                 // Containers must be recursively tested...  Or do they???
2709 /*
2710 So the idea here is to flatten the structure, *then* test the objects within.  Flattening includes the Containers as well; we can do this because it's pointers all the way down.
2711 */
2712 //              bool oldHitObj = c->hitObject, oldHovered = c->hovered;
2713 //              Object * oldClicked = c->clicked;
2714 /*
2715 still need to compare old state to new state, and set things up based upon that...
2716 likely we can just rely on the object itself and steal its state like we have in the commented out portion below; can prolly rewrite the HitTest() portion to be one line: needUpdate = HitTest(cObj, point);
2717 Well, you could if there was only one object in the Container.  But since there isn't, we have to keep the if HitTest() == true then needUpdate = true bit.  Because otherwise, a false result anywhere will kill the needed update elsewhere.
2718 */
2719                 Container * c = (Container *)obj;
2720                 c->hitObject = false;
2721                 c->hovered = false;
2722                 c->clicked = NULL;
2723
2724                 VPVector flat = Flatten(c);
2725
2726 //printf("HitTest::OTContainer (size=%li)\n", flat.size());
2727                 for(VPVectorIter i=flat.begin(); i!=flat.end(); i++)
2728                 {
2729                         Object * cObj = (Object *)(*i);
2730
2731                         // Skip the flattened containers (if any)...
2732                         if (cObj->type == OTContainer)
2733                                 continue;
2734
2735                         // We do it this way instead of needUpdate = HitTest() because we
2736                         // are checking more than one object, and that way of doing will
2737                         // not return consistent results.
2738                         if (HitTest(cObj, point) == true)
2739 //                      {
2740 //printf("HitTest::OTContainer, subobj ($%llX) hit!\n", cObj);
2741                                 needUpdate = true;
2742 //                              c->hitObject = true;
2743 //                              c->clicked = cObj;
2744 //                              c->hovered = true;
2745 //                      }
2746
2747                         // Same reasons for doing it this way here apply.
2748                         if (cObj->hitObject == true)
2749                         {
2750 //printf("HitTest::cObj->hitObject == true! ($%llX)\n", cObj);
2751                                 c->hitObject = true;
2752                                 c->clicked = cObj;
2753                         }//*/
2754
2755                         if (cObj->hitPoint[0] == true)
2756                         {
2757 //printf("HitTest::cObj->hitObject == true! ($%llX)\n", cObj);
2758                                 c->hitPoint[0] = true;
2759                                 c->clicked = cObj;
2760                         }//*/
2761
2762                         if (cObj->hitPoint[1] == true)
2763                         {
2764 //printf("HitTest::cObj->hitObject == true! ($%llX)\n", cObj);
2765                                 c->hitPoint[1] = true;
2766                                 c->clicked = cObj;
2767                         }//*/
2768
2769                         if (cObj->hitPoint[2] == true)
2770                         {
2771 //printf("HitTest::cObj->hitObject == true! ($%llX)\n", cObj);
2772                                 c->hitPoint[2] = true;
2773                                 c->clicked = cObj;
2774                         }//*/
2775
2776                         if (cObj->hovered == true)
2777                                 c->hovered = true;//*/
2778                 }
2779
2780                 break;
2781         }
2782
2783         default:
2784                 break;
2785         }
2786
2787         return needUpdate;
2788 }
2789
2790 bool DrawingView::HandleObjectClicked(void)
2791 {
2792         if (dragged->type == OTDimension)
2793         {
2794                 Dimension * d = (Dimension *)dragged;
2795
2796                 if (d->hitPoint[2])
2797                 {
2798                         // Hit the "flip sides" switch, so flip 'em
2799                         Point temp = d->p[0];
2800                         d->p[0] = d->p[1];
2801                         d->p[1] = temp;
2802                         return true;
2803                 }
2804                 else if (d->hitPoint[3])
2805                 {
2806                         // There are three cases here: aligned, horizontal, & vertical.
2807                         // Aligned and horizontal do the same thing, vertical goes back to
2808                         // linear.
2809                         if (d->subtype == DTLinearVert)
2810                                 d->subtype = DTLinear;
2811                         else
2812                                 d->subtype = DTLinearVert;
2813
2814                         return true;
2815                 }
2816                 else if (d->hitPoint[4])
2817                 {
2818                         // There are three cases here: aligned, horizontal, & vertical.
2819                         // Aligned and vertical do the same thing, horizontal goes back to
2820                         // linear.
2821                         if (d->subtype == DTLinearHorz)
2822                                 d->subtype = DTLinear;
2823                         else
2824                                 d->subtype = DTLinearHorz;
2825
2826                         return true;
2827                 }
2828         }
2829
2830         return false;
2831 }
2832
2833 void DrawingView::HandleObjectMovement(Point point)
2834 {
2835         Point delta = point - oldPoint;
2836 //printf("HOM: old = (%f,%f), new = (%f, %f), delta = (%f, %f)\n", oldPoint.x, oldPoint.y, point.x, point.y, delta.x, delta.y);
2837 //      Object * obj = (Object *)hover[0];
2838         Object * obj = dragged;
2839 //printf("Object type = %i (size=%i), ", obj->type, hover.size());
2840 //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"));
2841
2842         switch (obj->type)
2843         {
2844         case OTLine:
2845                 if (obj->hitPoint[0])
2846                 {
2847                         if (Global::fixedLength)
2848                         {
2849                                 Vector line = point - obj->p[1];
2850                                 Vector unit = line.Unit();
2851                                 point = obj->p[1] + (unit * obj->length);
2852                         }
2853
2854                         obj->p[0] = point;
2855                 }
2856                 else if (obj->hitPoint[1])
2857                 {
2858                         if (Global::fixedLength)
2859                         {
2860                                 Vector line = point - obj->p[0];
2861                                 Vector unit = line.Unit();
2862                                 point = obj->p[0] + (unit * obj->length);
2863                         }
2864
2865                         obj->p[1] = point;
2866                 }
2867                 else if (obj->hitObject)
2868                 {
2869                         obj->p[0] += delta;
2870                         obj->p[1] += delta;
2871                 }
2872
2873                 break;
2874
2875         case OTCircle:
2876                 if (obj->hitPoint[0])
2877                         obj->p[0] = point;
2878                 else if (obj->hitObject)
2879                 {
2880                         double oldRadius = obj->length;
2881                         obj->radius[0] = Vector::Magnitude(obj->p[0], point);
2882
2883                         QString text = QObject::tr("Radius: %1\nScale: %2%");
2884                         informativeText = text.arg(obj->radius[0], 0, 'd', 4).arg(obj->radius[0] / oldRadius * 100.0, 0, 'd', 0);
2885                 }
2886
2887                 break;
2888
2889         case OTArc:
2890                 if (obj->hitPoint[0])
2891                         obj->p[0] = point;
2892                 else if (obj->hitPoint[1])
2893                 {
2894                         // Change the Arc's span (handle #1)
2895                         if (shiftDown)
2896                         {
2897                                 double angle = Vector::Angle(obj->p[0], point);
2898                                 double delta = angle - obj->angle[0];
2899
2900                                 if (delta < 0)
2901                                         delta += TAU;
2902
2903                                 obj->angle[1] -= delta;
2904                                 obj->angle[0] = angle;
2905
2906                                 if (obj->angle[1] < 0)
2907                                         obj->angle[1] += TAU;
2908
2909                                 QString text = QObject::tr("Span: %1") + QChar(0x00B0) + QObject::tr("\n%2") + QChar(0x00B0) + QObject::tr(" - %3") + QChar(0x00B0);
2910                                 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);
2911                                 return;
2912                         }
2913
2914                         double angle = Vector::Angle(obj->p[0], point);
2915                         obj->angle[0] = angle;
2916                         QString text = QObject::tr("Start angle: %1") + QChar(0x00B0);
2917                         informativeText = text.arg(obj->angle[0] * RADIANS_TO_DEGREES, 0, 'd', 4);
2918                 }
2919                 else if (obj->hitPoint[2])
2920                 {
2921                         // Change the Arc's span (handle #2)
2922                         if (shiftDown)
2923                         {
2924                                 double angle = Vector::Angle(obj->p[0], point);
2925                                 obj->angle[1] = angle - obj->angle[0];
2926
2927                                 if (obj->angle[1] < 0)
2928                                         obj->angle[1] += TAU;
2929
2930                                 QString text = QObject::tr("Span: %1") + QChar(0x00B0) + QObject::tr("\n%2") + QChar(0x00B0) + QObject::tr(" - %3") + QChar(0x00B0);
2931                                 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);
2932                                 return;
2933                         }
2934
2935                         double angle = Vector::Angle(obj->p[0], point);
2936                         obj->angle[0] = angle - obj->angle[1];
2937
2938                         if (obj->angle[0] < 0)
2939                                 obj->angle[0] += TAU;
2940
2941                         QString text = QObject::tr("End angle: %1") + QChar(0x00B0);
2942                         informativeText = text.arg((obj->angle[0] + obj->angle[1]) * RADIANS_TO_DEGREES, 0, 'd', 4);
2943                 }
2944                 else if (obj->hitObject)
2945                 {
2946                         if (shiftDown)
2947                         {
2948                                 return;
2949                         }
2950
2951                         obj->radius[0] = Vector::Magnitude(obj->p[0], point);
2952                         QString text = QObject::tr("Radius: %1");
2953                         informativeText = text.arg(obj->radius[0], 0, 'd', 4);
2954                 }
2955
2956                 break;
2957
2958         case OTDimension:
2959                 if (obj->hitPoint[0])
2960                         obj->p[0] = point;
2961                 else if (obj->hitPoint[1])
2962                         obj->p[1] = point;
2963                 else if (obj->hitObject)
2964                 {
2965                         // Move measurement lines in/out
2966                         if (shiftDown)
2967                         {
2968                                 Dimension * d = (Dimension *)obj;
2969                                 double dist = Geometry::DistanceToLineFromPoint(d->lp[0], d->lp[1], point);
2970                                 float scaledThickness = Global::scale * obj->thickness;
2971                                 // Looks like offset is 0 to +MAX, but line is at 10.0.  So
2972                                 // anything less than 10.0 should set the offset to 0.
2973                                 d->offset = 0;
2974
2975                                 if (dist > (10.0 * scaledThickness))
2976                                         d->offset = dist - (10.0 * scaledThickness);
2977                         }
2978                         else
2979                         {
2980                                 obj->p[0] += delta;
2981                                 obj->p[1] += delta;
2982                         }
2983                 }
2984
2985                 break;
2986
2987         case OTText:
2988                 if (obj->hitObject)
2989                         obj->p[0] += delta;
2990
2991                 break;
2992
2993         case OTContainer:
2994                 // This is shitty, but works for now until I can code up something
2995                 // nicer :-)
2996 /*
2997 The idea is to make it so whichever point on the object in question is being dragged becomes the snap point for the container; shouldn't be too difficult to figure out how to do this.
2998 */
2999 //              TranslateObject(obj, delta);
3000                 TranslateContainer((Container *)obj, point, delta);
3001
3002                 break;
3003         default:
3004                 break;
3005         }
3006 }
3007
3008 void DrawingView::AddDimensionTo(void * o)
3009 {
3010         Object * obj = (Object *)o;
3011
3012         switch (obj->type)
3013         {
3014         case OTLine:
3015                 document.Add(new Dimension(obj->p[0], obj->p[1]));
3016                 break;
3017         case OTCircle:
3018                 break;
3019         case OTEllipse:
3020                 break;
3021         case OTArc:
3022                 break;
3023         case OTSpline:
3024                 break;
3025         }
3026 }