]> Shamusworld >> Repos - architektonas/blob - src/drawingview.cpp
Added ability to manipulate Text objects.
[architektonas] / src / drawingview.cpp
1 // drawingview.cpp
2 //
3 // Part of the Architektonas Project
4 // (C) 2011 Underground Software
5 // See the README and GPLv3 files for licensing and warranty information
6 //
7 // JLH = James Hammons <jlhamm@acm.org>
8 //
9 // Who  When        What
10 // ---  ----------  ------------------------------------------------------------
11 // JLH  03/22/2011  Created this file
12 // JLH  09/29/2011  Added middle mouse button panning
13 //
14
15 // FIXED:
16 //
17 // - Redo rendering code to *not* use Qt's transform functions, as they are tied
18 //   to a left-handed system and we need a right-handed one. [DONE]
19 //
20 // STILL TO BE DONE:
21 //
22 // - Lots of stuff
23 // - Layer locking (hiding works)
24 //
25
26 // Uncomment this for debugging...
27 //#define DEBUG
28 //#define DEBUGFOO                              // Various tool debugging...
29 //#define DEBUGTP                               // Toolpalette debugging...
30
31 #include "drawingview.h"
32
33 #include <stdint.h>
34 #include "geometry.h"
35 #include "global.h"
36 #include "mathconstants.h"
37 #include "painter.h"
38 #include "structs.h"
39 #include "utils.h"
40
41
42 #define BACKGROUND_MAX_SIZE     512
43
44
45 DrawingView::DrawingView(QWidget * parent/*= NULL*/): QWidget(parent),
46         // The value in the settings file will override this.
47         useAntialiasing(true), /*numSelected(0),*/ numHovered(0), shiftDown(false),
48         ctrlDown(false),
49         gridBackground(BACKGROUND_MAX_SIZE, BACKGROUND_MAX_SIZE),
50         scale(1.0), offsetX(-10), offsetY(-10), document(true),
51         gridPixels(0), collided(false), hoveringIntersection(false),
52         dragged(NULL), draggingObject(false), angleSnap(false)
53 {
54 //wtf? doesn't work except in c++11???  document = { 0 };
55         setBackgroundRole(QPalette::Base);
56         setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
57
58         Global::gridSpacing = 12.0;             // In base units (inch is default)
59 #if 0
60         Line * line = new Line(Vector(5, 5), Vector(50, 40), &document);
61         document.Add(line);
62         document.Add(new Line(Vector(50, 40), Vector(10, 83), &document));
63         document.Add(new Line(Vector(10, 83), Vector(17, 2), &document));
64         document.Add(new Circle(Vector(100, 100), 36, &document));
65         document.Add(new Circle(Vector(50, 150), 49, &document));
66         document.Add(new Arc(Vector(300, 300), 32, PI / 4.0, PI * 1.3, &document)),
67         document.Add(new Arc(Vector(200, 200), 60, PI / 2.0, PI * 1.5, &document));
68 #if 1
69         Dimension * dimension = new Dimension(Vector(0, 0), Vector(0, 0), DTLinear, &document);
70         line->SetDimensionOnLine(dimension);
71         document.Add(dimension);
72 #else
73         // Alternate way to do the above...
74         line->SetDimensionOnLine();
75 #endif
76 #else
77         Line * line = new Line;//(Vector(5, 5), Vector(50, 40), &document);
78         line->p[0] = Vector(5, 5);
79         line->p[1] = Vector(50, 40);
80         line->type = OTLine;
81         line->thickness = 2.0;
82         line->style = LSDash;
83         line->color = 0xFF7F00;
84         line->layer = 0;
85         document.objects.push_back(line);
86         document.objects.push_back(new Line(Vector(50, 40), Vector(10, 83)));
87         document.objects.push_back(new Line(Vector(10, 83), Vector(17, 2)));
88         document.objects.push_back(new Circle(Vector(100, 100), 36));
89         document.objects.push_back(new Circle(Vector(50, 150), 49));
90         document.objects.push_back(new Arc(Vector(300, 300), 32, TAU / 8.0, TAU * 0.65)),
91         document.objects.push_back(new Arc(Vector(200, 200), 60, TAU / 4.0, TAU * 0.75));
92         document.objects.push_back(new Dimension(Vector(50, 40), Vector(5, 5)));
93         document.objects.push_back(new Text(Vector(10, 83), "Here is some awesome text!"));
94 #endif
95
96 /*
97 Here we set the grid size in pixels--12 in this case. Initially, we have our
98 zoom set to make this represent 12 inches at a zoom factor of 25%. (This is
99 arbitrary.) So, to be able to decouple the grid size from the zoom, we need
100 to be able to set the size of the background grid (which we do here at an
101 arbitrary 12 pixels) to anything we want (within reason, of course :-).
102
103 The drawing enforces the grid spacing through the drawing->gridSpacing variable.
104
105         drawing->gridSpacing = 12.0 / Global::zoom;
106
107 Global::zoom is the zoom factor for the drawing, and all mouse clicks are
108 translated to Cartesian coordinates through this. (Initially, Global::zoom is
109 set to 1.0. SCREEN_ZOOM is set to 1.0/4.0.)
110
111 Really, the 100% zoom level can be set at *any* zoom level, it's more of a
112 convenience function than any measure of absolutes. Doing things that way we
113 could rid ourselves of the whole SCREEN_ZOOM parameter and all the attendant
114 shittiness that comes with it.
115
116 However, it seems that SCREEN_ZOOM is used to make text and arrow sizes show up
117 a certain way, which means we should probably create something else in those
118 objects to take its place--like some kind of scale factor. This would seem to
119 imply that certain point sizes actually *do* tie things like fonts to absolute
120 sizes on the screen, but not necessarily because you could have an inch scale
121 with text that is quite small relative to other objects on the screen, which
122 currently you have to zoom in to see (and which blows up the text). Point sizes
123 in an application like this are a bit meaningless; even though an inch is an
124 inch regardless of the zoom level a piece of text can be larger or smaller than
125 this. Maybe this is the case for having a base unit and basing point sizes off
126 of that.
127
128 Here's what's been figured out. Global::zoom is simply the ratio of pixels to
129 base units. What that means is that if you have a 12px grid with a 6" grid size
130 (& base unit of "inches"), Global::zoom becomes 12px / 6" = 2.0 px/in.
131
132 Dimensions now have a "size" parameter to set their absolute size in relation
133 to the base unit. ATM, the arrows are drawn in pixels, but also scaled by
134 Global::zoom *and* size. Same with the dimension text; it's drawn at 10pt and
135 scaled the same way as the arrowheads.
136
137 Need a way to scale line widths as well. :-/ Shouldn't be too difficult, just
138 need a thickness parameter similar to the "size" param for dimensions. (And now
139 we do! :-)
140
141 */
142         SetGridSize(12);        // This is in pixels
143 }
144
145
146 void DrawingView::SetGridSize(uint32_t size)
147 {
148         // Sanity check
149         if (size == gridPixels)
150                 return;
151
152         // Recreate the background bitmap
153         gridPixels = size;
154         QPainter pmp(&gridBackground);
155         pmp.fillRect(0, 0, BACKGROUND_MAX_SIZE, BACKGROUND_MAX_SIZE, QColor(240, 240, 240));
156         pmp.setPen(QPen(QColor(210, 210, 255), 2.0, Qt::SolidLine));
157
158         for(int i=0; i<(BACKGROUND_MAX_SIZE-1); i+=gridPixels)
159         {
160                 pmp.drawLine(i, 0, i, BACKGROUND_MAX_SIZE - 1);
161                 pmp.drawLine(0, i, BACKGROUND_MAX_SIZE - 1, i);
162         }
163
164         pmp.end();
165
166         // Set up new BG brush & zoom level (pixels per base unit)
167         Global::zoom = gridPixels / Global::gridSpacing;
168         UpdateGridBackground();
169 }
170
171
172 void DrawingView::UpdateGridBackground(void)
173 {
174         // Transform the origin to Qt coordinates
175         Vector pixmapOrigin = Painter::CartesianToQtCoords(Vector());
176         int x = (int)pixmapOrigin.x;
177         int y = (int)pixmapOrigin.y;
178         // Use mod arithmetic to grab the correct swatch of background
179 /*
180 Negative numbers still screw it up... Need to think about what we're
181 trying to do here. The fact that it worked with 72 seems to have been pure luck.
182 It seems the problem is negative numbers: We can't let that happen.
183 When taking away the zero, it pops over 1 px at zero, then goes about 1/2 a
184 grid at x<0.
185
186 The bitmap looks like this:
187
188 +---+---+---+---+---
189 |   |   |   |   |
190 |   |   |   |   |
191 +---+---+---+---+---
192 |   |   |   |   |
193 |   |   |   |   |
194 |   |   |   |   |
195
196 @ x = 1, we want it to look like:
197
198 -+---+---+---+---+---
199  |   |   |   |   |
200  |   |   |   |   |
201 -+---+---+---+---+---
202  |   |   |   |   |
203  |   |   |   |   |
204  |   |   |   |   |
205
206 Which means we need to grab the sample from x = 3. @ x = -1:
207
208 ---+---+---+---+---
209    |   |   |   |
210    |   |   |   |
211 ---+---+---+---+---
212    |   |   |   |
213    |   |   |   |
214    |   |   |   |
215
216 Which means we need to grab the sample from x = 1. Which means we have to take
217 the mirror of the modulus of gridPixels.
218
219 Doing a mod of a negative number is problematic: 1st, the compiler converts the
220 negative number to an unsigned int, then it does the mod. Gets you wrong answers
221 most of the time, unless you use a power of 2. :-P So what we do here is just
222 take the modulus of the negation, which means we don't have to worry about
223 mirroring it later.
224
225 The positive case looks gruesome (and it is) but it boils down to this: We take
226 the modulus of the X coordinate, then mirror it by subtraction from the
227 maximum (in this case, gridPixels). This gives us a number in the range of 1 to
228 gridPixels. But we need the case where the result equalling gridPixels to be
229 zero; so we do another modulus operation on the result to achieve this.
230 */
231         if (x < 0)
232                 x = -x % gridPixels;
233         else
234                 x = (gridPixels - (x % gridPixels)) % gridPixels;
235
236         if (y < 0)
237                 y = -y % gridPixels;
238         else
239                 y = (gridPixels - (y % gridPixels)) % gridPixels;
240
241         // Here we grab a section of the bigger pixmap, so that the background
242         // *looks* like it's scrolling...
243         QPixmap pm = gridBackground.copy(x, y, gridPixels, gridPixels);
244         QPalette pal = palette();
245         pal.setBrush(backgroundRole(), QBrush(pm));
246         setAutoFillBackground(true);
247         setPalette(pal);
248 }
249
250
251 //
252 // Basically, we just make a single pass through the Container. If the layer #
253 // is less than the layer # being deleted, then do nothing. If the layer # is
254 // equal to the layer # being deleted, then delete the object. If the layer #
255 // is greater than the layer # being deleted, then set the layer # to its layer
256 // # - 1.
257 //
258 void DrawingView::DeleteCurrentLayer(int layer)
259 {
260 //printf("DrawingView::DeleteCurrentLayer(): currentLayer = %i\n", layer);
261         std::vector<void *>::iterator i = document.objects.begin();
262
263         while (i != document.objects.end())
264         {
265                 Object * obj = (Object *)(*i);
266
267                 if (obj->layer < layer)
268                         i++;
269                 else if (obj->layer == layer)
270                 {
271                         document.objects.erase(i);
272                         delete obj;
273                 }
274                 else
275                 {
276                         obj->layer--;
277                         i++;
278                 }
279         }
280
281         // We've just done a destructive action, so update the screen!
282         update();
283 }
284
285
286 void DrawingView::HandleLayerToggle(void)
287 {
288         // A layer's visibility was toggled, so update the screen...
289         update();
290 }
291
292
293 //
294 // A layer was moved up or down in the layer list, so we have to swap the
295 // document's object's layer numbers in the layers that were swapped.
296 //
297 void DrawingView::HandleLayerSwap(int layer1, int layer2)
298 {
299 //printf("DrawingView: Swapping layers %i and %i.\n", layer1, layer2);
300         std::vector<void *>::iterator i;
301
302         for(i=document.objects.begin(); i!=document.objects.end(); i++)
303         {
304                 Object * obj = (Object *)(*i);
305
306                 if (obj->layer == layer1)
307                         obj->layer = layer2;
308                 else if (obj->layer == layer2)
309                         obj->layer = layer1;
310         }
311 }
312
313
314 QPoint DrawingView::GetAdjustedMousePosition(QMouseEvent * event)
315 {
316         // This is undoing the transform, e.g. going from client coords to local
317         // coords. In essence, the height - y is height + (y * -1), the (y * -1)
318         // term doing the conversion of the y-axis from increasing bottom to top.
319         return QPoint(offsetX + event->x(), offsetY + (size().height() - event->y()));
320 }
321
322
323 QPoint DrawingView::GetAdjustedClientPosition(int x, int y)
324 {
325         // VOODOO ALERT (ON Y COMPONENT!!!!) (eh?)
326         // No voodoo here, it's just grouped wrong to see it. It should be:
327         // -offsetY + (size.height() + (y * -1.0)) <-- this is wrong, offsetY should be positive [why? we use -offsetX after all]
328         return QPoint(-offsetX + x, (size().height() - (-offsetY + y)) * +1.0);
329 }
330
331
332 void DrawingView::paintEvent(QPaintEvent * /*event*/)
333 {
334         QPainter qtPainter(this);
335         Painter painter(&qtPainter);
336
337         if (useAntialiasing)
338                 qtPainter.setRenderHint(QPainter::Antialiasing);
339
340         Global::viewportHeight = size().height();
341
342         // Draw coordinate axes
343         painter.SetPen(QPen(Qt::blue, 1.0, Qt::DotLine));
344         painter.DrawLine(0, -16384, 0, 16384);
345         painter.DrawLine(-16384, 0, 16384, 0);
346
347         // Do object rendering...
348         for(int i=0; i<Global::numLayers; i++)
349         {
350                 if (Global::layerHidden[i] == false)
351                         RenderObjects(&painter, document.objects, i);
352         }
353
354         // Do tool rendering, if any...
355         if (Global::tool)
356         {
357                 painter.SetPen(QPen(QColor(200, 100, 0, 255), 1.0, Qt::DashLine));
358                 painter.DrawCrosshair(oldPoint);
359                 ToolDraw(&painter);
360         }
361
362         // Do selection rectangle rendering, if any
363         if (Global::selectionInProgress)
364         {
365                 painter.SetPen(QPen(QColor(255, 127, 0, 255)));
366                 painter.SetBrush(QBrush(QColor(255, 127, 0, 100)));
367                 painter.DrawRect(Global::selection);
368         }
369
370         if (hoveringIntersection)
371                 painter.DrawHandle(intersectionPoint);
372
373         if (!informativeText.isEmpty())
374                 painter.DrawInformativeText(informativeText);
375 }
376
377
378 //
379 // Renders objects in the passed in vector
380 //
381 void DrawingView::RenderObjects(Painter * painter, std::vector<void *> & v, int layer, bool ignoreLayer/*= false*/)
382 {
383         std::vector<void *>::iterator i;
384
385         for(i=v.begin(); i!=v.end(); i++)
386         {
387                 Object * obj = (Object *)(*i);
388                 float scaledThickness = Global::scale * obj->thickness;
389
390                 // If the object isn't on the current layer being drawn, skip it
391                 if (!ignoreLayer && (obj->layer != layer))
392                         continue;
393
394                 if ((Global::tool == TTRotate) && ctrlDown && obj->selected)
395                 {
396                         painter->SetPen(0x00FF00, 2.0, LSSolid);
397                 }
398                 else
399                 {
400                         painter->SetPen(obj->color, Global::zoom * scaledThickness, obj->style);
401                         painter->SetBrush(obj->color);
402
403                         if (obj->selected || obj->hitObject)
404                                 painter->SetPen(0xFF0000, Global::zoom * scaledThickness, LSDash);
405                 }
406
407                 switch (obj->type)
408                 {
409                 case OTLine:
410                         painter->DrawLine(obj->p[0], obj->p[1]);
411
412                         if (obj->hitPoint[0])
413                                 painter->DrawHandle(obj->p[0]);
414
415                         if (obj->hitPoint[1])
416                                 painter->DrawHandle(obj->p[1]);
417
418                         break;
419                 case OTCircle:
420                         painter->SetBrush(QBrush(Qt::NoBrush));
421                         painter->DrawEllipse(obj->p[0], obj->radius[0], obj->radius[0]);
422
423                         if (obj->hitPoint[0])
424                                 painter->DrawHandle(obj->p[0]);
425
426                         break;
427                 case OTArc:
428                         painter->DrawArc(obj->p[0], obj->radius[0], obj->angle[0], obj->angle[1]);
429
430                         if (obj->hitPoint[0])
431                                 painter->DrawHandle(obj->p[0]);
432
433                         if (obj->hitPoint[1])
434                                 painter->DrawHandle(obj->p[0] + (Vector(cos(obj->angle[0]), sin(obj->angle[0])) * obj->radius[0]));
435
436                         if (obj->hitPoint[2])
437                                 painter->DrawHandle(obj->p[0] + (Vector(cos(obj->angle[0] + obj->angle[1]), sin(obj->angle[0] + obj->angle[1])) * obj->radius[0]));
438
439                         break;
440                 case OTDimension:
441                 {
442                         Dimension * d = (Dimension *)obj;
443
444                         Vector v(d->p[0], d->p[1]);
445                         double angle = v.Angle();
446                         Vector unit = v.Unit();
447                         d->lp[0] = d->p[0], d->lp[1] = d->p[1];
448                         Vector ortho;
449                         double x1, y1, length;
450
451                         if (d->subtype == DTLinearVert)
452                         {
453                                 if ((angle < 0) || (angle > HALF_TAU))
454                                 {
455                                         x1 = (d->p[0].x > d->p[1].x ? d->p[0].x : d->p[1].x);
456                                         y1 = (d->p[0].y > d->p[1].y ? d->p[0].y : d->p[1].y);
457                                         ortho = Vector(1.0, 0);
458                                         angle = THREE_QTR_TAU;
459                                 }
460                                 else
461                                 {
462                                         x1 = (d->p[0].x > d->p[1].x ? d->p[1].x : d->p[0].x);
463                                         y1 = (d->p[0].y > d->p[1].y ? d->p[1].y : d->p[0].y);
464                                         ortho = Vector(-1.0, 0);
465                                         angle = QTR_TAU;
466                                 }
467
468                                 d->lp[0].x = d->lp[1].x = x1;
469                                 length = fabs(d->p[0].y - d->p[1].y);
470                         }
471                         else if (d->subtype == DTLinearHorz)
472                         {
473                                 if ((angle < QTR_TAU) || (angle > THREE_QTR_TAU))
474                                 {
475                                         x1 = (d->p[0].x > d->p[1].x ? d->p[0].x : d->p[1].x);
476                                         y1 = (d->p[0].y > d->p[1].y ? d->p[0].y : d->p[1].y);
477                                         ortho = Vector(0, 1.0);
478                                         angle = 0;
479                                 }
480                                 else
481                                 {
482                                         x1 = (d->p[0].x > d->p[1].x ? d->p[1].x : d->p[0].x);
483                                         y1 = (d->p[0].y > d->p[1].y ? d->p[1].y : d->p[0].y);
484                                         ortho = Vector(0, -1.0);
485                                         angle = HALF_TAU;
486                                 }
487
488                                 d->lp[0].y = d->lp[1].y = y1;
489                                 length = fabs(d->p[0].x - d->p[1].x);
490                         }
491                         else if (d->subtype == DTLinear)
492                         {
493                                 angle = Vector(d->lp[0], d->lp[1]).Angle();
494                                 ortho = Vector::Normal(d->lp[0], d->lp[1]);
495                                 length = v.Magnitude();
496                         }
497
498                         unit = Vector(d->lp[0], d->lp[1]).Unit();
499
500                         Point p1 = d->lp[0] + (ortho * 10.0 * scaledThickness);
501                         Point p2 = d->lp[1] + (ortho * 10.0 * scaledThickness);
502                         Point p3 = d->lp[0] + (ortho * 16.0 * scaledThickness);
503                         Point p4 = d->lp[1] + (ortho * 16.0 * scaledThickness);
504                         Point p5 = d->p[0] + (ortho * 4.0 * scaledThickness);
505                         Point p6 = d->p[1] + (ortho * 4.0 * scaledThickness);
506
507                 /*
508                 The numbers hardcoded into here, what are they?
509                 I believe they are pixels.
510                 */
511                         // Draw extension lines (if certain type)
512                         painter->DrawLine(p3, p5);
513                         painter->DrawLine(p4, p6);
514
515                         // Calculate whether or not the arrowheads are too crowded to put
516                         // inside the extension lines. 9.0 is the length of the arrowhead.
517                         double t = Geometry::ParameterOfLineAndPoint(d->lp[0], d->lp[1], d->lp[1] - (unit * 9.0 * scaledThickness));
518
519                         // On the screen, it's acting like this is actually 58%...
520                         // This is correct, we want it to happen at > 50%
521                         if (t > 0.58)
522                         {
523                                 // Draw main dimension line + arrowheads
524                                 painter->DrawLine(p1, p2);
525                                 painter->DrawArrowhead(p1, p2, scaledThickness);
526                                 painter->DrawArrowhead(p2, p1, scaledThickness);
527                         }
528                         else
529                         {
530                                 // Draw outside arrowheads
531                                 Point p7 = p1 - (unit * 9.0 * scaledThickness);
532                                 Point p8 = p2 + (unit * 9.0 * scaledThickness);
533                                 painter->DrawArrowhead(p1, p7, scaledThickness);
534                                 painter->DrawArrowhead(p2, p8, scaledThickness);
535                                 painter->DrawLine(p1, p1 - (unit * 14.0 * scaledThickness));
536                                 painter->DrawLine(p2, p2 + (unit * 14.0 * scaledThickness));
537                         }
538
539                         // Draw length of dimension line...
540                         painter->SetFont(QFont("Arial", 8.0 * Global::zoom * scaledThickness));
541                         Point ctr = p2 + (Vector(p2, p1) / 2.0);
542
543                         QString dimText;
544
545                         if (length < 12.0)
546                                 dimText = QString("%1\"").arg(length);
547                         else
548                         {
549                                 double feet = (double)((int)length / 12);
550                                 double inches = length - (feet * 12.0);
551
552                                 if (inches == 0)
553                                         dimText = QString("%1'").arg(feet);
554                                 else
555                                         dimText = QString("%1' %2\"").arg(feet).arg(inches);
556                         }
557
558                         painter->DrawAngledText(ctr, angle, dimText, scaledThickness);
559
560                         if (d->hitObject)
561                         {
562                                 Point hp1 = (p1 + p2) / 2.0;
563                                 Point hp2 = (p1 + hp1) / 2.0;
564                                 Point hp3 = (hp1 + p2) / 2.0;
565
566                                 if (d->hitPoint[2])
567                                 {
568                                         painter->SetPen(QPen(Qt::magenta, 1.0, Qt::SolidLine));
569                                         painter->SetBrush(QBrush(QColor(Qt::magenta)));
570                                         painter->DrawArrowHandle(hp1, ortho.Angle() + HALF_TAU);
571                                         painter->SetPen(QPen(Qt::magenta, 2.0, Qt::DotLine));
572                                 }
573
574                                 painter->DrawHandle(hp1);
575                                 painter->SetPen(QPen(Qt::blue, 1.0 * Global::zoom * scaledThickness, Qt::SolidLine));
576
577                                 if (d->hitPoint[3])
578                                 {
579                                         painter->SetPen(QPen(Qt::magenta, 1.0, Qt::SolidLine));
580                                         painter->SetBrush(QBrush(QColor(Qt::magenta)));
581                                         painter->DrawArrowToLineHandle(hp2, (d->subtype == DTLinearVert ? v.Angle() - QTR_TAU : (v.Angle() < HALF_TAU ? HALF_TAU : 0)));
582                                         painter->SetPen(QPen(Qt::magenta, 2.0, Qt::DotLine));
583                                 }
584
585                                 painter->DrawHandle(hp2);
586                                 painter->SetPen(QPen(Qt::blue, 1.0 * Global::zoom * scaledThickness, Qt::SolidLine));
587
588                                 if (d->hitPoint[4])
589                                 {
590                                         painter->SetPen(QPen(Qt::magenta, 1.0, Qt::SolidLine));
591                                         painter->SetBrush(QBrush(QColor(Qt::magenta)));
592                                         painter->DrawArrowToLineHandle(hp3, (d->subtype == DTLinearHorz ? v.Angle() - QTR_TAU : (v.Angle() > HALF_TAU && v.Angle() < THREE_QTR_TAU ? THREE_QTR_TAU : QTR_TAU)));
593                                         painter->SetPen(QPen(Qt::magenta, 2.0, Qt::DotLine));
594                                 }
595
596                                 painter->DrawHandle(hp3);
597                         }
598
599                         if (obj->hitPoint[0])
600                                 painter->DrawHandle(obj->p[0]);
601
602                         if (obj->hitPoint[1])
603                                 painter->DrawHandle(obj->p[1]);
604
605                         break;
606                 }
607                 case OTText:
608                 {
609                         Text * t = (Text *)obj;
610
611                         if (t->measured == false)
612                         {
613                                 t->extents = painter->MeasureTextObject(t->s.c_str(), scaledThickness);
614                                 t->measured = true;
615                         }
616
617                         painter->DrawTextObject(t->p[0], t->s.c_str(), scaledThickness, t->angle[0]);
618                         break;
619                 }
620                 case OTSpline:
621                 {
622                         break;
623                 }
624                 case OTPolygon:
625                 {
626                         break;
627                 }
628                 case OTContainer:
629                 {
630                         // Containers require recursive rendering...
631                         Container * c = (Container *)obj;
632                         RenderObjects(painter, (*c).objects, layer);
633
634 //printf("Container extents: <%lf, %lf>, <%lf, %lf>\nsize: %i\n", r.l, r.t, r.r, r.b, c->objects.size());
635                         // Containers also have special indicators showing they are selected
636                         if (c->selected || c->hitObject)
637                         {
638                                 Rect r = GetObjectExtents(obj);
639                                 painter->DrawRectCorners(r);
640                         }
641
642                         break;
643                 }
644                 default:
645                         break;
646                 }
647         }
648 }
649
650
651 void DrawingView::AddHoveredToSelection(void)
652 {
653         std::vector<void *>::iterator i;
654
655         for(i=document.objects.begin(); i!=document.objects.end(); i++)
656         {
657                 if (((Object *)(*i))->hovered)
658                         ((Object *)(*i))->selected = true;
659         }
660 }
661
662
663 void DrawingView::GetSelection(std::vector<void *> & v)
664 {
665         v.clear();
666         std::vector<void *>::iterator i;
667
668         for(i=document.objects.begin(); i!=document.objects.end(); i++)
669         {
670                 if (((Object *)(*i))->selected)
671                         v.push_back(*i);
672         }
673 }
674
675
676 void DrawingView::GetHovered(std::vector<void *> & v)
677 {
678         v.clear();
679         std::vector<void *>::iterator i;
680
681         for(i=document.objects.begin(); i!=document.objects.end(); i++)
682         {
683                 if (((Object *)(*i))->hovered)
684 //              {
685 //printf("GetHovered: adding object (%X) to hover... hp1=%s, hp2=%s, hl=%s\n", (*i), (((Line *)(*i))->hitPoint[0] ? "true" : "false"), (((Line *)(*i))->hitPoint[1] ? "true" : "false"), (((Line *)(*i))->hitObject ? "true" : "false"));
686                         v.push_back(*i);
687 //              }
688         }
689 }
690
691
692 void DrawingView::resizeEvent(QResizeEvent * /*event*/)
693 {
694         Global::screenSize = Vector(size().width(), size().height());
695         UpdateGridBackground();
696 }
697
698
699 void DrawingView::ToolHandler(int mode, Point p)
700 {
701         // Drop angle snap until it's needed
702         angleSnap = false;
703
704         if (Global::tool == TTLine)
705                 LineHandler(mode, p);
706         else if (Global::tool == TTCircle)
707                 CircleHandler(mode, p);
708         else if (Global::tool == TTArc)
709                 ArcHandler(mode, p);
710         else if (Global::tool == TTRotate)
711                 RotateHandler(mode, p);
712         else if (Global::tool == TTMirror)
713                 MirrorHandler(mode, p);
714 }
715
716
717 void DrawingView::ToolDraw(Painter * painter)
718 {
719         if (Global::tool == TTLine)
720         {
721                 if (Global::toolState == TSNone)
722                 {
723                         painter->DrawHandle(toolPoint[0]);
724                 }
725                 else if ((Global::toolState == TSPoint2) && shiftDown)
726                 {
727                         painter->DrawHandle(toolPoint[1]);
728                 }
729                 else
730                 {
731                         painter->DrawLine(toolPoint[0], toolPoint[1]);
732                         painter->DrawHandle(toolPoint[1]);
733
734                         Vector v(toolPoint[0], toolPoint[1]);
735                         double absAngle = v.Angle() * RADIANS_TO_DEGREES;
736                         double absLength = v.Magnitude();
737                         QString text = tr("Length: %1 in.\n") + QChar(0x2221) + tr(": %2");
738                         informativeText = text.arg(absLength).arg(absAngle);
739                 }
740         }
741         else if (Global::tool == TTCircle)
742         {
743                 if (Global::toolState == TSNone)
744                 {
745                         painter->DrawHandle(toolPoint[0]);
746                 }
747                 else if ((Global::toolState == TSPoint2) && shiftDown)
748                 {
749                         painter->DrawHandle(toolPoint[1]);
750                 }
751                 else
752                 {
753                         painter->DrawCross(toolPoint[0]);
754                         double length = Vector::Magnitude(toolPoint[0], toolPoint[1]);
755                         painter->SetBrush(QBrush(Qt::NoBrush));
756                         painter->DrawEllipse(toolPoint[0], length, length);
757                         QString text = tr("Radius: %1 in.");
758                         informativeText = text.arg(length);
759                 }
760         }
761         else if (Global::tool == TTArc)
762         {
763                 if (Global::toolState == TSNone)
764                 {
765                         painter->DrawHandle(toolPoint[0]);
766                 }
767                 else if (Global::toolState == TSPoint2)
768                 {
769                         double length = Vector::Magnitude(toolPoint[0], toolPoint[1]);
770                         painter->SetBrush(QBrush(Qt::NoBrush));
771                         painter->DrawEllipse(toolPoint[0], length, length);
772                         painter->DrawLine(toolPoint[0], toolPoint[1]);
773                         painter->DrawHandle(toolPoint[1]);
774                         QString text = tr("Radius: %1 in.");
775                         informativeText = text.arg(length);
776                 }
777                 else if (Global::toolState == TSPoint3)
778                 {
779                         double angle = Vector::Angle(toolPoint[0], toolPoint[2]);
780                         painter->DrawLine(toolPoint[0], toolPoint[2]);
781                         painter->SetBrush(QBrush(Qt::NoBrush));
782                         painter->DrawEllipse(toolPoint[0], toolPoint[1].x, toolPoint[1].x);
783                         painter->DrawHandle(toolPoint[0] + (Vector(cos(angle), sin(angle)) * toolPoint[1].x));
784                         QString text = tr("Angle start: %1") + QChar(0x00B0);
785                         informativeText = text.arg(RADIANS_TO_DEGREES * angle);
786                 }
787                 else
788                 {
789                         double angle = Vector::Angle(toolPoint[0], toolPoint[3]);
790                         double span = angle - toolPoint[2].x;
791
792                         if (span < 0)
793                                 span += TAU;
794
795                         painter->DrawLine(toolPoint[0], toolPoint[3]);
796                         painter->SetBrush(QBrush(Qt::NoBrush));
797                         painter->DrawEllipse(toolPoint[0], toolPoint[1].x, toolPoint[1].x);
798                         painter->SetPen(0xFF00FF, 2.0, LSSolid);
799                         painter->DrawArc(toolPoint[0], toolPoint[1].x, toolPoint[2].x, span);
800                         painter->DrawHandle(toolPoint[0] + (Vector(cos(angle), sin(angle)) * toolPoint[1].x));
801                         QString text = tr("Arc span: %1") + QChar(0x00B0);
802                         informativeText = text.arg(RADIANS_TO_DEGREES * span);
803                 }
804         }
805         else if (Global::tool == TTRotate)
806         {
807                 if ((Global::toolState == TSNone) || (Global::toolState == TSPoint1))
808                         painter->DrawHandle(toolPoint[0]);
809                 else if ((Global::toolState == TSPoint2) && shiftDown)
810                         painter->DrawHandle(toolPoint[1]);
811                 else
812                 {
813                         if (toolPoint[0] == toolPoint[1])
814                                 return;
815
816                         painter->DrawLine(toolPoint[0], toolPoint[1]);
817
818                         double absAngle = (Vector(toolPoint[1] - toolPoint[0]).Angle()) * RADIANS_TO_DEGREES;
819                         QString text = QChar(0x2221) + QObject::tr(": %1");
820                         informativeText = text.arg(absAngle);
821
822                         if (ctrlDown)
823                                 informativeText += " (Copy)";
824                 }
825         }
826         else if (Global::tool == TTMirror)
827         {
828                 if ((Global::toolState == TSNone) || (Global::toolState == TSPoint1))
829                         painter->DrawHandle(toolPoint[0]);
830                 else if ((Global::toolState == TSPoint2) && shiftDown)
831                         painter->DrawHandle(toolPoint[1]);
832                 else
833                 {
834                         if (toolPoint[0] == toolPoint[1])
835                                 return;
836                         
837                         Point mirrorPoint = toolPoint[0] + Vector(toolPoint[1], toolPoint[0]);
838                         painter->DrawLine(mirrorPoint, toolPoint[1]);
839
840                         double absAngle = (Vector(toolPoint[1] - toolPoint[0]).Angle()) * RADIANS_TO_DEGREES;
841
842                         if (absAngle > 180.0)
843                                 absAngle -= 180.0;
844
845                         QString text = QChar(0x2221) + QObject::tr(": %1");
846                         informativeText = text.arg(absAngle);
847
848                         if (ctrlDown)
849                                 informativeText += " (Copy)";
850                 }
851         }
852 }
853
854
855 void DrawingView::LineHandler(int mode, Point p)
856 {
857         switch (mode)
858         {
859         case ToolMouseDown:
860                 if (Global::toolState == TSNone)
861                         toolPoint[0] = p;
862                 else
863                         toolPoint[1] = p;
864
865                 break;
866         case ToolMouseMove:
867                 if (Global::toolState == TSNone)
868                         toolPoint[0] = p;
869                 else
870                         toolPoint[1] = p;
871
872                 break;
873         case ToolMouseUp:
874                 if (Global::toolState == TSNone)
875                 {
876                         Global::toolState = TSPoint2;
877                         // Prevent spurious line from drawing...
878                         toolPoint[1] = toolPoint[0];
879                 }
880                 else if ((Global::toolState == TSPoint2) && shiftDown)
881                 {
882                         // Key override is telling us to make a new line, not continue the
883                         // previous one.
884                         toolPoint[0] = toolPoint[1];
885                 }
886                 else
887                 {
888                         Line * l = new Line(toolPoint[0], toolPoint[1]);
889                         l->layer = Global::activeLayer;
890                         document.objects.push_back(l);
891                         toolPoint[0] = toolPoint[1];
892                 }
893         }
894 }
895
896
897 void DrawingView::CircleHandler(int mode, Point p)
898 {
899         switch (mode)
900         {
901         case ToolMouseDown:
902                 if (Global::toolState == TSNone)
903                         toolPoint[0] = p;
904                 else
905                         toolPoint[1] = p;
906
907                 break;
908         case ToolMouseMove:
909                 if (Global::toolState == TSNone)
910                         toolPoint[0] = p;
911                 else
912                         toolPoint[1] = p;
913
914                 break;
915         case ToolMouseUp:
916                 if (Global::toolState == TSNone)
917                 {
918                         Global::toolState = TSPoint2;
919                         // Prevent spurious line from drawing...
920                         toolPoint[1] = toolPoint[0];
921                 }
922                 else if ((Global::toolState == TSPoint2) && shiftDown)
923                 {
924                         // Key override is telling us to make a new line, not continue the
925                         // previous one.
926                         toolPoint[0] = toolPoint[1];
927                 }
928                 else
929                 {
930                         double length = Vector::Magnitude(toolPoint[0], toolPoint[1]);
931                         Circle * c = new Circle(toolPoint[0], length);
932                         c->layer = Global::activeLayer;
933                         document.objects.push_back(c);
934                         toolPoint[0] = toolPoint[1];
935                         Global::toolState = TSNone;
936                 }
937         }
938 }
939
940
941 void DrawingView::ArcHandler(int mode, Point p)
942 {
943         switch (mode)
944         {
945         case ToolMouseDown:
946                 if (Global::toolState == TSNone)
947                         toolPoint[0] = p;
948                 else if (Global::toolState == TSPoint2)
949                         toolPoint[1] = p;
950                 else if (Global::toolState == TSPoint3)
951                         toolPoint[2] = p;
952                 else
953                         toolPoint[3] = p;
954
955                 break;
956         case ToolMouseMove:
957                 if (Global::toolState == TSNone)
958                         toolPoint[0] = p;
959                 else if (Global::toolState == TSPoint2)
960                         toolPoint[1] = p;
961                 else if (Global::toolState == TSPoint3)
962                 {
963                         toolPoint[2] = p;
964                         angleSnap = true;
965                 }
966                 else
967                 {
968                         toolPoint[3] = p;
969                         angleSnap = true;
970                 }
971
972                 break;
973         case ToolMouseUp:
974                 if (Global::toolState == TSNone)
975                 {
976                         // Prevent spurious line from drawing...
977                         toolPoint[1] = toolPoint[0];
978                         Global::toolState = TSPoint2;
979                 }
980                 else if (Global::toolState == TSPoint2)
981                 {
982                         if (shiftDown)
983                         {
984                                 // Key override is telling us to start arc at new center, not
985                                 // continue the current one.
986                                 toolPoint[0] = toolPoint[1];
987                                 return;
988                         }
989
990                         // Set the radius in toolPoint[1].x
991                         toolPoint[1].x = Vector::Magnitude(toolPoint[0], toolPoint[1]);
992                         Global::toolState = TSPoint3;
993                 }
994                 else if (Global::toolState == TSPoint3)
995                 {
996                         // Set the angle in toolPoint[2].x
997                         toolPoint[2].x = Vector::Angle(toolPoint[0], toolPoint[2]);
998                         Global::toolState = TSPoint4;
999                 }
1000                 else
1001                 {
1002                         double endAngle = Vector::Angle(toolPoint[0], toolPoint[3]);
1003                         double span = endAngle - toolPoint[2].x;
1004
1005                         if (span < 0)
1006                                 span += TAU;
1007
1008                         Arc * arc = new Arc(toolPoint[0], toolPoint[1].x, toolPoint[2].x, span);
1009                         arc->layer = Global::activeLayer;
1010                         document.objects.push_back(arc);
1011                         Global::toolState = TSNone;
1012                 }
1013         }
1014 }
1015
1016
1017 void DrawingView::RotateHandler(int mode, Point p)
1018 {
1019         switch (mode)
1020         {
1021         case ToolMouseDown:
1022                 if (Global::toolState == TSNone)
1023                 {
1024                         toolPoint[0] = p;
1025                         SavePointsFrom(select, toolScratch);
1026                         Global::toolState = TSPoint1;
1027                 }
1028                 else if (Global::toolState == TSPoint1)
1029                         toolPoint[0] = p;
1030                 else
1031                         toolPoint[1] = p;
1032
1033                 break;
1034         case ToolMouseMove:
1035                 if ((Global::toolState == TSPoint1) || (Global::toolState == TSNone))
1036                         toolPoint[0] = p;
1037                 else if (Global::toolState == TSPoint2)
1038                 {
1039                         toolPoint[1] = p;
1040
1041                         if (shiftDown)
1042                                 return;
1043
1044                         angleSnap = true;
1045                         double angle = Vector(toolPoint[0], toolPoint[1]).Angle();
1046                         std::vector<void *>::iterator j = select.begin();
1047                         std::vector<Object>::iterator i = toolScratch.begin();
1048
1049                         for(; i!=toolScratch.end(); i++, j++)
1050                         {
1051                                 Object obj = *i;
1052                                 Point p1 = Geometry::RotatePointAroundPoint(obj.p[0], toolPoint[0], angle);
1053                                 Point p2 = Geometry::RotatePointAroundPoint(obj.p[1], toolPoint[0], angle);
1054                                 Object * obj2 = (Object *)(*j);
1055                                 obj2->p[0] = p1;
1056                                 obj2->p[1] = p2;
1057
1058                                 if (obj.type == OTArc)
1059                                 {
1060                                         obj2->angle[0] = obj.angle[0] + angle;
1061
1062                                         if (obj2->angle[0] > TAU)
1063                                                 obj2->angle[0] -= TAU;
1064                                 }
1065                         }
1066                 }
1067
1068                 break;
1069         case ToolMouseUp:
1070                 if (Global::toolState == TSPoint1)
1071                 {
1072                         Global::toolState = TSPoint2;
1073                         // Prevent spurious line from drawing...
1074                         toolPoint[1] = toolPoint[0];
1075                 }
1076                 else if ((Global::toolState == TSPoint2) && shiftDown)
1077                 {
1078                         // Key override is telling us to make a new line, not continue the
1079                         // previous one.
1080                         toolPoint[0] = toolPoint[1];
1081                 }
1082                 else
1083                 {
1084                         // Either we're finished with our rotate, or we're stamping a copy.
1085                         if (ctrlDown)
1086                         {
1087                                 // Stamp a copy of the selection at the current rotation & bail
1088                                 std::vector<void *> temp;
1089                                 CopyObjects(select, temp);
1090                                 ClearSelected(temp);
1091                                 AddObjectsTo(document.objects, temp);
1092                                 RestorePointsTo(select, toolScratch);
1093                                 return;
1094                         }
1095
1096                         toolPoint[0] = p;
1097                         Global::toolState = TSPoint1;
1098                         SavePointsFrom(select, toolScratch);
1099                 }
1100
1101                 break;
1102         case ToolKeyDown:
1103                 // Reset the selection if shift held down...
1104                 if (shiftDown)
1105                         RestorePointsTo(select, toolScratch);
1106
1107                 break;
1108         case ToolKeyUp:
1109                 // Reset selection when key is let up
1110                 if (!shiftDown)
1111                         RotateHandler(ToolMouseMove, toolPoint[1]);
1112
1113                 break;
1114         case ToolCleanup:
1115                 RestorePointsTo(select, toolScratch);
1116         }
1117 }
1118
1119
1120 void DrawingView::MirrorHandler(int mode, Point p)
1121 {
1122         switch (mode)
1123         {
1124         case ToolMouseDown:
1125                 if (Global::toolState == TSNone)
1126                 {
1127                         toolPoint[0] = p;
1128                         SavePointsFrom(select, toolScratch);
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         case ToolMouseMove:
1138                 if ((Global::toolState == TSPoint1) || (Global::toolState == TSNone))
1139                         toolPoint[0] = p;
1140                 else if (Global::toolState == TSPoint2)
1141                 {
1142                         toolPoint[1] = p;
1143
1144                         if (shiftDown)
1145                                 return;
1146
1147                         angleSnap = true;
1148                         double angle = Vector(toolPoint[0], toolPoint[1]).Angle();
1149                         std::vector<void *>::iterator j = select.begin();
1150                         std::vector<Object>::iterator i = toolScratch.begin();
1151
1152                         for(; i!=toolScratch.end(); i++, j++)
1153                         {
1154                                 Object obj = *i;
1155                                 Point p1 = Geometry::MirrorPointAroundLine(obj.p[0], toolPoint[0], toolPoint[1]);
1156                                 Point p2 = Geometry::MirrorPointAroundLine(obj.p[1], toolPoint[0], toolPoint[1]);
1157                                 Object * obj2 = (Object *)(*j);
1158                                 obj2->p[0] = p1;
1159                                 obj2->p[1] = p2;
1160
1161                                 if (obj.type == OTArc)
1162                                 {
1163                                         // This is 2*mirror angle - obj angle - obj span
1164                                         obj2->angle[0] = (2.0 * angle) - obj.angle[0] - obj.angle[1];
1165
1166                                         if (obj2->angle[0] > TAU)
1167                                                 obj2->angle[0] -= TAU;
1168                                 }
1169                         }
1170                 }
1171
1172                 break;
1173         case ToolMouseUp:
1174                 if (Global::toolState == TSPoint1)
1175                 {
1176                         Global::toolState = TSPoint2;
1177                         // Prevent spurious line from drawing...
1178                         toolPoint[1] = toolPoint[0];
1179                 }
1180                 else if ((Global::toolState == TSPoint2) && shiftDown)
1181                 {
1182                         // Key override is telling us to make a new line, not continue the
1183                         // previous one.
1184                         toolPoint[0] = toolPoint[1];
1185                 }
1186                 else
1187                 {
1188                         // Either we're finished with our rotate, or we're stamping a copy.
1189                         if (ctrlDown)
1190                         {
1191                                 // Stamp a copy of the selection at the current rotation & bail
1192                                 std::vector<void *> temp;
1193                                 CopyObjects(select, temp);
1194                                 ClearSelected(temp);
1195                                 AddObjectsTo(document.objects, temp);
1196                                 RestorePointsTo(select, toolScratch);
1197                                 return;
1198                         }
1199
1200                         toolPoint[0] = p;
1201                         Global::toolState = TSPoint1;
1202                         SavePointsFrom(select, toolScratch);
1203                 }
1204
1205                 break;
1206         case ToolKeyDown:
1207                 // Reset the selection if shift held down...
1208                 if (shiftDown)
1209                         RestorePointsTo(select, toolScratch);
1210
1211                 break;
1212         case ToolKeyUp:
1213                 // Reset selection when key is let up
1214                 if (!shiftDown)
1215                         MirrorHandler(ToolMouseMove, toolPoint[1]);
1216
1217                 break;
1218         case ToolCleanup:
1219                 RestorePointsTo(select, toolScratch);
1220         }
1221 }
1222
1223
1224 void DrawingView::mousePressEvent(QMouseEvent * event)
1225 {
1226         if (event->button() == Qt::LeftButton)
1227         {
1228                 Vector point = Painter::QtToCartesianCoords(Vector(event->x(), event->y()));
1229
1230                 // Handle tool processing, if any
1231                 if (Global::tool)
1232                 {
1233                         if (hoveringIntersection)
1234                                 point = intersectionPoint;
1235                         else if (Global::snapToGrid)
1236                                 point = SnapPointToGrid(point);
1237
1238                         //Also, may want to figure out if hovering over a snap point on an
1239                         //object, snap to grid if not.
1240                         // Snap to object point if valid...
1241 //                      if (Global::snapPointIsValid)
1242 //                              point = Global::snapPoint;
1243
1244                         ToolHandler(ToolMouseDown, point);
1245                         return;
1246                 }
1247
1248                 // Clear the selection only if CTRL isn't being held on click
1249                 if (!ctrlDown)
1250                         ClearSelected(document.objects);
1251 //                      ClearSelection();
1252
1253                 // If any objects are being hovered on click, add them to the selection
1254                 // & return
1255                 if (numHovered > 0)
1256                 {
1257                         AddHoveredToSelection();
1258                         update();       // needed??
1259                         GetHovered(hover);      // prolly needed
1260                         dragged = (Object *)hover[0];
1261                         draggingObject = true;
1262
1263                         // See if anything is using just a straight click on a handle
1264                         if (HandleObjectClicked())
1265                         {
1266                                 draggingObject = false;
1267                                 update();
1268                                 return;
1269                         }
1270
1271                         // Needed for grab & moving objects
1272                         // We do it *after*... why? (doesn't seem to confer any advantage...)
1273                         if (hoveringIntersection)
1274                                 oldPoint = intersectionPoint;
1275                         else if (Global::snapToGrid)
1276                                 oldPoint = SnapPointToGrid(point);
1277
1278                         return;
1279                 }
1280
1281                 // Didn't hit any object and not using a tool, so do a selection rectangle
1282                 Global::selectionInProgress = true;
1283                 Global::selection.setTopLeft(QPointF(point.x, point.y));
1284                 Global::selection.setBottomRight(QPointF(point.x, point.y));
1285         }
1286         else if (event->button() == Qt::MiddleButton)
1287         {
1288                 scrollDrag = true;
1289                 oldPoint = Vector(event->x(), event->y());
1290                 // Should also change the mouse pointer as well...
1291                 setCursor(Qt::SizeAllCursor);
1292         }
1293 }
1294
1295
1296 void DrawingView::mouseMoveEvent(QMouseEvent * event)
1297 {
1298         Vector point = Painter::QtToCartesianCoords(Vector(event->x(), event->y()));
1299         Global::selection.setBottomRight(QPointF(point.x, point.y));
1300         // Only needs to be done here, as mouse down is always preceded by movement
1301         Global::snapPointIsValid = false;
1302         hoveringIntersection = false;
1303
1304         // Scrolling...
1305         if (event->buttons() & Qt::MiddleButton)
1306         {
1307                 point = Vector(event->x(), event->y());
1308                 // Since we're using Qt coords for scrolling, we have to adjust them
1309                 // here to conform to Cartesian coords, since the origin is using
1310                 // Cartesian. :-)
1311                 Vector delta(oldPoint, point);
1312                 delta /= Global::zoom;
1313                 delta.y = -delta.y;
1314                 Global::origin -= delta;
1315
1316                 UpdateGridBackground();
1317                 update();
1318                 oldPoint = point;
1319                 return;
1320         }
1321
1322         // If we're doing a selection rect, see if any objects are engulfed by it
1323         // (implies left mouse button held down)
1324         if (Global::selectionInProgress)
1325         {
1326                 CheckObjectBounds();
1327                 update();
1328                 return;
1329         }
1330
1331         // Do object hit testing...
1332         bool needUpdate = HitTestObjects(point);
1333         GetHovered(hover);
1334
1335         // Check for multi-hover...
1336         if (numHovered > 1)
1337         {
1338 //need to check for case where hover is over 2 circles and a 3rd's center...
1339                 Object * obj1 = (Object *)hover[0], * obj2 = (Object *)hover[1];
1340
1341                 Geometry::Intersects(obj1, obj2);
1342                 int numIntersecting = Global::numIntersectParams;
1343                 double t = Global::intersectParam[0];
1344                 double u = Global::intersectParam[1];
1345
1346                 if (numIntersecting > 0)
1347                 {
1348                         Vector v1 = Geometry::GetPointForParameter(obj1, t);
1349                         Vector v2 = Geometry::GetPointForParameter(obj2, u);
1350                         QString text = tr("Intersection t=%1 (%3, %4), u=%2 (%5, %6)");
1351                         informativeText = text.arg(t).arg(u).arg(v1.x).arg(v1.y).arg(v2.x).arg(v2.y);
1352
1353                         hoveringIntersection = true;
1354                         intersectionPoint = v1;
1355                 }
1356
1357                 numIntersecting = Global::numIntersectPoints;
1358
1359                 if (numIntersecting > 0)
1360                 {
1361                         Vector v1 = Global::intersectPoint[0];
1362
1363                         if (numIntersecting == 2)
1364                         {
1365                                 Vector v2 = Global::intersectPoint[1];
1366
1367                                 if (Vector::Magnitude(v2, point) < Vector::Magnitude(v1, point))
1368                                         v1 = v2;
1369                         }
1370
1371                         QString text = tr("Intersection <%1, %2>");
1372                         informativeText = text.arg(v1.x).arg(v1.y);
1373                         hoveringIntersection = true;
1374                         intersectionPoint = v1;
1375                 }
1376         }
1377
1378         // Handle object movement (left button down & over an object)
1379         if ((event->buttons() & Qt::LeftButton) && draggingObject && !Global::tool)
1380         {
1381                 if (hoveringIntersection)
1382                         point = intersectionPoint;
1383                 else if (hoverPointValid)
1384                         point = hoverPoint;
1385                 else if (Global::snapToGrid)
1386                         point = SnapPointToGrid(point);
1387
1388                 HandleObjectMovement(point);
1389                 update();
1390                 oldPoint = point;
1391                 return;
1392         }
1393
1394         // Do tool handling, if any are active...
1395         if (Global::tool)
1396         {
1397                 if (hoveringIntersection)
1398                         point = intersectionPoint;
1399                 else if (hoverPointValid)
1400                         point = hoverPoint;
1401                 else if (Global::snapToGrid)
1402                 {
1403                         if (angleSnap)
1404                                 point = SnapPointToAngle(point);
1405                         else
1406                                 point = SnapPointToGrid(point);
1407                 }
1408
1409                 ToolHandler(ToolMouseMove, point);
1410         }
1411
1412         // This is used to draw the tool crosshair...
1413         oldPoint = point;
1414
1415         if (needUpdate || Global::tool)
1416                 update();
1417 }
1418
1419
1420 void DrawingView::mouseReleaseEvent(QMouseEvent * event)
1421 {
1422         if (event->button() == Qt::LeftButton)
1423         {
1424 //We need to update especially if nothing collided and the state needs to change. !!! FIX !!!
1425 //could set it up to use the document's update function (assumes that all object
1426 //updates are being reported correctly:
1427 //              if (document.NeedsUpdate())
1428                 // Do an update if collided with at least *one* object in the document
1429 //              if (collided)
1430                         update();
1431
1432                 if (Global::tool)
1433                 {
1434                         Vector point = Painter::QtToCartesianCoords(Vector(event->x(), event->y()));
1435                         ToolHandler(ToolMouseUp, point);
1436                         return;
1437                 }
1438
1439                 if (Global::selectionInProgress)
1440                         Global::selectionInProgress = false;
1441
1442                 informativeText.clear();
1443 // Should we be doing this automagically? Hmm...
1444                 // Clear our vectors
1445                 select.clear();
1446                 hover.clear();
1447
1448                 // Scoop 'em up
1449                 std::vector<void *>::iterator i;
1450
1451                 for(i=document.objects.begin(); i!=document.objects.end(); i++)
1452                 {
1453                         if (((Object *)(*i))->selected)
1454                                 select.push_back(*i);
1455                 }
1456
1457                 draggingObject = false;
1458         }
1459         else if (event->button() == Qt::MiddleButton)
1460         {
1461                 scrollDrag = false;
1462                 setCursor(Qt::ArrowCursor);
1463         }
1464 }
1465
1466
1467 void DrawingView::wheelEvent(QWheelEvent * event)
1468 {
1469         double zoomFactor = 1.25;
1470         QSize sizeWin = size();
1471         Vector center(sizeWin.width() / 2.0, sizeWin.height() / 2.0);
1472         center = Painter::QtToCartesianCoords(center);
1473
1474         // This is not centering for some reason. Need to figure out why. :-/
1475         if (event->delta() > 0)
1476         {
1477                 Vector newOrigin = center - ((center - Global::origin) / zoomFactor);
1478                 Global::origin = newOrigin;
1479                 Global::zoom *= zoomFactor;
1480         }
1481         else
1482         {
1483                 Vector newOrigin = center + ((-center + Global::origin) * zoomFactor);
1484                 Global::origin = newOrigin;
1485                 Global::zoom /= zoomFactor;
1486         }
1487
1488 //      Global::gridSpacing = gridPixels / Painter::zoom;
1489 //      UpdateGridBackground();
1490         SetGridSize(Global::gridSpacing * Global::zoom);
1491         update();
1492 //      zoomIndicator->setText(QString("Grid: %1\", BU: Inch").arg(Global::gridSpacing));
1493 }
1494
1495
1496 void DrawingView::keyPressEvent(QKeyEvent * event)
1497 {
1498         bool oldShift = shiftDown;
1499         bool oldCtrl = ctrlDown;
1500
1501         if (event->key() == Qt::Key_Shift)
1502                 shiftDown = true;
1503         else if (event->key() == Qt::Key_Control)
1504                 ctrlDown = true;
1505
1506         if ((oldShift != shiftDown) || (oldCtrl != ctrlDown))
1507         {
1508                 if (Global::tool)
1509                         ToolHandler(ToolKeyDown, Point(0, 0));
1510
1511                 update();
1512         }
1513
1514         if (select.size() > 0)
1515         {
1516                 if (event->key() == Qt::Key_Up)
1517                 {
1518                         TranslateObjects(select, Point(0, +1.0));
1519                         update();
1520                 }
1521                 else if (event->key() == Qt::Key_Down)
1522                 {
1523                         TranslateObjects(select, Point(0, -1.0));
1524                         update();
1525                 }
1526                 else if (event->key() == Qt::Key_Right)
1527                 {
1528                         TranslateObjects(select, Point(+1.0, 0));
1529                         update();
1530                 }
1531                 else if (event->key() == Qt::Key_Left)
1532                 {
1533                         TranslateObjects(select, Point(-1.0, 0));
1534                         update();
1535                 }
1536         }
1537 }
1538
1539
1540 void DrawingView::keyReleaseEvent(QKeyEvent * event)
1541 {
1542         bool oldShift = shiftDown;
1543         bool oldCtrl = ctrlDown;
1544
1545         if (event->key() == Qt::Key_Shift)
1546                 shiftDown = false;
1547         else if (event->key() == Qt::Key_Control)
1548                 ctrlDown = false;
1549
1550         if ((oldShift != shiftDown) || (oldCtrl != ctrlDown))
1551         {
1552                 if (Global::tool)
1553                         ToolHandler(ToolKeyUp, Point(0, 0));
1554
1555                 update();
1556         }
1557 }
1558
1559
1560 //
1561 // This looks strange, but it's really quite simple: We want a point that's
1562 // more than half-way to the next grid point to snap there while conversely we
1563 // want a point that's less than half-way to to the next grid point then snap
1564 // to the one before it. So we add half of the grid spacing to the point, then
1565 // divide by it so that we can remove the fractional part, then multiply it
1566 // back to get back to the correct answer.
1567 //
1568 Point DrawingView::SnapPointToGrid(Point point)
1569 {
1570         point += Global::gridSpacing / 2.0;             // *This* adds to Z!!!
1571         point /= Global::gridSpacing;
1572         point.x = floor(point.x);//need to fix this for negative numbers...
1573         point.y = floor(point.y);
1574         point.z = 0;                                    // Make *sure* Z doesn't go anywhere!!!
1575         point *= Global::gridSpacing;
1576         return point;
1577 }
1578
1579
1580 Point DrawingView::SnapPointToAngle(Point point)
1581 {
1582         // Snap to a single digit angle (using toolpoint #1 as the center)
1583         double angle = Vector::Angle(toolPoint[0], point);
1584         double length = Vector::Magnitude(toolPoint[0], point);
1585
1586         // Convert from radians to degrees
1587         double degAngle = angle * RADIANS_TO_DEGREES;
1588         double snapAngle = (double)((int)(degAngle + 0.5));
1589
1590         Vector v;
1591         v.SetAngleAndLength(snapAngle * DEGREES_TO_RADIANS, length);
1592         point = toolPoint[0] + v;
1593
1594         return point;
1595 }
1596
1597
1598 Rect DrawingView::GetObjectExtents(Object * obj)
1599 {
1600         // Default to empty rect, if object checks below fail for some reason
1601         Rect rect;
1602
1603         switch (obj->type)
1604         {
1605         case OTLine:
1606         case OTDimension:
1607         {
1608                 rect = Rect(obj->p[0], obj->p[1]);
1609                 break;
1610         }
1611
1612         case OTCircle:
1613         {
1614                 rect = Rect(obj->p[0], obj->p[0]);
1615                 rect.Expand(obj->radius[0]);
1616                 break;
1617         }
1618
1619         case OTArc:
1620         {
1621                 Arc * a = (Arc *)obj;
1622
1623                 double start = a->angle[0];
1624                 double end = start + a->angle[1];
1625                 rect = Rect(Point(cos(start), sin(start)), Point(cos(end), sin(end)));
1626
1627                 // If the end of the arc is before the beginning, add 360 degrees to it
1628                 if (end < start)
1629                         end += TAU;
1630
1631                 // Adjust the bounds depending on which axes are crossed
1632                 if ((start < QTR_TAU) && (end > QTR_TAU))
1633                         rect.t = 1.0;
1634
1635                 if ((start < HALF_TAU) && (end > HALF_TAU))
1636                         rect.l = -1.0;
1637
1638                 if ((start < THREE_QTR_TAU) && (end > THREE_QTR_TAU))
1639                         rect.b = -1.0;
1640
1641                 if ((start < TAU) && (end > TAU))
1642                         rect.r = 1.0;
1643
1644                 if ((start < (TAU + QTR_TAU)) && (end > (TAU + QTR_TAU)))
1645                         rect.t = 1.0;
1646
1647                 if ((start < (TAU + HALF_TAU)) && (end > (TAU + HALF_TAU)))
1648                         rect.l = -1.0;
1649
1650                 if ((start < (TAU + THREE_QTR_TAU)) && (end > (TAU + THREE_QTR_TAU)))
1651                         rect.b = -1.0;
1652
1653                 rect *= a->radius[0];
1654                 rect.Translate(a->p[0]);
1655                 break;
1656         }
1657
1658         case OTText:
1659         {
1660                 Text * t = (Text *)obj;
1661                 rect = Rect(t->p[0], Point(t->p[0].x + t->extents.Width(), t->p[0].y - t->extents.Height()));
1662                 break;
1663         }
1664
1665         case OTContainer:
1666         {
1667                 Container * c = (Container *)obj;
1668                 std::vector<void *>::iterator i = c->objects.begin();
1669                 rect = GetObjectExtents((Object *)*i);
1670                 i++;
1671
1672                 for(; i!=c->objects.end(); i++)
1673                         rect |= GetObjectExtents((Object *)*i);
1674         }
1675
1676         default:
1677                 break;
1678         }
1679
1680         return rect;
1681 }
1682
1683
1684 void DrawingView::CheckObjectBounds(void)
1685 {
1686         std::vector<void *>::iterator i;
1687
1688         for(i=document.objects.begin(); i!=document.objects.end(); i++)
1689         {
1690                 Object * obj = (Object *)(*i);
1691                 obj->selected = false;
1692
1693                 switch (obj->type)
1694                 {
1695                 case OTLine:
1696                 case OTDimension:
1697                 {
1698                         Line * l = (Line *)obj;
1699
1700                         if (Global::selection.contains(l->p[0].x, l->p[0].y) && Global::selection.contains(l->p[1].x, l->p[1].y))
1701                                 l->selected = true;
1702
1703                         break;
1704                 }
1705
1706                 case OTCircle:
1707                 {
1708                         Circle * c = (Circle *)obj;
1709
1710                         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]))
1711                                 c->selected = true;
1712
1713                         break;
1714                 }
1715
1716                 case OTArc:
1717                 {
1718                         Arc * a = (Arc *)obj;
1719
1720                         double start = a->angle[0];
1721                         double end = start + a->angle[1];
1722                         QPointF p1(cos(start), sin(start));
1723                         QPointF p2(cos(end), sin(end));
1724                         QRectF bounds(p1, p2);
1725
1726 #if 1
1727                         // Swap X/Y coordinates if they're backwards...
1728                         if (bounds.left() > bounds.right())
1729                         {
1730                                 double temp = bounds.left();
1731                                 bounds.setLeft(bounds.right());
1732                                 bounds.setRight(temp);
1733                         }
1734
1735                         if (bounds.bottom() > bounds.top())
1736                         {
1737                                 double temp = bounds.bottom();
1738                                 bounds.setBottom(bounds.top());
1739                                 bounds.setTop(temp);
1740                         }
1741 #else
1742                         // Doesn't work as advertised! For shame!
1743                         bounds = bounds.normalized();
1744 #endif
1745
1746                         // If the end of the arc is before the beginning, add 360 degrees to it
1747                         if (end < start)
1748                                 end += TAU;
1749
1750                         // Adjust the bounds depending on which axes are crossed
1751                         if ((start < QTR_TAU) && (end > QTR_TAU))
1752                                 bounds.setTop(1.0);
1753
1754                         if ((start < HALF_TAU) && (end > HALF_TAU))
1755                                 bounds.setLeft(-1.0);
1756
1757                         if ((start < THREE_QTR_TAU) && (end > THREE_QTR_TAU))
1758                                 bounds.setBottom(-1.0);
1759
1760                         if ((start < TAU) && (end > TAU))
1761                                 bounds.setRight(1.0);
1762
1763                         if ((start < (TAU + QTR_TAU)) && (end > (TAU + QTR_TAU)))
1764                                 bounds.setTop(1.0);
1765
1766                         if ((start < (TAU + HALF_TAU)) && (end > (TAU + HALF_TAU)))
1767                                 bounds.setLeft(-1.0);
1768
1769                         if ((start < (TAU + THREE_QTR_TAU)) && (end > (TAU + THREE_QTR_TAU)))
1770                                 bounds.setBottom(-1.0);
1771
1772                         bounds.setTopLeft(QPointF(bounds.left() * a->radius[0], bounds.top() * a->radius[0]));
1773                         bounds.setBottomRight(QPointF(bounds.right() * a->radius[0], bounds.bottom() * a->radius[0]));
1774                         bounds.translate(a->p[0].x, a->p[0].y);
1775
1776                         if (Global::selection.contains(bounds))
1777                                 a->selected = true;
1778
1779                         break;
1780                 }
1781
1782                 case OTText:
1783                 {
1784                         Text * t = (Text *)obj;
1785                         Rect r(obj->p[0], Point(t->p[0].x + t->extents.Width(), t->p[0].y - t->extents.Height()));
1786
1787                         if (Global::selection.contains(r.l, r.t) && Global::selection.contains(r.r, r.b))
1788                                 t->selected = true;
1789
1790                         break;
1791                 }
1792
1793                 default:
1794                         break;
1795                 }
1796         }
1797 }
1798
1799
1800 bool DrawingView::HitTestObjects(Point point)
1801 {
1802         std::vector<void *>::iterator i;
1803         numHovered = 0;
1804         bool needUpdate = false;
1805         hoverPointValid = false;
1806
1807         for(i=document.objects.begin(); i!=document.objects.end(); i++)
1808         {
1809                 Object * obj = (Object *)(*i);
1810
1811                 // If we're seeing the object we're dragging, skip it
1812                 if (draggingObject && (obj == dragged))
1813                         continue;
1814
1815                 if (HitTest(obj, point))
1816                         needUpdate = true;
1817
1818                 if (obj->hovered)
1819                 {
1820                         numHovered++;
1821 //printf("MouseMove: OBJECT HOVERED (numHovered = %i)\n", numHovered);
1822                         emit(ObjectHovered(obj));
1823                 }
1824         }
1825
1826         return needUpdate;
1827 }
1828
1829
1830 bool DrawingView::HitTest(Object * obj, Point point)
1831 {
1832         bool needUpdate = false;
1833
1834         switch (obj->type)
1835         {
1836         case OTLine:
1837         {
1838                 bool oldHP0 = obj->hitPoint[0], oldHP1 = obj->hitPoint[1], oldHO = obj->hitObject;
1839                 obj->hitPoint[0] = obj->hitPoint[1] = obj->hitObject = false;
1840                 Vector lineSegment = obj->p[1] - obj->p[0];
1841                 Vector v1 = point - obj->p[0];
1842                 Vector v2 = point - obj->p[1];
1843                 double t = Geometry::ParameterOfLineAndPoint(obj->p[0], obj->p[1], point);
1844                 double distance;
1845
1846                 if (t < 0.0)
1847                         distance = v1.Magnitude();
1848                 else if (t > 1.0)
1849                         distance = v2.Magnitude();
1850                 else
1851                         // distance = ?Det?(ls, v1) / |ls|
1852                         distance = fabs((lineSegment.x * v1.y - v1.x * lineSegment.y)
1853                                 / lineSegment.Magnitude());
1854
1855                 if ((v1.Magnitude() * Global::zoom) < 8.0)
1856                 {
1857                         obj->hitPoint[0] = true;
1858                         hoverPoint = obj->p[0];
1859                         hoverPointValid = true;
1860                 }
1861                 else if ((v2.Magnitude() * Global::zoom) < 8.0)
1862                 {
1863                         obj->hitPoint[1] = true;
1864                         hoverPoint = obj->p[1];
1865                         hoverPointValid = true;
1866                 }
1867                 else if ((distance * Global::zoom) < 5.0)
1868                         obj->hitObject = true;
1869
1870                 obj->hovered = (obj->hitPoint[0] || obj->hitPoint[1] || obj->hitObject ? true : false);
1871
1872                 if ((oldHP0 != obj->hitPoint[0]) || (oldHP1 != obj->hitPoint[1]) || (oldHO != obj->hitObject))
1873                         needUpdate = true;
1874
1875                 break;
1876         }
1877
1878         case OTCircle:
1879         {
1880                 bool oldHP = obj->hitPoint[0], oldHO = obj->hitObject;
1881                 obj->hitPoint[0] = obj->hitObject = false;
1882                 double length = Vector::Magnitude(obj->p[0], point);
1883
1884                 if ((length * Global::zoom) < 8.0)
1885                 {
1886                         obj->hitPoint[0] = true;
1887                         hoverPoint = obj->p[0];
1888                         hoverPointValid = true;
1889                 }
1890                 else if ((fabs(length - obj->radius[0]) * Global::zoom) < 2.0)
1891                         obj->hitObject = true;
1892
1893                 obj->hovered = (obj->hitPoint[0] || obj->hitObject ? true : false);
1894
1895                 if ((oldHP != obj->hitPoint[0]) || (oldHO != obj->hitObject))
1896                         needUpdate = true;
1897
1898                 break;
1899         }
1900
1901         case OTArc:
1902         {
1903                 bool oldHP0 = obj->hitPoint[0], oldHP1 = obj->hitPoint[1], oldHP2 = obj->hitPoint[2], oldHO = obj->hitObject;
1904                 obj->hitPoint[0] = obj->hitPoint[1] = obj->hitPoint[2] = obj->hitObject = false;
1905                 double length = Vector::Magnitude(obj->p[0], point);
1906                 double angle = Vector::Angle(obj->p[0], point);
1907
1908                 // Make sure we get the angle in the correct spot
1909                 if (angle < obj->angle[0])
1910                         angle += TAU;
1911
1912                 // Get the span that we're pointing at...
1913                 double span = angle - obj->angle[0];
1914
1915                 // N.B.: Still need to hit test the arc start & arc span handles...
1916                 double spanAngle = obj->angle[0] + obj->angle[1];
1917                 Point handle1 = obj->p[0] + (Vector(cos(obj->angle[0]), sin(obj->angle[0])) * obj->radius[0]);
1918                 Point handle2 = obj->p[0] + (Vector(cos(spanAngle), sin(spanAngle)) * obj->radius[0]);
1919                 double length2 = Vector::Magnitude(point, handle1);
1920                 double length3 = Vector::Magnitude(point, handle2);
1921
1922                 if ((length * Global::zoom) < 8.0)
1923                 {
1924                         obj->hitPoint[0] = true;
1925                         hoverPoint = obj->p[0];
1926                         hoverPointValid = true;
1927                 }
1928                 else if ((length2 * Global::zoom) < 8.0)
1929                 {
1930                         obj->hitPoint[1] = true;
1931                         hoverPoint = handle1;
1932                         hoverPointValid = true;
1933                 }
1934                 else if ((length3 * Global::zoom) < 8.0)
1935                 {
1936                         obj->hitPoint[2] = true;
1937                         hoverPoint = handle2;
1938                         hoverPointValid = true;
1939                 }
1940                 else if (((fabs(length - obj->radius[0]) * Global::zoom) < 2.0) && (span < obj->angle[1]))
1941                         obj->hitObject = true;
1942
1943                 obj->hovered = (obj->hitPoint[0] || obj->hitPoint[1] || obj->hitPoint[2] || obj->hitObject ? true : false);
1944
1945                 if ((oldHP0 != obj->hitPoint[0]) || (oldHP1 != obj->hitPoint[1]) || (oldHP2 != obj->hitPoint[2]) || (oldHO != obj->hitObject))
1946                         needUpdate = true;
1947
1948                 break;
1949         }
1950
1951         case OTDimension:
1952         {
1953                 bool oldHP0 = obj->hitPoint[0], oldHP1 = obj->hitPoint[1], oldHP2 = obj->hitPoint[2], oldHP3 = obj->hitPoint[3], oldHP4 = obj->hitPoint[4], oldHO = obj->hitObject;
1954                 obj->hitPoint[0] = obj->hitPoint[1] = obj->hitPoint[2] = obj->hitPoint[3] = obj->hitPoint[4] = obj->hitObject = false;
1955
1956                 Dimension * d = (Dimension *)obj;
1957
1958                 Vector orthogonal = Vector::Normal(d->lp[0], d->lp[1]);
1959                 // Get our line parallel to our points
1960                 float scaledThickness = Global::scale * obj->thickness;
1961                 Point p1 = d->lp[0] + (orthogonal * 10.0 * scaledThickness);
1962                 Point p2 = d->lp[1] + (orthogonal * 10.0 * scaledThickness);
1963                 Point p3(p1, point);
1964
1965                 Vector v1(d->p[0], point);
1966                 Vector v2(d->p[1], point);
1967                 Vector lineSegment(p1, p2);
1968                 double t = Geometry::ParameterOfLineAndPoint(p1, p2, point);
1969                 double distance;
1970                 Point midpoint = (p1 + p2) / 2.0;
1971                 Point hFSPoint = Point(midpoint, point);
1972                 Point hCS1Point = Point((p1 + midpoint) / 2.0, point);
1973                 Point hCS2Point = Point((midpoint + p2) / 2.0, point);
1974
1975                 if (t < 0.0)
1976                         distance = v1.Magnitude();
1977                 else if (t > 1.0)
1978                         distance = v2.Magnitude();
1979                 else
1980                         // distance = ?Det?(ls, v1) / |ls|
1981                         distance = fabs((lineSegment.x * p3.y - p3.x * lineSegment.y)
1982                                 / lineSegment.Magnitude());
1983
1984                 if ((v1.Magnitude() * Global::zoom) < 8.0)
1985                         obj->hitPoint[0] = true;
1986                 else if ((v2.Magnitude() * Global::zoom) < 8.0)
1987                         obj->hitPoint[1] = true;
1988                 else if ((distance * Global::zoom) < 5.0)
1989                         obj->hitObject = true;
1990
1991                 if ((hFSPoint.Magnitude() * Global::zoom) < 8.0)
1992                         obj->hitPoint[2] = true;
1993                 else if ((hCS1Point.Magnitude() * Global::zoom) < 8.0)
1994                         obj->hitPoint[3] = true;
1995                 else if ((hCS2Point.Magnitude() * Global::zoom) < 8.0)
1996                         obj->hitPoint[4] = true;
1997
1998 //              return (hitPoint1 || hitPoint2 || hitLine || hitFlipSwitch || hitChangeSwitch1 || hitChangeSwitch2 ? true : false);
1999                 obj->hovered = (obj->hitPoint[0] || obj->hitPoint[1] || obj->hitPoint[2] || obj->hitPoint[3] || obj->hitPoint[4] || obj->hitObject ? true : false);
2000
2001                 if ((oldHP0 != obj->hitPoint[0]) || (oldHP1 != obj->hitPoint[1]) || (oldHP2 != obj->hitPoint[2]) || (oldHP3 != obj->hitPoint[3]) || (oldHP4 != obj->hitPoint[4]) || (oldHO != obj->hitObject))
2002                         needUpdate = true;
2003
2004                 break;
2005         }
2006
2007         case OTText:
2008         {
2009                 Text * t = (Text *)obj;
2010                 bool oldHO = obj->hitObject;
2011                 obj->hitObject = false;
2012
2013                 Rect r(obj->p[0], Point(obj->p[0].x + t->extents.Width(), obj->p[0].y - t->extents.Height()));
2014 //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);
2015
2016                 if (r.Contains(point))
2017                         obj->hitObject = true;
2018
2019                 obj->hovered = (obj->hitObject ? true : false);
2020
2021                 if (oldHO != obj->hitObject)
2022                         needUpdate = true;
2023
2024                 break;
2025         }
2026
2027         case OTContainer:
2028         {
2029                 // Containers must be recursively tested...
2030                 Container * c = (Container *)obj;
2031                 c->hitObject = false;
2032                 c->hovered = false;
2033                 std::vector<void *>::iterator i;
2034
2035                 for(i=c->objects.begin(); i!=c->objects.end(); i++)
2036                 {
2037                         Object * cObj = (Object *)*i;
2038
2039                         if (HitTest(cObj, point))
2040                                 needUpdate = true;
2041
2042                         if (cObj->hitObject == true)
2043                                 c->hitObject = true;
2044
2045                         if (cObj->hovered == true)
2046                                 c->hovered = true;
2047                 }
2048
2049                 break;
2050         }
2051
2052         default:
2053                 break;
2054         }
2055
2056         return needUpdate;
2057 }
2058
2059
2060 bool DrawingView::HandleObjectClicked(void)
2061 {
2062         if (dragged->type == OTDimension)
2063         {
2064                 Dimension * d = (Dimension *)dragged;
2065
2066                 if (d->hitPoint[2])
2067                 {
2068                         // Hit the "flip sides" switch, so flip 'em
2069                         Point temp = d->p[0];
2070                         d->p[0] = d->p[1];
2071                         d->p[1] = temp;
2072                         return true;
2073                 }
2074                 else if (d->hitPoint[3])
2075                 {
2076                         // There are three cases here: aligned, horizontal, & vertical.
2077                         // Aligned and horizontal do the same thing, vertical goes back to
2078                         // linear.
2079                         if (d->subtype == DTLinearVert)
2080                                 d->subtype = DTLinear;
2081                         else
2082                                 d->subtype = DTLinearVert;
2083
2084                         return true;
2085                 }
2086                 else if (d->hitPoint[4])
2087                 {
2088                         // There are three cases here: aligned, horizontal, & vertical.
2089                         // Aligned and vertical do the same thing, horizontal goes back to
2090                         // linear.
2091                         if (d->subtype == DTLinearHorz)
2092                                 d->subtype = DTLinear;
2093                         else
2094                                 d->subtype = DTLinearHorz;
2095
2096                         return true;
2097                 }
2098         }
2099
2100         return false;
2101 }
2102
2103
2104 void DrawingView::HandleObjectMovement(Point point)
2105 {
2106         Point delta = point - oldPoint;
2107 //printf("HOM: old = (%f,%f), new = (%f, %f), delta = (%f, %f)\n", oldPoint.x, oldPoint.y, point.x, point.y, delta.x, delta.y);
2108 //      Object * obj = (Object *)hover[0];
2109         Object * obj = dragged;
2110 //printf("Object type = %i (size=%i), ", obj->type, hover.size());
2111 //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"));
2112
2113         switch (obj->type)
2114         {
2115         case OTLine:
2116                 if (obj->hitPoint[0])
2117                         obj->p[0] = point;
2118                 else if (obj->hitPoint[1])
2119                         obj->p[1] = point;
2120                 else if (obj->hitObject)
2121                 {
2122                         obj->p[0] += delta;
2123                         obj->p[1] += delta;
2124                 }
2125
2126                 break;
2127
2128         case OTCircle:
2129                 if (obj->hitPoint[0])
2130                         obj->p[0] = point;
2131                 else if (obj->hitObject)
2132                 {
2133 //this doesn't work. we need to save this on mouse down for this to work correctly!
2134 //                      double oldRadius = obj->radius[0];
2135                         obj->radius[0] = Vector::Magnitude(obj->p[0], point);
2136
2137                         QString text = QObject::tr("Radius: %1");//\nScale: %2%");
2138                         informativeText = text.arg(obj->radius[0], 0, 'd', 4);//.arg(obj->radius[0] / oldRadius * 100.0, 0, 'd', 0);
2139                 }
2140
2141                 break;
2142
2143         case OTArc:
2144                 if (obj->hitPoint[0])
2145                         obj->p[0] = point;
2146                 else if (obj->hitPoint[1])
2147                 {
2148                         // Change the Arc's span (handle #1)
2149                         if (shiftDown)
2150                         {
2151                                 double angle = Vector::Angle(obj->p[0], point);
2152                                 double delta = angle - obj->angle[0];
2153
2154                                 if (delta < 0)
2155                                         delta += TAU;
2156
2157                                 obj->angle[1] -= delta;
2158                                 obj->angle[0] = angle;
2159
2160                                 if (obj->angle[1] < 0)
2161                                         obj->angle[1] += TAU;
2162
2163                                 QString text = QObject::tr("Span: %1") + QChar(0x00B0) + QObject::tr("\n%2") + QChar(0x00B0) + QObject::tr(" - %3") + QChar(0x00B0);
2164                                 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);
2165                                 return;
2166                         }
2167
2168                         double angle = Vector::Angle(obj->p[0], point);
2169                         obj->angle[0] = angle;
2170                         QString text = QObject::tr("Start angle: %1") + QChar(0x00B0);
2171                         informativeText = text.arg(obj->angle[0] * RADIANS_TO_DEGREES, 0, 'd', 4);
2172                 }
2173                 else if (obj->hitPoint[2])
2174                 {
2175                         // Change the Arc's span (handle #2)
2176                         if (shiftDown)
2177                         {
2178                                 double angle = Vector::Angle(obj->p[0], point);
2179                                 obj->angle[1] = angle - obj->angle[0];
2180
2181                                 if (obj->angle[1] < 0)
2182                                         obj->angle[1] += TAU;
2183
2184                                 QString text = QObject::tr("Span: %1") + QChar(0x00B0) + QObject::tr("\n%2") + QChar(0x00B0) + QObject::tr(" - %3") + QChar(0x00B0);
2185                                 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);
2186                                 return;
2187                         }
2188
2189                         double angle = Vector::Angle(obj->p[0], point);
2190                         obj->angle[0] = angle - obj->angle[1];
2191
2192                         if (obj->angle[0] < 0)
2193                                 obj->angle[0] += TAU;
2194
2195                         QString text = QObject::tr("End angle: %1") + QChar(0x00B0);
2196                         informativeText = text.arg((obj->angle[0] + obj->angle[1]) * RADIANS_TO_DEGREES, 0, 'd', 4);
2197                 }
2198                 else if (obj->hitObject)
2199                 {
2200                         if (shiftDown)
2201                         {
2202                                 return;
2203                         }
2204
2205                         obj->radius[0] = Vector::Magnitude(obj->p[0], point);
2206                         QString text = QObject::tr("Radius: %1");
2207                         informativeText = text.arg(obj->radius[0], 0, 'd', 4);
2208                 }
2209
2210                 break;
2211
2212         case OTDimension:
2213                 if (obj->hitPoint[0])
2214                         obj->p[0] = point;
2215                 else if (obj->hitPoint[1])
2216                         obj->p[1] = point;
2217                 else if (obj->hitObject)
2218                 {
2219                         obj->p[0] += delta;
2220                         obj->p[1] += delta;
2221                 }
2222
2223                 break;
2224
2225         case OTText:
2226                 if (obj->hitObject)
2227                         obj->p[0] += delta;
2228
2229                 break;
2230
2231         case OTContainer:
2232                 // This is shitty, but works for now until I can code up something
2233                 // nicer :-)
2234                 TranslateObject(obj, delta);
2235
2236                 break;
2237         default:
2238                 break;
2239         }
2240 }
2241