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