]> Shamusworld >> Repos - architektonas/blob - src/drawingview.cpp
943a9b9f4cd9edb088df0a5d8a75a9824512d4cd
[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                         painter->DrawTextObject(t->p[0], t->s.c_str(), scaledThickness);
611                         break;
612                 }
613                 case OTSpline:
614                 {
615                         break;
616                 }
617                 case OTPolygon:
618                 {
619                         break;
620                 }
621                 case OTContainer:
622                 {
623                         // Containers require recursive rendering...
624                         Container * c = (Container *)obj;
625                         RenderObjects(painter, (*c).objects, layer);
626
627 //printf("Container extents: <%lf, %lf>, <%lf, %lf>\nsize: %i\n", r.l, r.t, r.r, r.b, c->objects.size());
628                         // Containers also have special indicators showing they are selected
629                         if (c->selected || c->hitObject)
630                         {
631                                 Rect r = GetObjectExtents(obj);
632                                 painter->DrawRectCorners(r);
633                         }
634
635                         break;
636                 }
637                 default:
638                         break;
639                 }
640         }
641 }
642
643
644 void DrawingView::AddHoveredToSelection(void)
645 {
646         std::vector<void *>::iterator i;
647
648         for(i=document.objects.begin(); i!=document.objects.end(); i++)
649         {
650                 if (((Object *)(*i))->hovered)
651                         ((Object *)(*i))->selected = true;
652         }
653 }
654
655
656 void DrawingView::GetSelection(std::vector<void *> & v)
657 {
658         v.clear();
659         std::vector<void *>::iterator i;
660
661         for(i=document.objects.begin(); i!=document.objects.end(); i++)
662         {
663                 if (((Object *)(*i))->selected)
664                         v.push_back(*i);
665         }
666 }
667
668
669 void DrawingView::GetHovered(std::vector<void *> & v)
670 {
671         v.clear();
672         std::vector<void *>::iterator i;
673
674         for(i=document.objects.begin(); i!=document.objects.end(); i++)
675         {
676                 if (((Object *)(*i))->hovered)
677 //              {
678 //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"));
679                         v.push_back(*i);
680 //              }
681         }
682 }
683
684
685 void DrawingView::resizeEvent(QResizeEvent * /*event*/)
686 {
687         Global::screenSize = Vector(size().width(), size().height());
688         UpdateGridBackground();
689 }
690
691
692 void DrawingView::ToolHandler(int mode, Point p)
693 {
694         // Drop angle snap until it's needed
695         angleSnap = false;
696
697         if (Global::tool == TTLine)
698                 LineHandler(mode, p);
699         else if (Global::tool == TTCircle)
700                 CircleHandler(mode, p);
701         else if (Global::tool == TTArc)
702                 ArcHandler(mode, p);
703         else if (Global::tool == TTRotate)
704                 RotateHandler(mode, p);
705         else if (Global::tool == TTMirror)
706                 MirrorHandler(mode, p);
707 }
708
709
710 void DrawingView::ToolDraw(Painter * painter)
711 {
712         if (Global::tool == TTLine)
713         {
714                 if (Global::toolState == TSNone)
715                 {
716                         painter->DrawHandle(toolPoint[0]);
717                 }
718                 else if ((Global::toolState == TSPoint2) && shiftDown)
719                 {
720                         painter->DrawHandle(toolPoint[1]);
721                 }
722                 else
723                 {
724                         painter->DrawLine(toolPoint[0], toolPoint[1]);
725                         painter->DrawHandle(toolPoint[1]);
726
727                         Vector v(toolPoint[0], toolPoint[1]);
728                         double absAngle = v.Angle() * RADIANS_TO_DEGREES;
729                         double absLength = v.Magnitude();
730                         QString text = tr("Length: %1 in.\n") + QChar(0x2221) + tr(": %2");
731                         informativeText = text.arg(absLength).arg(absAngle);
732                 }
733         }
734         else if (Global::tool == TTCircle)
735         {
736                 if (Global::toolState == TSNone)
737                 {
738                         painter->DrawHandle(toolPoint[0]);
739                 }
740                 else if ((Global::toolState == TSPoint2) && shiftDown)
741                 {
742                         painter->DrawHandle(toolPoint[1]);
743                 }
744                 else
745                 {
746                         painter->DrawCross(toolPoint[0]);
747                         double length = Vector::Magnitude(toolPoint[0], toolPoint[1]);
748                         painter->SetBrush(QBrush(Qt::NoBrush));
749                         painter->DrawEllipse(toolPoint[0], length, length);
750                         QString text = tr("Radius: %1 in.");
751                         informativeText = text.arg(length);
752                 }
753         }
754         else if (Global::tool == TTArc)
755         {
756                 if (Global::toolState == TSNone)
757                 {
758                         painter->DrawHandle(toolPoint[0]);
759                 }
760                 else if (Global::toolState == TSPoint2)
761                 {
762                         double length = Vector::Magnitude(toolPoint[0], toolPoint[1]);
763                         painter->SetBrush(QBrush(Qt::NoBrush));
764                         painter->DrawEllipse(toolPoint[0], length, length);
765                         painter->DrawLine(toolPoint[0], toolPoint[1]);
766                         painter->DrawHandle(toolPoint[1]);
767                         QString text = tr("Radius: %1 in.");
768                         informativeText = text.arg(length);
769                 }
770                 else if (Global::toolState == TSPoint3)
771                 {
772                         double angle = Vector::Angle(toolPoint[0], toolPoint[2]);
773                         painter->DrawLine(toolPoint[0], toolPoint[2]);
774                         painter->SetBrush(QBrush(Qt::NoBrush));
775                         painter->DrawEllipse(toolPoint[0], toolPoint[1].x, toolPoint[1].x);
776                         painter->DrawHandle(toolPoint[0] + (Vector(cos(angle), sin(angle)) * toolPoint[1].x));
777                         QString text = tr("Angle start: %1") + QChar(0x00B0);
778                         informativeText = text.arg(RADIANS_TO_DEGREES * angle);
779                 }
780                 else
781                 {
782                         double angle = Vector::Angle(toolPoint[0], toolPoint[3]);
783                         double span = angle - toolPoint[2].x;
784
785                         if (span < 0)
786                                 span += TAU;
787
788                         painter->DrawLine(toolPoint[0], toolPoint[3]);
789                         painter->SetBrush(QBrush(Qt::NoBrush));
790                         painter->DrawEllipse(toolPoint[0], toolPoint[1].x, toolPoint[1].x);
791                         painter->SetPen(0xFF00FF, 2.0, LSSolid);
792                         painter->DrawArc(toolPoint[0], toolPoint[1].x, toolPoint[2].x, span);
793                         painter->DrawHandle(toolPoint[0] + (Vector(cos(angle), sin(angle)) * toolPoint[1].x));
794                         QString text = tr("Arc span: %1") + QChar(0x00B0);
795                         informativeText = text.arg(RADIANS_TO_DEGREES * span);
796                 }
797         }
798         else if (Global::tool == TTRotate)
799         {
800                 if ((Global::toolState == TSNone) || (Global::toolState == TSPoint1))
801                         painter->DrawHandle(toolPoint[0]);
802                 else if ((Global::toolState == TSPoint2) && shiftDown)
803                         painter->DrawHandle(toolPoint[1]);
804                 else
805                 {
806                         if (toolPoint[0] == toolPoint[1])
807                                 return;
808
809                         painter->DrawLine(toolPoint[0], toolPoint[1]);
810
811                         double absAngle = (Vector(toolPoint[1] - toolPoint[0]).Angle()) * RADIANS_TO_DEGREES;
812                         QString text = QChar(0x2221) + QObject::tr(": %1");
813                         informativeText = text.arg(absAngle);
814
815                         if (ctrlDown)
816                                 informativeText += " (Copy)";
817                 }
818         }
819         else if (Global::tool == TTMirror)
820         {
821                 if ((Global::toolState == TSNone) || (Global::toolState == TSPoint1))
822                         painter->DrawHandle(toolPoint[0]);
823                 else if ((Global::toolState == TSPoint2) && shiftDown)
824                         painter->DrawHandle(toolPoint[1]);
825                 else
826                 {
827                         if (toolPoint[0] == toolPoint[1])
828                                 return;
829                         
830                         Point mirrorPoint = toolPoint[0] + Vector(toolPoint[1], toolPoint[0]);
831                         painter->DrawLine(mirrorPoint, toolPoint[1]);
832
833                         double absAngle = (Vector(toolPoint[1] - toolPoint[0]).Angle()) * RADIANS_TO_DEGREES;
834
835                         if (absAngle > 180.0)
836                                 absAngle -= 180.0;
837
838                         QString text = QChar(0x2221) + QObject::tr(": %1");
839                         informativeText = text.arg(absAngle);
840
841                         if (ctrlDown)
842                                 informativeText += " (Copy)";
843                 }
844         }
845 }
846
847
848 void DrawingView::LineHandler(int mode, Point p)
849 {
850         switch (mode)
851         {
852         case ToolMouseDown:
853                 if (Global::toolState == TSNone)
854                         toolPoint[0] = p;
855                 else
856                         toolPoint[1] = p;
857
858                 break;
859         case ToolMouseMove:
860                 if (Global::toolState == TSNone)
861                         toolPoint[0] = p;
862                 else
863                         toolPoint[1] = p;
864
865                 break;
866         case ToolMouseUp:
867                 if (Global::toolState == TSNone)
868                 {
869                         Global::toolState = TSPoint2;
870                         // Prevent spurious line from drawing...
871                         toolPoint[1] = toolPoint[0];
872                 }
873                 else if ((Global::toolState == TSPoint2) && shiftDown)
874                 {
875                         // Key override is telling us to make a new line, not continue the
876                         // previous one.
877                         toolPoint[0] = toolPoint[1];
878                 }
879                 else
880                 {
881                         Line * l = new Line(toolPoint[0], toolPoint[1]);
882                         l->layer = Global::activeLayer;
883                         document.objects.push_back(l);
884                         toolPoint[0] = toolPoint[1];
885                 }
886         }
887 }
888
889
890 void DrawingView::CircleHandler(int mode, Point p)
891 {
892         switch (mode)
893         {
894         case ToolMouseDown:
895                 if (Global::toolState == TSNone)
896                         toolPoint[0] = p;
897                 else
898                         toolPoint[1] = p;
899
900                 break;
901         case ToolMouseMove:
902                 if (Global::toolState == TSNone)
903                         toolPoint[0] = p;
904                 else
905                         toolPoint[1] = p;
906
907                 break;
908         case ToolMouseUp:
909                 if (Global::toolState == TSNone)
910                 {
911                         Global::toolState = TSPoint2;
912                         // Prevent spurious line from drawing...
913                         toolPoint[1] = toolPoint[0];
914                 }
915                 else if ((Global::toolState == TSPoint2) && shiftDown)
916                 {
917                         // Key override is telling us to make a new line, not continue the
918                         // previous one.
919                         toolPoint[0] = toolPoint[1];
920                 }
921                 else
922                 {
923                         double length = Vector::Magnitude(toolPoint[0], toolPoint[1]);
924                         Circle * c = new Circle(toolPoint[0], length);
925                         c->layer = Global::activeLayer;
926                         document.objects.push_back(c);
927                         toolPoint[0] = toolPoint[1];
928                         Global::toolState = TSNone;
929                 }
930         }
931 }
932
933
934 void DrawingView::ArcHandler(int mode, Point p)
935 {
936         switch (mode)
937         {
938         case ToolMouseDown:
939                 if (Global::toolState == TSNone)
940                         toolPoint[0] = p;
941                 else if (Global::toolState == TSPoint2)
942                         toolPoint[1] = p;
943                 else if (Global::toolState == TSPoint3)
944                         toolPoint[2] = p;
945                 else
946                         toolPoint[3] = p;
947
948                 break;
949         case ToolMouseMove:
950                 if (Global::toolState == TSNone)
951                         toolPoint[0] = p;
952                 else if (Global::toolState == TSPoint2)
953                         toolPoint[1] = p;
954                 else if (Global::toolState == TSPoint3)
955                 {
956                         toolPoint[2] = p;
957                         angleSnap = true;
958                 }
959                 else
960                 {
961                         toolPoint[3] = p;
962                         angleSnap = true;
963                 }
964
965                 break;
966         case ToolMouseUp:
967                 if (Global::toolState == TSNone)
968                 {
969                         // Prevent spurious line from drawing...
970                         toolPoint[1] = toolPoint[0];
971                         Global::toolState = TSPoint2;
972                 }
973                 else if (Global::toolState == TSPoint2)
974                 {
975                         if (shiftDown)
976                         {
977                                 // Key override is telling us to start arc at new center, not
978                                 // continue the current one.
979                                 toolPoint[0] = toolPoint[1];
980                                 return;
981                         }
982
983                         // Set the radius in toolPoint[1].x
984                         toolPoint[1].x = Vector::Magnitude(toolPoint[0], toolPoint[1]);
985                         Global::toolState = TSPoint3;
986                 }
987                 else if (Global::toolState == TSPoint3)
988                 {
989                         // Set the angle in toolPoint[2].x
990                         toolPoint[2].x = Vector::Angle(toolPoint[0], toolPoint[2]);
991                         Global::toolState = TSPoint4;
992                 }
993                 else
994                 {
995                         double endAngle = Vector::Angle(toolPoint[0], toolPoint[3]);
996                         double span = endAngle - toolPoint[2].x;
997
998                         if (span < 0)
999                                 span += TAU;
1000
1001                         Arc * arc = new Arc(toolPoint[0], toolPoint[1].x, toolPoint[2].x, span);
1002                         arc->layer = Global::activeLayer;
1003                         document.objects.push_back(arc);
1004                         Global::toolState = TSNone;
1005                 }
1006         }
1007 }
1008
1009
1010 void DrawingView::RotateHandler(int mode, Point p)
1011 {
1012         switch (mode)
1013         {
1014         case ToolMouseDown:
1015                 if (Global::toolState == TSNone)
1016                 {
1017                         toolPoint[0] = p;
1018                         SavePointsFrom(select, toolScratch);
1019                         Global::toolState = TSPoint1;
1020                 }
1021                 else if (Global::toolState == TSPoint1)
1022                         toolPoint[0] = p;
1023                 else
1024                         toolPoint[1] = p;
1025
1026                 break;
1027         case ToolMouseMove:
1028                 if ((Global::toolState == TSPoint1) || (Global::toolState == TSNone))
1029                         toolPoint[0] = p;
1030                 else if (Global::toolState == TSPoint2)
1031                 {
1032                         toolPoint[1] = p;
1033
1034                         if (shiftDown)
1035                                 return;
1036
1037                         angleSnap = true;
1038                         double angle = Vector(toolPoint[0], toolPoint[1]).Angle();
1039                         std::vector<void *>::iterator j = select.begin();
1040                         std::vector<Object>::iterator i = toolScratch.begin();
1041
1042                         for(; i!=toolScratch.end(); i++, j++)
1043                         {
1044                                 Object obj = *i;
1045                                 Point p1 = Geometry::RotatePointAroundPoint(obj.p[0], toolPoint[0], angle);
1046                                 Point p2 = Geometry::RotatePointAroundPoint(obj.p[1], toolPoint[0], angle);
1047                                 Object * obj2 = (Object *)(*j);
1048                                 obj2->p[0] = p1;
1049                                 obj2->p[1] = p2;
1050
1051                                 if (obj.type == OTArc)
1052                                 {
1053                                         obj2->angle[0] = obj.angle[0] + angle;
1054
1055                                         if (obj2->angle[0] > TAU)
1056                                                 obj2->angle[0] -= TAU;
1057                                 }
1058                         }
1059                 }
1060
1061                 break;
1062         case ToolMouseUp:
1063                 if (Global::toolState == TSPoint1)
1064                 {
1065                         Global::toolState = TSPoint2;
1066                         // Prevent spurious line from drawing...
1067                         toolPoint[1] = toolPoint[0];
1068                 }
1069                 else if ((Global::toolState == TSPoint2) && shiftDown)
1070                 {
1071                         // Key override is telling us to make a new line, not continue the
1072                         // previous one.
1073                         toolPoint[0] = toolPoint[1];
1074                 }
1075                 else
1076                 {
1077                         // Either we're finished with our rotate, or we're stamping a copy.
1078                         if (ctrlDown)
1079                         {
1080                                 // Stamp a copy of the selection at the current rotation & bail
1081                                 std::vector<void *> temp;
1082                                 CopyObjects(select, temp);
1083                                 ClearSelected(temp);
1084                                 AddObjectsTo(document.objects, temp);
1085                                 RestorePointsTo(select, toolScratch);
1086                                 return;
1087                         }
1088
1089                         toolPoint[0] = p;
1090                         Global::toolState = TSPoint1;
1091                         SavePointsFrom(select, toolScratch);
1092                 }
1093
1094                 break;
1095         case ToolKeyDown:
1096                 // Reset the selection if shift held down...
1097                 if (shiftDown)
1098                         RestorePointsTo(select, toolScratch);
1099
1100                 break;
1101         case ToolKeyUp:
1102                 // Reset selection when key is let up
1103                 if (!shiftDown)
1104                         RotateHandler(ToolMouseMove, toolPoint[1]);
1105
1106                 break;
1107         case ToolCleanup:
1108                 RestorePointsTo(select, toolScratch);
1109         }
1110 }
1111
1112
1113 void DrawingView::MirrorHandler(int mode, Point p)
1114 {
1115         switch (mode)
1116         {
1117         case ToolMouseDown:
1118                 if (Global::toolState == TSNone)
1119                 {
1120                         toolPoint[0] = p;
1121                         SavePointsFrom(select, toolScratch);
1122                         Global::toolState = TSPoint1;
1123                 }
1124                 else if (Global::toolState == TSPoint1)
1125                         toolPoint[0] = p;
1126                 else
1127                         toolPoint[1] = p;
1128
1129                 break;
1130         case ToolMouseMove:
1131                 if ((Global::toolState == TSPoint1) || (Global::toolState == TSNone))
1132                         toolPoint[0] = p;
1133                 else if (Global::toolState == TSPoint2)
1134                 {
1135                         toolPoint[1] = p;
1136
1137                         if (shiftDown)
1138                                 return;
1139
1140                         angleSnap = true;
1141                         double angle = Vector(toolPoint[0], toolPoint[1]).Angle();
1142                         std::vector<void *>::iterator j = select.begin();
1143                         std::vector<Object>::iterator i = toolScratch.begin();
1144
1145                         for(; i!=toolScratch.end(); i++, j++)
1146                         {
1147                                 Object obj = *i;
1148                                 Point p1 = Geometry::MirrorPointAroundLine(obj.p[0], toolPoint[0], toolPoint[1]);
1149                                 Point p2 = Geometry::MirrorPointAroundLine(obj.p[1], toolPoint[0], toolPoint[1]);
1150                                 Object * obj2 = (Object *)(*j);
1151                                 obj2->p[0] = p1;
1152                                 obj2->p[1] = p2;
1153
1154                                 if (obj.type == OTArc)
1155                                 {
1156                                         // This is 2*mirror angle - obj angle - obj span
1157                                         obj2->angle[0] = (2.0 * angle) - obj.angle[0] - obj.angle[1];
1158
1159                                         if (obj2->angle[0] > TAU)
1160                                                 obj2->angle[0] -= TAU;
1161                                 }
1162                         }
1163                 }
1164
1165                 break;
1166         case ToolMouseUp:
1167                 if (Global::toolState == TSPoint1)
1168                 {
1169                         Global::toolState = TSPoint2;
1170                         // Prevent spurious line from drawing...
1171                         toolPoint[1] = toolPoint[0];
1172                 }
1173                 else if ((Global::toolState == TSPoint2) && shiftDown)
1174                 {
1175                         // Key override is telling us to make a new line, not continue the
1176                         // previous one.
1177                         toolPoint[0] = toolPoint[1];
1178                 }
1179                 else
1180                 {
1181                         // Either we're finished with our rotate, or we're stamping a copy.
1182                         if (ctrlDown)
1183                         {
1184                                 // Stamp a copy of the selection at the current rotation & bail
1185                                 std::vector<void *> temp;
1186                                 CopyObjects(select, temp);
1187                                 ClearSelected(temp);
1188                                 AddObjectsTo(document.objects, temp);
1189                                 RestorePointsTo(select, toolScratch);
1190                                 return;
1191                         }
1192
1193                         toolPoint[0] = p;
1194                         Global::toolState = TSPoint1;
1195                         SavePointsFrom(select, toolScratch);
1196                 }
1197
1198                 break;
1199         case ToolKeyDown:
1200                 // Reset the selection if shift held down...
1201                 if (shiftDown)
1202                         RestorePointsTo(select, toolScratch);
1203
1204                 break;
1205         case ToolKeyUp:
1206                 // Reset selection when key is let up
1207                 if (!shiftDown)
1208                         MirrorHandler(ToolMouseMove, toolPoint[1]);
1209
1210                 break;
1211         case ToolCleanup:
1212                 RestorePointsTo(select, toolScratch);
1213         }
1214 }
1215
1216
1217 void DrawingView::mousePressEvent(QMouseEvent * event)
1218 {
1219         if (event->button() == Qt::LeftButton)
1220         {
1221                 Vector point = Painter::QtToCartesianCoords(Vector(event->x(), event->y()));
1222
1223                 // Handle tool processing, if any
1224                 if (Global::tool)
1225                 {
1226                         if (hoveringIntersection)
1227                                 point = intersectionPoint;
1228                         else if (Global::snapToGrid)
1229                                 point = SnapPointToGrid(point);
1230
1231                         //Also, may want to figure out if hovering over a snap point on an
1232                         //object, snap to grid if not.
1233                         // Snap to object point if valid...
1234 //                      if (Global::snapPointIsValid)
1235 //                              point = Global::snapPoint;
1236
1237                         ToolHandler(ToolMouseDown, point);
1238                         return;
1239                 }
1240
1241                 // Clear the selection only if CTRL isn't being held on click
1242                 if (!ctrlDown)
1243                         ClearSelected(document.objects);
1244 //                      ClearSelection();
1245
1246                 // If any objects are being hovered on click, add them to the selection
1247                 // & return
1248                 if (numHovered > 0)
1249                 {
1250                         AddHoveredToSelection();
1251                         update();       // needed??
1252                         GetHovered(hover);      // prolly needed
1253                         dragged = (Object *)hover[0];
1254                         draggingObject = true;
1255
1256                         // See if anything is using just a straight click on a handle
1257                         if (HandleObjectClicked())
1258                         {
1259                                 draggingObject = false;
1260                                 update();
1261                                 return;
1262                         }
1263
1264                         // Needed for grab & moving objects
1265                         // We do it *after*... why? (doesn't seem to confer any advantage...)
1266                         if (hoveringIntersection)
1267                                 oldPoint = intersectionPoint;
1268                         else if (Global::snapToGrid)
1269                                 oldPoint = SnapPointToGrid(point);
1270
1271                         return;
1272                 }
1273
1274                 // Didn't hit any object and not using a tool, so do a selection rectangle
1275                 Global::selectionInProgress = true;
1276                 Global::selection.setTopLeft(QPointF(point.x, point.y));
1277                 Global::selection.setBottomRight(QPointF(point.x, point.y));
1278         }
1279         else if (event->button() == Qt::MiddleButton)
1280         {
1281                 scrollDrag = true;
1282                 oldPoint = Vector(event->x(), event->y());
1283                 // Should also change the mouse pointer as well...
1284                 setCursor(Qt::SizeAllCursor);
1285         }
1286 }
1287
1288
1289 void DrawingView::mouseMoveEvent(QMouseEvent * event)
1290 {
1291         Vector point = Painter::QtToCartesianCoords(Vector(event->x(), event->y()));
1292         Global::selection.setBottomRight(QPointF(point.x, point.y));
1293         // Only needs to be done here, as mouse down is always preceded by movement
1294         Global::snapPointIsValid = false;
1295         hoveringIntersection = false;
1296
1297         // Scrolling...
1298         if (event->buttons() & Qt::MiddleButton)
1299         {
1300                 point = Vector(event->x(), event->y());
1301                 // Since we're using Qt coords for scrolling, we have to adjust them
1302                 // here to conform to Cartesian coords, since the origin is using
1303                 // Cartesian. :-)
1304                 Vector delta(oldPoint, point);
1305                 delta /= Global::zoom;
1306                 delta.y = -delta.y;
1307                 Global::origin -= delta;
1308
1309                 UpdateGridBackground();
1310                 update();
1311                 oldPoint = point;
1312                 return;
1313         }
1314
1315         // If we're doing a selection rect, see if any objects are engulfed by it
1316         // (implies left mouse button held down)
1317         if (Global::selectionInProgress)
1318         {
1319                 CheckObjectBounds();
1320                 update();
1321                 return;
1322         }
1323
1324         // Do object hit testing...
1325         bool needUpdate = HitTestObjects(point);
1326         GetHovered(hover);
1327
1328         // Check for multi-hover...
1329         if (numHovered > 1)
1330         {
1331 //need to check for case where hover is over 2 circles and a 3rd's center...
1332                 Object * obj1 = (Object *)hover[0], * obj2 = (Object *)hover[1];
1333
1334                 Geometry::Intersects(obj1, obj2);
1335                 int numIntersecting = Global::numIntersectParams;
1336                 double t = Global::intersectParam[0];
1337                 double u = Global::intersectParam[1];
1338
1339                 if (numIntersecting > 0)
1340                 {
1341                         Vector v1 = Geometry::GetPointForParameter(obj1, t);
1342                         Vector v2 = Geometry::GetPointForParameter(obj2, u);
1343                         QString text = tr("Intersection t=%1 (%3, %4), u=%2 (%5, %6)");
1344                         informativeText = text.arg(t).arg(u).arg(v1.x).arg(v1.y).arg(v2.x).arg(v2.y);
1345
1346                         hoveringIntersection = true;
1347                         intersectionPoint = v1;
1348                 }
1349
1350                 numIntersecting = Global::numIntersectPoints;
1351
1352                 if (numIntersecting > 0)
1353                 {
1354                         Vector v1 = Global::intersectPoint[0];
1355
1356                         if (numIntersecting == 2)
1357                         {
1358                                 Vector v2 = Global::intersectPoint[1];
1359
1360                                 if (Vector::Magnitude(v2, point) < Vector::Magnitude(v1, point))
1361                                         v1 = v2;
1362                         }
1363
1364                         QString text = tr("Intersection <%1, %2>");
1365                         informativeText = text.arg(v1.x).arg(v1.y);
1366                         hoveringIntersection = true;
1367                         intersectionPoint = v1;
1368                 }
1369         }
1370
1371         // Handle object movement (left button down & over an object)
1372         if ((event->buttons() & Qt::LeftButton) && draggingObject && !Global::tool)
1373         {
1374                 if (hoveringIntersection)
1375                         point = intersectionPoint;
1376                 else if (hoverPointValid)
1377                         point = hoverPoint;
1378                 else if (Global::snapToGrid)
1379                         point = SnapPointToGrid(point);
1380
1381                 HandleObjectMovement(point);
1382                 update();
1383                 oldPoint = point;
1384                 return;
1385         }
1386
1387         // Do tool handling, if any are active...
1388         if (Global::tool)
1389         {
1390                 if (hoveringIntersection)
1391                         point = intersectionPoint;
1392                 else if (hoverPointValid)
1393                         point = hoverPoint;
1394                 else if (Global::snapToGrid)
1395                 {
1396                         if (angleSnap)
1397                                 point = SnapPointToAngle(point);
1398                         else
1399                                 point = SnapPointToGrid(point);
1400                 }
1401
1402                 ToolHandler(ToolMouseMove, point);
1403         }
1404
1405         // This is used to draw the tool crosshair...
1406         oldPoint = point;
1407
1408         if (needUpdate || Global::tool)
1409                 update();
1410 }
1411
1412
1413 void DrawingView::mouseReleaseEvent(QMouseEvent * event)
1414 {
1415         if (event->button() == Qt::LeftButton)
1416         {
1417 //We need to update especially if nothing collided and the state needs to change. !!! FIX !!!
1418 //could set it up to use the document's update function (assumes that all object
1419 //updates are being reported correctly:
1420 //              if (document.NeedsUpdate())
1421                 // Do an update if collided with at least *one* object in the document
1422 //              if (collided)
1423                         update();
1424
1425                 if (Global::tool)
1426                 {
1427                         Vector point = Painter::QtToCartesianCoords(Vector(event->x(), event->y()));
1428                         ToolHandler(ToolMouseUp, point);
1429                         return;
1430                 }
1431
1432                 if (Global::selectionInProgress)
1433                         Global::selectionInProgress = false;
1434
1435                 informativeText.clear();
1436 // Should we be doing this automagically? Hmm...
1437                 // Clear our vectors
1438                 select.clear();
1439                 hover.clear();
1440
1441                 // Scoop 'em up
1442                 std::vector<void *>::iterator i;
1443
1444                 for(i=document.objects.begin(); i!=document.objects.end(); i++)
1445                 {
1446                         if (((Object *)(*i))->selected)
1447                                 select.push_back(*i);
1448                 }
1449
1450                 draggingObject = false;
1451         }
1452         else if (event->button() == Qt::MiddleButton)
1453         {
1454                 scrollDrag = false;
1455                 setCursor(Qt::ArrowCursor);
1456         }
1457 }
1458
1459
1460 void DrawingView::wheelEvent(QWheelEvent * event)
1461 {
1462         double zoomFactor = 1.25;
1463         QSize sizeWin = size();
1464         Vector center(sizeWin.width() / 2.0, sizeWin.height() / 2.0);
1465         center = Painter::QtToCartesianCoords(center);
1466
1467         // This is not centering for some reason. Need to figure out why. :-/
1468         if (event->delta() > 0)
1469         {
1470                 Vector newOrigin = center - ((center - Global::origin) / zoomFactor);
1471                 Global::origin = newOrigin;
1472                 Global::zoom *= zoomFactor;
1473         }
1474         else
1475         {
1476                 Vector newOrigin = center + ((-center + Global::origin) * zoomFactor);
1477                 Global::origin = newOrigin;
1478                 Global::zoom /= zoomFactor;
1479         }
1480
1481 //      Global::gridSpacing = gridPixels / Painter::zoom;
1482 //      UpdateGridBackground();
1483         SetGridSize(Global::gridSpacing * Global::zoom);
1484         update();
1485 //      zoomIndicator->setText(QString("Grid: %1\", BU: Inch").arg(Global::gridSpacing));
1486 }
1487
1488
1489 void DrawingView::keyPressEvent(QKeyEvent * event)
1490 {
1491         bool oldShift = shiftDown;
1492         bool oldCtrl = ctrlDown;
1493
1494         if (event->key() == Qt::Key_Shift)
1495                 shiftDown = true;
1496         else if (event->key() == Qt::Key_Control)
1497                 ctrlDown = true;
1498
1499         if ((oldShift != shiftDown) || (oldCtrl != ctrlDown))
1500         {
1501                 if (Global::tool)
1502                         ToolHandler(ToolKeyDown, Point(0, 0));
1503
1504                 update();
1505         }
1506
1507         if (select.size() > 0)
1508         {
1509                 if (event->key() == Qt::Key_Up)
1510                 {
1511                         TranslateObjects(select, Point(0, +1.0));
1512                         update();
1513                 }
1514                 else if (event->key() == Qt::Key_Down)
1515                 {
1516                         TranslateObjects(select, Point(0, -1.0));
1517                         update();
1518                 }
1519                 else if (event->key() == Qt::Key_Right)
1520                 {
1521                         TranslateObjects(select, Point(+1.0, 0));
1522                         update();
1523                 }
1524                 else if (event->key() == Qt::Key_Left)
1525                 {
1526                         TranslateObjects(select, Point(-1.0, 0));
1527                         update();
1528                 }
1529         }
1530 }
1531
1532
1533 void DrawingView::keyReleaseEvent(QKeyEvent * event)
1534 {
1535         bool oldShift = shiftDown;
1536         bool oldCtrl = ctrlDown;
1537
1538         if (event->key() == Qt::Key_Shift)
1539                 shiftDown = false;
1540         else if (event->key() == Qt::Key_Control)
1541                 ctrlDown = false;
1542
1543         if ((oldShift != shiftDown) || (oldCtrl != ctrlDown))
1544         {
1545                 if (Global::tool)
1546                         ToolHandler(ToolKeyUp, Point(0, 0));
1547
1548                 update();
1549         }
1550 }
1551
1552
1553 //
1554 // This looks strange, but it's really quite simple: We want a point that's
1555 // more than half-way to the next grid point to snap there while conversely we
1556 // want a point that's less than half-way to to the next grid point then snap
1557 // to the one before it. So we add half of the grid spacing to the point, then
1558 // divide by it so that we can remove the fractional part, then multiply it
1559 // back to get back to the correct answer.
1560 //
1561 Point DrawingView::SnapPointToGrid(Point point)
1562 {
1563         point += Global::gridSpacing / 2.0;             // *This* adds to Z!!!
1564         point /= Global::gridSpacing;
1565         point.x = floor(point.x);//need to fix this for negative numbers...
1566         point.y = floor(point.y);
1567         point.z = 0;                                    // Make *sure* Z doesn't go anywhere!!!
1568         point *= Global::gridSpacing;
1569         return point;
1570 }
1571
1572
1573 Point DrawingView::SnapPointToAngle(Point point)
1574 {
1575         // Snap to a single digit angle (using toolpoint #1 as the center)
1576         double angle = Vector::Angle(toolPoint[0], point);
1577         double length = Vector::Magnitude(toolPoint[0], point);
1578
1579         // Convert from radians to degrees
1580         double degAngle = angle * RADIANS_TO_DEGREES;
1581         double snapAngle = (double)((int)(degAngle + 0.5));
1582
1583         Vector v;
1584         v.SetAngleAndLength(snapAngle * DEGREES_TO_RADIANS, length);
1585         point = toolPoint[0] + v;
1586
1587         return point;
1588 }
1589
1590
1591 Rect DrawingView::GetObjectExtents(Object * obj)
1592 {
1593         // Default to empty rect, if object checks below fail for some reason
1594         Rect rect;
1595
1596         switch (obj->type)
1597         {
1598         case OTLine:
1599         {
1600                 rect = Rect(obj->p[0], obj->p[1]);
1601                 break;
1602         }
1603         case OTCircle:
1604         {
1605                 rect = Rect(obj->p[0], obj->p[0]);
1606                 rect.Expand(obj->radius[0]);
1607                 break;
1608         }
1609         case OTArc:
1610         {
1611                 Arc * a = (Arc *)obj;
1612
1613                 double start = a->angle[0];
1614                 double end = start + a->angle[1];
1615                 rect = Rect(Point(cos(start), sin(start)), Point(cos(end), sin(end)));
1616
1617                 // If the end of the arc is before the beginning, add 360 degrees to it
1618                 if (end < start)
1619                         end += TAU;
1620
1621                 // Adjust the bounds depending on which axes are crossed
1622                 if ((start < QTR_TAU) && (end > QTR_TAU))
1623                         rect.t = 1.0;
1624
1625                 if ((start < HALF_TAU) && (end > HALF_TAU))
1626                         rect.l = -1.0;
1627
1628                 if ((start < THREE_QTR_TAU) && (end > THREE_QTR_TAU))
1629                         rect.b = -1.0;
1630
1631                 if ((start < TAU) && (end > TAU))
1632                         rect.r = 1.0;
1633
1634                 if ((start < (TAU + QTR_TAU)) && (end > (TAU + QTR_TAU)))
1635                         rect.t = 1.0;
1636
1637                 if ((start < (TAU + HALF_TAU)) && (end > (TAU + HALF_TAU)))
1638                         rect.l = -1.0;
1639
1640                 if ((start < (TAU + THREE_QTR_TAU)) && (end > (TAU + THREE_QTR_TAU)))
1641                         rect.b = -1.0;
1642
1643                 rect *= a->radius[0];
1644                 rect.Translate(a->p[0]);
1645
1646                 break;
1647         }
1648         case OTContainer:
1649         {
1650                 Container * c = (Container *)obj;
1651                 std::vector<void *>::iterator i = c->objects.begin();
1652                 rect = GetObjectExtents((Object *)*i);
1653                 i++;
1654
1655                 for(; i!=c->objects.end(); i++)
1656                         rect |= GetObjectExtents((Object *)*i);
1657         }
1658         default:
1659                 break;
1660         }
1661
1662         return rect;
1663 }
1664
1665
1666 void DrawingView::CheckObjectBounds(void)
1667 {
1668         std::vector<void *>::iterator i;
1669
1670         for(i=document.objects.begin(); i!=document.objects.end(); i++)
1671         {
1672                 Object * obj = (Object *)(*i);
1673                 obj->selected = false;
1674
1675                 switch (obj->type)
1676                 {
1677                 case OTLine:
1678                 {
1679                         Line * l = (Line *)obj;
1680
1681                         if (Global::selection.contains(l->p[0].x, l->p[0].y) && Global::selection.contains(l->p[1].x, l->p[1].y))
1682                                 l->selected = true;
1683
1684                         break;
1685                 }
1686                 case OTCircle:
1687                 {
1688                         Circle * c = (Circle *)obj;
1689
1690                         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]))
1691                                 c->selected = true;
1692
1693                         break;
1694                 }
1695                 case OTArc:
1696                 {
1697                         Arc * a = (Arc *)obj;
1698
1699                         double start = a->angle[0];
1700                         double end = start + a->angle[1];
1701                         QPointF p1(cos(start), sin(start));
1702                         QPointF p2(cos(end), sin(end));
1703                         QRectF bounds(p1, p2);
1704
1705 #if 1
1706                         // Swap X/Y coordinates if they're backwards...
1707                         if (bounds.left() > bounds.right())
1708                         {
1709                                 double temp = bounds.left();
1710                                 bounds.setLeft(bounds.right());
1711                                 bounds.setRight(temp);
1712                         }
1713
1714                         if (bounds.bottom() > bounds.top())
1715                         {
1716                                 double temp = bounds.bottom();
1717                                 bounds.setBottom(bounds.top());
1718                                 bounds.setTop(temp);
1719                         }
1720 #else
1721                         // Doesn't work as advertised! For shame!
1722                         bounds = bounds.normalized();
1723 #endif
1724
1725                         // If the end of the arc is before the beginning, add 360 degrees to it
1726                         if (end < start)
1727                                 end += TAU;
1728
1729                         // Adjust the bounds depending on which axes are crossed
1730                         if ((start < QTR_TAU) && (end > QTR_TAU))
1731                                 bounds.setTop(1.0);
1732
1733                         if ((start < HALF_TAU) && (end > HALF_TAU))
1734                                 bounds.setLeft(-1.0);
1735
1736                         if ((start < THREE_QTR_TAU) && (end > THREE_QTR_TAU))
1737                                 bounds.setBottom(-1.0);
1738
1739                         if ((start < TAU) && (end > TAU))
1740                                 bounds.setRight(1.0);
1741
1742                         if ((start < (TAU + QTR_TAU)) && (end > (TAU + QTR_TAU)))
1743                                 bounds.setTop(1.0);
1744
1745                         if ((start < (TAU + HALF_TAU)) && (end > (TAU + HALF_TAU)))
1746                                 bounds.setLeft(-1.0);
1747
1748                         if ((start < (TAU + THREE_QTR_TAU)) && (end > (TAU + THREE_QTR_TAU)))
1749                                 bounds.setBottom(-1.0);
1750
1751                         bounds.setTopLeft(QPointF(bounds.left() * a->radius[0], bounds.top() * a->radius[0]));
1752                         bounds.setBottomRight(QPointF(bounds.right() * a->radius[0], bounds.bottom() * a->radius[0]));
1753                         bounds.translate(a->p[0].x, a->p[0].y);
1754
1755                         if (Global::selection.contains(bounds))
1756                                 a->selected = true;
1757
1758                         break;
1759                 }
1760                 default:
1761                         break;
1762                 }
1763         }
1764 }
1765
1766
1767 bool DrawingView::HitTestObjects(Point point)
1768 {
1769         std::vector<void *>::iterator i;
1770         numHovered = 0;
1771         bool needUpdate = false;
1772         hoverPointValid = false;
1773
1774         for(i=document.objects.begin(); i!=document.objects.end(); i++)
1775         {
1776                 Object * obj = (Object *)(*i);
1777
1778                 // If we're seeing the object we're dragging, skip it
1779                 if (draggingObject && (obj == dragged))
1780                         continue;
1781
1782                 if (HitTest(obj, point))
1783                         needUpdate = true;
1784
1785                 if (obj->hovered)
1786                 {
1787                         numHovered++;
1788 //printf("MouseMove: OBJECT HOVERED (numHovered = %i)\n", numHovered);
1789                         emit(ObjectHovered(obj));
1790                 }
1791         }
1792
1793         return needUpdate;
1794 }
1795
1796
1797 bool DrawingView::HitTest(Object * obj, Point point)
1798 {
1799         bool needUpdate = false;
1800
1801         switch (obj->type)
1802         {
1803         case OTLine:
1804         {
1805                 bool oldHP0 = obj->hitPoint[0], oldHP1 = obj->hitPoint[1], oldHO = obj->hitObject;
1806                 obj->hitPoint[0] = obj->hitPoint[1] = obj->hitObject = false;
1807                 Vector lineSegment = obj->p[1] - obj->p[0];
1808                 Vector v1 = point - obj->p[0];
1809                 Vector v2 = point - obj->p[1];
1810                 double t = Geometry::ParameterOfLineAndPoint(obj->p[0], obj->p[1], point);
1811                 double distance;
1812
1813                 if (t < 0.0)
1814                         distance = v1.Magnitude();
1815                 else if (t > 1.0)
1816                         distance = v2.Magnitude();
1817                 else
1818                         // distance = ?Det?(ls, v1) / |ls|
1819                         distance = fabs((lineSegment.x * v1.y - v1.x * lineSegment.y)
1820                                 / lineSegment.Magnitude());
1821
1822                 if ((v1.Magnitude() * Global::zoom) < 8.0)
1823                 {
1824                         obj->hitPoint[0] = true;
1825                         hoverPoint = obj->p[0];
1826                         hoverPointValid = true;
1827                 }
1828                 else if ((v2.Magnitude() * Global::zoom) < 8.0)
1829                 {
1830                         obj->hitPoint[1] = true;
1831                         hoverPoint = obj->p[1];
1832                         hoverPointValid = true;
1833                 }
1834                 else if ((distance * Global::zoom) < 5.0)
1835                         obj->hitObject = true;
1836
1837                 obj->hovered = (obj->hitPoint[0] || obj->hitPoint[1] || obj->hitObject ? true : false);
1838
1839                 if ((oldHP0 != obj->hitPoint[0]) || (oldHP1 != obj->hitPoint[1]) || (oldHO != obj->hitObject))
1840                         needUpdate = true;
1841
1842                 break;
1843         }
1844         case OTCircle:
1845         {
1846                 bool oldHP = obj->hitPoint[0], oldHO = obj->hitObject;
1847                 obj->hitPoint[0] = obj->hitObject = false;
1848                 double length = Vector::Magnitude(obj->p[0], point);
1849
1850                 if ((length * Global::zoom) < 8.0)
1851                         obj->hitPoint[0] = true;
1852                 else if ((fabs(length - obj->radius[0]) * Global::zoom) < 2.0)
1853                         obj->hitObject = true;
1854
1855                 obj->hovered = (obj->hitPoint[0] || obj->hitObject ? true : false);
1856
1857                 if ((oldHP != obj->hitPoint[0]) || (oldHO != obj->hitObject))
1858                         needUpdate = true;
1859
1860                 break;
1861         }
1862         case OTArc:
1863         {
1864                 bool oldHP0 = obj->hitPoint[0], oldHP1 = obj->hitPoint[1], oldHP2 = obj->hitPoint[2], oldHO = obj->hitObject;
1865                 obj->hitPoint[0] = obj->hitPoint[1] = obj->hitPoint[2] = obj->hitObject = false;
1866                 double length = Vector::Magnitude(obj->p[0], point);
1867                 double angle = Vector::Angle(obj->p[0], point);
1868
1869                 // Make sure we get the angle in the correct spot
1870                 if (angle < obj->angle[0])
1871                         angle += TAU;
1872
1873                 // Get the span that we're pointing at...
1874                 double span = angle - obj->angle[0];
1875
1876                 // N.B.: Still need to hit test the arc start & arc span handles...
1877                 double spanAngle = obj->angle[0] + obj->angle[1];
1878                 Point handle1 = obj->p[0] + (Vector(cos(obj->angle[0]), sin(obj->angle[0])) * obj->radius[0]);
1879                 Point handle2 = obj->p[0] + (Vector(cos(spanAngle), sin(spanAngle)) * obj->radius[0]);
1880                 double length2 = Vector::Magnitude(point, handle1);
1881                 double length3 = Vector::Magnitude(point, handle2);
1882
1883                 if ((length * Global::zoom) < 8.0)
1884                         obj->hitPoint[0] = true;
1885                 else if ((length2 * Global::zoom) < 8.0)
1886                 {
1887                         obj->hitPoint[1] = true;
1888                         hoverPoint = handle1;
1889                         hoverPointValid = true;
1890                 }
1891                 else if ((length3 * Global::zoom) < 8.0)
1892                 {
1893                         obj->hitPoint[2] = true;
1894                         hoverPoint = handle2;
1895                         hoverPointValid = true;
1896                 }
1897                 else if (((fabs(length - obj->radius[0]) * Global::zoom) < 2.0) && (span < obj->angle[1]))
1898                         obj->hitObject = true;
1899
1900                 obj->hovered = (obj->hitPoint[0] || obj->hitPoint[1] || obj->hitPoint[2] || obj->hitObject ? true : false);
1901
1902                 if ((oldHP0 != obj->hitPoint[0]) || (oldHP1 != obj->hitPoint[1]) || (oldHP2 != obj->hitPoint[2]) || (oldHO != obj->hitObject))
1903                         needUpdate = true;
1904
1905                 break;
1906         }
1907         case OTDimension:
1908         {
1909                 bool oldHP0 = obj->hitPoint[0], oldHP1 = obj->hitPoint[1], oldHP2 = obj->hitPoint[2], oldHP3 = obj->hitPoint[3], oldHP4 = obj->hitPoint[4], oldHO = obj->hitObject;
1910                 obj->hitPoint[0] = obj->hitPoint[1] = obj->hitPoint[2] = obj->hitPoint[3] = obj->hitPoint[4] = obj->hitObject = false;
1911
1912                 Dimension * d = (Dimension *)obj;
1913
1914                 Vector orthogonal = Vector::Normal(d->lp[0], d->lp[1]);
1915                 // Get our line parallel to our points
1916                 float scaledThickness = Global::scale * obj->thickness;
1917                 Point p1 = d->lp[0] + (orthogonal * 10.0 * scaledThickness);
1918                 Point p2 = d->lp[1] + (orthogonal * 10.0 * scaledThickness);
1919                 Point p3(p1, point);
1920
1921                 Vector v1(d->p[0], point);
1922                 Vector v2(d->p[1], point);
1923                 Vector lineSegment(p1, p2);
1924                 double t = Geometry::ParameterOfLineAndPoint(p1, p2, point);
1925                 double distance;
1926                 Point midpoint = (p1 + p2) / 2.0;
1927                 Point hFSPoint = Point(midpoint, point);
1928                 Point hCS1Point = Point((p1 + midpoint) / 2.0, point);
1929                 Point hCS2Point = Point((midpoint + p2) / 2.0, point);
1930
1931                 if (t < 0.0)
1932                         distance = v1.Magnitude();
1933                 else if (t > 1.0)
1934                         distance = v2.Magnitude();
1935                 else
1936                         // distance = ?Det?(ls, v1) / |ls|
1937                         distance = fabs((lineSegment.x * p3.y - p3.x * lineSegment.y)
1938                                 / lineSegment.Magnitude());
1939
1940                 if ((v1.Magnitude() * Global::zoom) < 8.0)
1941                         obj->hitPoint[0] = true;
1942                 else if ((v2.Magnitude() * Global::zoom) < 8.0)
1943                         obj->hitPoint[1] = true;
1944                 else if ((distance * Global::zoom) < 5.0)
1945                         obj->hitObject = true;
1946
1947                 if ((hFSPoint.Magnitude() * Global::zoom) < 8.0)
1948                         obj->hitPoint[2] = true;
1949                 else if ((hCS1Point.Magnitude() * Global::zoom) < 8.0)
1950                         obj->hitPoint[3] = true;
1951                 else if ((hCS2Point.Magnitude() * Global::zoom) < 8.0)
1952                         obj->hitPoint[4] = true;
1953
1954 //              return (hitPoint1 || hitPoint2 || hitLine || hitFlipSwitch || hitChangeSwitch1 || hitChangeSwitch2 ? true : false);
1955                 obj->hovered = (obj->hitPoint[0] || obj->hitPoint[1] || obj->hitPoint[2] || obj->hitPoint[3] || obj->hitPoint[4] || obj->hitObject ? true : false);
1956
1957                 if ((oldHP0 != obj->hitPoint[0]) || (oldHP1 != obj->hitPoint[1]) || (oldHP2 != obj->hitPoint[2]) || (oldHP3 != obj->hitPoint[3]) || (oldHP4 != obj->hitPoint[4]) || (oldHO != obj->hitObject))
1958                         needUpdate = true;
1959
1960
1961                 break;
1962         }
1963         case OTContainer:
1964         {
1965                 // Containers must be recursively tested...
1966                 Container * c = (Container *)obj;
1967                 c->hitObject = false;
1968                 c->hovered = false;
1969                 std::vector<void *>::iterator i;
1970
1971                 for(i=c->objects.begin(); i!=c->objects.end(); i++)
1972                 {
1973                         Object * cObj = (Object *)*i;
1974
1975                         if (HitTest(cObj, point))
1976                                 needUpdate = true;
1977
1978                         if (cObj->hitObject == true)
1979                                 c->hitObject = true;
1980
1981                         if (cObj->hovered == true)
1982                                 c->hovered = true;
1983                 }
1984
1985                 break;
1986         }
1987         default:
1988                 break;
1989         }
1990
1991         return needUpdate;
1992 }
1993
1994
1995 bool DrawingView::HandleObjectClicked(void)
1996 {
1997         if (dragged->type == OTDimension)
1998         {
1999                 Dimension * d = (Dimension *)dragged;
2000
2001                 if (d->hitPoint[2])
2002                 {
2003                         // Hit the "flip sides" switch, so flip 'em
2004                         Point temp = d->p[0];
2005                         d->p[0] = d->p[1];
2006                         d->p[1] = temp;
2007                         return true;
2008                 }
2009                 else if (d->hitPoint[3])
2010                 {
2011                         // There are three cases here: aligned, horizontal, & vertical.
2012                         // Aligned and horizontal do the same thing, vertical goes back to
2013                         // linear.
2014                         if (d->subtype == DTLinearVert)
2015                                 d->subtype = DTLinear;
2016                         else
2017                                 d->subtype = DTLinearVert;
2018
2019                         return true;
2020                 }
2021                 else if (d->hitPoint[4])
2022                 {
2023                         // There are three cases here: aligned, horizontal, & vertical.
2024                         // Aligned and vertical do the same thing, horizontal goes back to
2025                         // linear.
2026                         if (d->subtype == DTLinearHorz)
2027                                 d->subtype = DTLinear;
2028                         else
2029                                 d->subtype = DTLinearHorz;
2030
2031                         return true;
2032                 }
2033         }
2034
2035         return false;
2036 }
2037
2038
2039 void DrawingView::HandleObjectMovement(Point point)
2040 {
2041         Point delta = point - oldPoint;
2042 //printf("HOM: old = (%f,%f), new = (%f, %f), delta = (%f, %f)\n", oldPoint.x, oldPoint.y, point.x, point.y, delta.x, delta.y);
2043 //      Object * obj = (Object *)hover[0];
2044         Object * obj = dragged;
2045 //printf("Object type = %i (size=%i), ", obj->type, hover.size());
2046 //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"));
2047
2048         switch (obj->type)
2049         {
2050         case OTLine:
2051                 if (obj->hitPoint[0])
2052                         obj->p[0] = point;
2053                 else if (obj->hitPoint[1])
2054                         obj->p[1] = point;
2055                 else if (obj->hitObject)
2056                 {
2057                         obj->p[0] += delta;
2058                         obj->p[1] += delta;
2059                 }
2060
2061                 break;
2062
2063         case OTCircle:
2064                 if (obj->hitPoint[0])
2065                         obj->p[0] = point;
2066                 else if (obj->hitObject)
2067                 {
2068 //this doesn't work. we need to save this on mouse down for this to work correctly!
2069 //                      double oldRadius = obj->radius[0];
2070                         obj->radius[0] = Vector::Magnitude(obj->p[0], point);
2071
2072                         QString text = QObject::tr("Radius: %1");//\nScale: %2%");
2073                         informativeText = text.arg(obj->radius[0], 0, 'd', 4);//.arg(obj->radius[0] / oldRadius * 100.0, 0, 'd', 0);
2074                 }
2075
2076                 break;
2077
2078         case OTArc:
2079                 if (obj->hitPoint[0])
2080                         obj->p[0] = point;
2081                 else if (obj->hitPoint[1])
2082                 {
2083                         // Change the Arc's span (handle #1)
2084                         if (shiftDown)
2085                         {
2086                                 double angle = Vector::Angle(obj->p[0], point);
2087                                 double delta = angle - obj->angle[0];
2088
2089                                 if (delta < 0)
2090                                         delta += TAU;
2091
2092                                 obj->angle[1] -= delta;
2093                                 obj->angle[0] = angle;
2094
2095                                 if (obj->angle[1] < 0)
2096                                         obj->angle[1] += TAU;
2097
2098                                 QString text = QObject::tr("Span: %1") + QChar(0x00B0) + QObject::tr("\n%2") + QChar(0x00B0) + QObject::tr(" - %3") + QChar(0x00B0);
2099                                 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);
2100                                 return;
2101                         }
2102
2103                         double angle = Vector::Angle(obj->p[0], point);
2104                         obj->angle[0] = angle;
2105                         QString text = QObject::tr("Start angle: %1") + QChar(0x00B0);
2106                         informativeText = text.arg(obj->angle[0] * RADIANS_TO_DEGREES, 0, 'd', 4);
2107                 }
2108                 else if (obj->hitPoint[2])
2109                 {
2110                         // Change the Arc's span (handle #2)
2111                         if (shiftDown)
2112                         {
2113                                 double angle = Vector::Angle(obj->p[0], point);
2114                                 obj->angle[1] = angle - obj->angle[0];
2115
2116                                 if (obj->angle[1] < 0)
2117                                         obj->angle[1] += TAU;
2118
2119                                 QString text = QObject::tr("Span: %1") + QChar(0x00B0) + QObject::tr("\n%2") + QChar(0x00B0) + QObject::tr(" - %3") + QChar(0x00B0);
2120                                 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);
2121                                 return;
2122                         }
2123
2124                         double angle = Vector::Angle(obj->p[0], point);
2125                         obj->angle[0] = angle - obj->angle[1];
2126
2127                         if (obj->angle[0] < 0)
2128                                 obj->angle[0] += TAU;
2129
2130                         QString text = QObject::tr("End angle: %1") + QChar(0x00B0);
2131                         informativeText = text.arg((obj->angle[0] + obj->angle[1]) * RADIANS_TO_DEGREES, 0, 'd', 4);
2132                 }
2133                 else if (obj->hitObject)
2134                 {
2135                         if (shiftDown)
2136                         {
2137                                 return;
2138                         }
2139
2140                         obj->radius[0] = Vector::Magnitude(obj->p[0], point);
2141                         QString text = QObject::tr("Radius: %1");
2142                         informativeText = text.arg(obj->radius[0], 0, 'd', 4);
2143                 }
2144
2145                 break;
2146
2147         case OTDimension:
2148                 if (obj->hitPoint[0])
2149                         obj->p[0] = point;
2150                 else if (obj->hitPoint[1])
2151                         obj->p[1] = point;
2152                 else if (obj->hitObject)
2153                 {
2154                         obj->p[0] += delta;
2155                         obj->p[1] += delta;
2156                 }
2157
2158                 break;
2159
2160         case OTContainer:
2161                 // This is shitty, but works for now until I can code up something
2162                 // nicer :-)
2163                 TranslateObject(obj, delta);
2164
2165                 break;
2166         default:
2167                 break;
2168         }
2169 }
2170