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