]> Shamusworld >> Repos - architektonas/blob - src/drawingview.cpp
ca063c51d548070b1a000158d15b0d53e9fdcf7e
[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         case OTDimension:
1600         {
1601                 rect = Rect(obj->p[0], obj->p[1]);
1602                 break;
1603         }
1604         case OTCircle:
1605         {
1606                 rect = Rect(obj->p[0], obj->p[0]);
1607                 rect.Expand(obj->radius[0]);
1608                 break;
1609         }
1610         case OTArc:
1611         {
1612                 Arc * a = (Arc *)obj;
1613
1614                 double start = a->angle[0];
1615                 double end = start + a->angle[1];
1616                 rect = Rect(Point(cos(start), sin(start)), Point(cos(end), sin(end)));
1617
1618                 // If the end of the arc is before the beginning, add 360 degrees to it
1619                 if (end < start)
1620                         end += TAU;
1621
1622                 // Adjust the bounds depending on which axes are crossed
1623                 if ((start < QTR_TAU) && (end > QTR_TAU))
1624                         rect.t = 1.0;
1625
1626                 if ((start < HALF_TAU) && (end > HALF_TAU))
1627                         rect.l = -1.0;
1628
1629                 if ((start < THREE_QTR_TAU) && (end > THREE_QTR_TAU))
1630                         rect.b = -1.0;
1631
1632                 if ((start < TAU) && (end > TAU))
1633                         rect.r = 1.0;
1634
1635                 if ((start < (TAU + QTR_TAU)) && (end > (TAU + QTR_TAU)))
1636                         rect.t = 1.0;
1637
1638                 if ((start < (TAU + HALF_TAU)) && (end > (TAU + HALF_TAU)))
1639                         rect.l = -1.0;
1640
1641                 if ((start < (TAU + THREE_QTR_TAU)) && (end > (TAU + THREE_QTR_TAU)))
1642                         rect.b = -1.0;
1643
1644                 rect *= a->radius[0];
1645                 rect.Translate(a->p[0]);
1646
1647                 break;
1648         }
1649         case OTContainer:
1650         {
1651                 Container * c = (Container *)obj;
1652                 std::vector<void *>::iterator i = c->objects.begin();
1653                 rect = GetObjectExtents((Object *)*i);
1654                 i++;
1655
1656                 for(; i!=c->objects.end(); i++)
1657                         rect |= GetObjectExtents((Object *)*i);
1658         }
1659         default:
1660                 break;
1661         }
1662
1663         return rect;
1664 }
1665
1666
1667 void DrawingView::CheckObjectBounds(void)
1668 {
1669         std::vector<void *>::iterator i;
1670
1671         for(i=document.objects.begin(); i!=document.objects.end(); i++)
1672         {
1673                 Object * obj = (Object *)(*i);
1674                 obj->selected = false;
1675
1676                 switch (obj->type)
1677                 {
1678                 case OTLine:
1679                 case OTDimension:
1680                 {
1681                         Line * l = (Line *)obj;
1682
1683                         if (Global::selection.contains(l->p[0].x, l->p[0].y) && Global::selection.contains(l->p[1].x, l->p[1].y))
1684                                 l->selected = true;
1685
1686                         break;
1687                 }
1688                 case OTCircle:
1689                 {
1690                         Circle * c = (Circle *)obj;
1691
1692                         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]))
1693                                 c->selected = true;
1694
1695                         break;
1696                 }
1697                 case OTArc:
1698                 {
1699                         Arc * a = (Arc *)obj;
1700
1701                         double start = a->angle[0];
1702                         double end = start + a->angle[1];
1703                         QPointF p1(cos(start), sin(start));
1704                         QPointF p2(cos(end), sin(end));
1705                         QRectF bounds(p1, p2);
1706
1707 #if 1
1708                         // Swap X/Y coordinates if they're backwards...
1709                         if (bounds.left() > bounds.right())
1710                         {
1711                                 double temp = bounds.left();
1712                                 bounds.setLeft(bounds.right());
1713                                 bounds.setRight(temp);
1714                         }
1715
1716                         if (bounds.bottom() > bounds.top())
1717                         {
1718                                 double temp = bounds.bottom();
1719                                 bounds.setBottom(bounds.top());
1720                                 bounds.setTop(temp);
1721                         }
1722 #else
1723                         // Doesn't work as advertised! For shame!
1724                         bounds = bounds.normalized();
1725 #endif
1726
1727                         // If the end of the arc is before the beginning, add 360 degrees to it
1728                         if (end < start)
1729                                 end += TAU;
1730
1731                         // Adjust the bounds depending on which axes are crossed
1732                         if ((start < QTR_TAU) && (end > QTR_TAU))
1733                                 bounds.setTop(1.0);
1734
1735                         if ((start < HALF_TAU) && (end > HALF_TAU))
1736                                 bounds.setLeft(-1.0);
1737
1738                         if ((start < THREE_QTR_TAU) && (end > THREE_QTR_TAU))
1739                                 bounds.setBottom(-1.0);
1740
1741                         if ((start < TAU) && (end > TAU))
1742                                 bounds.setRight(1.0);
1743
1744                         if ((start < (TAU + QTR_TAU)) && (end > (TAU + QTR_TAU)))
1745                                 bounds.setTop(1.0);
1746
1747                         if ((start < (TAU + HALF_TAU)) && (end > (TAU + HALF_TAU)))
1748                                 bounds.setLeft(-1.0);
1749
1750                         if ((start < (TAU + THREE_QTR_TAU)) && (end > (TAU + THREE_QTR_TAU)))
1751                                 bounds.setBottom(-1.0);
1752
1753                         bounds.setTopLeft(QPointF(bounds.left() * a->radius[0], bounds.top() * a->radius[0]));
1754                         bounds.setBottomRight(QPointF(bounds.right() * a->radius[0], bounds.bottom() * a->radius[0]));
1755                         bounds.translate(a->p[0].x, a->p[0].y);
1756
1757                         if (Global::selection.contains(bounds))
1758                                 a->selected = true;
1759
1760                         break;
1761                 }
1762                 default:
1763                         break;
1764                 }
1765         }
1766 }
1767
1768
1769 bool DrawingView::HitTestObjects(Point point)
1770 {
1771         std::vector<void *>::iterator i;
1772         numHovered = 0;
1773         bool needUpdate = false;
1774         hoverPointValid = false;
1775
1776         for(i=document.objects.begin(); i!=document.objects.end(); i++)
1777         {
1778                 Object * obj = (Object *)(*i);
1779
1780                 // If we're seeing the object we're dragging, skip it
1781                 if (draggingObject && (obj == dragged))
1782                         continue;
1783
1784                 if (HitTest(obj, point))
1785                         needUpdate = true;
1786
1787                 if (obj->hovered)
1788                 {
1789                         numHovered++;
1790 //printf("MouseMove: OBJECT HOVERED (numHovered = %i)\n", numHovered);
1791                         emit(ObjectHovered(obj));
1792                 }
1793         }
1794
1795         return needUpdate;
1796 }
1797
1798
1799 bool DrawingView::HitTest(Object * obj, Point point)
1800 {
1801         bool needUpdate = false;
1802
1803         switch (obj->type)
1804         {
1805         case OTLine:
1806         {
1807                 bool oldHP0 = obj->hitPoint[0], oldHP1 = obj->hitPoint[1], oldHO = obj->hitObject;
1808                 obj->hitPoint[0] = obj->hitPoint[1] = obj->hitObject = false;
1809                 Vector lineSegment = obj->p[1] - obj->p[0];
1810                 Vector v1 = point - obj->p[0];
1811                 Vector v2 = point - obj->p[1];
1812                 double t = Geometry::ParameterOfLineAndPoint(obj->p[0], obj->p[1], point);
1813                 double distance;
1814
1815                 if (t < 0.0)
1816                         distance = v1.Magnitude();
1817                 else if (t > 1.0)
1818                         distance = v2.Magnitude();
1819                 else
1820                         // distance = ?Det?(ls, v1) / |ls|
1821                         distance = fabs((lineSegment.x * v1.y - v1.x * lineSegment.y)
1822                                 / lineSegment.Magnitude());
1823
1824                 if ((v1.Magnitude() * Global::zoom) < 8.0)
1825                 {
1826                         obj->hitPoint[0] = true;
1827                         hoverPoint = obj->p[0];
1828                         hoverPointValid = true;
1829                 }
1830                 else if ((v2.Magnitude() * Global::zoom) < 8.0)
1831                 {
1832                         obj->hitPoint[1] = true;
1833                         hoverPoint = obj->p[1];
1834                         hoverPointValid = true;
1835                 }
1836                 else if ((distance * Global::zoom) < 5.0)
1837                         obj->hitObject = true;
1838
1839                 obj->hovered = (obj->hitPoint[0] || obj->hitPoint[1] || obj->hitObject ? true : false);
1840
1841                 if ((oldHP0 != obj->hitPoint[0]) || (oldHP1 != obj->hitPoint[1]) || (oldHO != obj->hitObject))
1842                         needUpdate = true;
1843
1844                 break;
1845         }
1846         case OTCircle:
1847         {
1848                 bool oldHP = obj->hitPoint[0], oldHO = obj->hitObject;
1849                 obj->hitPoint[0] = obj->hitObject = false;
1850                 double length = Vector::Magnitude(obj->p[0], point);
1851
1852                 if ((length * Global::zoom) < 8.0)
1853                         obj->hitPoint[0] = true;
1854                 else if ((fabs(length - obj->radius[0]) * Global::zoom) < 2.0)
1855                         obj->hitObject = true;
1856
1857                 obj->hovered = (obj->hitPoint[0] || obj->hitObject ? true : false);
1858
1859                 if ((oldHP != obj->hitPoint[0]) || (oldHO != obj->hitObject))
1860                         needUpdate = true;
1861
1862                 break;
1863         }
1864         case OTArc:
1865         {
1866                 bool oldHP0 = obj->hitPoint[0], oldHP1 = obj->hitPoint[1], oldHP2 = obj->hitPoint[2], oldHO = obj->hitObject;
1867                 obj->hitPoint[0] = obj->hitPoint[1] = obj->hitPoint[2] = obj->hitObject = false;
1868                 double length = Vector::Magnitude(obj->p[0], point);
1869                 double angle = Vector::Angle(obj->p[0], point);
1870
1871                 // Make sure we get the angle in the correct spot
1872                 if (angle < obj->angle[0])
1873                         angle += TAU;
1874
1875                 // Get the span that we're pointing at...
1876                 double span = angle - obj->angle[0];
1877
1878                 // N.B.: Still need to hit test the arc start & arc span handles...
1879                 double spanAngle = obj->angle[0] + obj->angle[1];
1880                 Point handle1 = obj->p[0] + (Vector(cos(obj->angle[0]), sin(obj->angle[0])) * obj->radius[0]);
1881                 Point handle2 = obj->p[0] + (Vector(cos(spanAngle), sin(spanAngle)) * obj->radius[0]);
1882                 double length2 = Vector::Magnitude(point, handle1);
1883                 double length3 = Vector::Magnitude(point, handle2);
1884
1885                 if ((length * Global::zoom) < 8.0)
1886                         obj->hitPoint[0] = true;
1887                 else if ((length2 * Global::zoom) < 8.0)
1888                 {
1889                         obj->hitPoint[1] = true;
1890                         hoverPoint = handle1;
1891                         hoverPointValid = true;
1892                 }
1893                 else if ((length3 * Global::zoom) < 8.0)
1894                 {
1895                         obj->hitPoint[2] = true;
1896                         hoverPoint = handle2;
1897                         hoverPointValid = true;
1898                 }
1899                 else if (((fabs(length - obj->radius[0]) * Global::zoom) < 2.0) && (span < obj->angle[1]))
1900                         obj->hitObject = true;
1901
1902                 obj->hovered = (obj->hitPoint[0] || obj->hitPoint[1] || obj->hitPoint[2] || obj->hitObject ? true : false);
1903
1904                 if ((oldHP0 != obj->hitPoint[0]) || (oldHP1 != obj->hitPoint[1]) || (oldHP2 != obj->hitPoint[2]) || (oldHO != obj->hitObject))
1905                         needUpdate = true;
1906
1907                 break;
1908         }
1909         case OTDimension:
1910         {
1911                 bool oldHP0 = obj->hitPoint[0], oldHP1 = obj->hitPoint[1], oldHP2 = obj->hitPoint[2], oldHP3 = obj->hitPoint[3], oldHP4 = obj->hitPoint[4], oldHO = obj->hitObject;
1912                 obj->hitPoint[0] = obj->hitPoint[1] = obj->hitPoint[2] = obj->hitPoint[3] = obj->hitPoint[4] = obj->hitObject = false;
1913
1914                 Dimension * d = (Dimension *)obj;
1915
1916                 Vector orthogonal = Vector::Normal(d->lp[0], d->lp[1]);
1917                 // Get our line parallel to our points
1918                 float scaledThickness = Global::scale * obj->thickness;
1919                 Point p1 = d->lp[0] + (orthogonal * 10.0 * scaledThickness);
1920                 Point p2 = d->lp[1] + (orthogonal * 10.0 * scaledThickness);
1921                 Point p3(p1, point);
1922
1923                 Vector v1(d->p[0], point);
1924                 Vector v2(d->p[1], point);
1925                 Vector lineSegment(p1, p2);
1926                 double t = Geometry::ParameterOfLineAndPoint(p1, p2, point);
1927                 double distance;
1928                 Point midpoint = (p1 + p2) / 2.0;
1929                 Point hFSPoint = Point(midpoint, point);
1930                 Point hCS1Point = Point((p1 + midpoint) / 2.0, point);
1931                 Point hCS2Point = Point((midpoint + p2) / 2.0, point);
1932
1933                 if (t < 0.0)
1934                         distance = v1.Magnitude();
1935                 else if (t > 1.0)
1936                         distance = v2.Magnitude();
1937                 else
1938                         // distance = ?Det?(ls, v1) / |ls|
1939                         distance = fabs((lineSegment.x * p3.y - p3.x * lineSegment.y)
1940                                 / lineSegment.Magnitude());
1941
1942                 if ((v1.Magnitude() * Global::zoom) < 8.0)
1943                         obj->hitPoint[0] = true;
1944                 else if ((v2.Magnitude() * Global::zoom) < 8.0)
1945                         obj->hitPoint[1] = true;
1946                 else if ((distance * Global::zoom) < 5.0)
1947                         obj->hitObject = true;
1948
1949                 if ((hFSPoint.Magnitude() * Global::zoom) < 8.0)
1950                         obj->hitPoint[2] = true;
1951                 else if ((hCS1Point.Magnitude() * Global::zoom) < 8.0)
1952                         obj->hitPoint[3] = true;
1953                 else if ((hCS2Point.Magnitude() * Global::zoom) < 8.0)
1954                         obj->hitPoint[4] = true;
1955
1956 //              return (hitPoint1 || hitPoint2 || hitLine || hitFlipSwitch || hitChangeSwitch1 || hitChangeSwitch2 ? true : false);
1957                 obj->hovered = (obj->hitPoint[0] || obj->hitPoint[1] || obj->hitPoint[2] || obj->hitPoint[3] || obj->hitPoint[4] || obj->hitObject ? true : false);
1958
1959                 if ((oldHP0 != obj->hitPoint[0]) || (oldHP1 != obj->hitPoint[1]) || (oldHP2 != obj->hitPoint[2]) || (oldHP3 != obj->hitPoint[3]) || (oldHP4 != obj->hitPoint[4]) || (oldHO != obj->hitObject))
1960                         needUpdate = true;
1961
1962
1963                 break;
1964         }
1965         case OTContainer:
1966         {
1967                 // Containers must be recursively tested...
1968                 Container * c = (Container *)obj;
1969                 c->hitObject = false;
1970                 c->hovered = false;
1971                 std::vector<void *>::iterator i;
1972
1973                 for(i=c->objects.begin(); i!=c->objects.end(); i++)
1974                 {
1975                         Object * cObj = (Object *)*i;
1976
1977                         if (HitTest(cObj, point))
1978                                 needUpdate = true;
1979
1980                         if (cObj->hitObject == true)
1981                                 c->hitObject = true;
1982
1983                         if (cObj->hovered == true)
1984                                 c->hovered = true;
1985                 }
1986
1987                 break;
1988         }
1989         default:
1990                 break;
1991         }
1992
1993         return needUpdate;
1994 }
1995
1996
1997 bool DrawingView::HandleObjectClicked(void)
1998 {
1999         if (dragged->type == OTDimension)
2000         {
2001                 Dimension * d = (Dimension *)dragged;
2002
2003                 if (d->hitPoint[2])
2004                 {
2005                         // Hit the "flip sides" switch, so flip 'em
2006                         Point temp = d->p[0];
2007                         d->p[0] = d->p[1];
2008                         d->p[1] = temp;
2009                         return true;
2010                 }
2011                 else if (d->hitPoint[3])
2012                 {
2013                         // There are three cases here: aligned, horizontal, & vertical.
2014                         // Aligned and horizontal do the same thing, vertical goes back to
2015                         // linear.
2016                         if (d->subtype == DTLinearVert)
2017                                 d->subtype = DTLinear;
2018                         else
2019                                 d->subtype = DTLinearVert;
2020
2021                         return true;
2022                 }
2023                 else if (d->hitPoint[4])
2024                 {
2025                         // There are three cases here: aligned, horizontal, & vertical.
2026                         // Aligned and vertical do the same thing, horizontal goes back to
2027                         // linear.
2028                         if (d->subtype == DTLinearHorz)
2029                                 d->subtype = DTLinear;
2030                         else
2031                                 d->subtype = DTLinearHorz;
2032
2033                         return true;
2034                 }
2035         }
2036
2037         return false;
2038 }
2039
2040
2041 void DrawingView::HandleObjectMovement(Point point)
2042 {
2043         Point delta = point - oldPoint;
2044 //printf("HOM: old = (%f,%f), new = (%f, %f), delta = (%f, %f)\n", oldPoint.x, oldPoint.y, point.x, point.y, delta.x, delta.y);
2045 //      Object * obj = (Object *)hover[0];
2046         Object * obj = dragged;
2047 //printf("Object type = %i (size=%i), ", obj->type, hover.size());
2048 //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"));
2049
2050         switch (obj->type)
2051         {
2052         case OTLine:
2053                 if (obj->hitPoint[0])
2054                         obj->p[0] = point;
2055                 else if (obj->hitPoint[1])
2056                         obj->p[1] = point;
2057                 else if (obj->hitObject)
2058                 {
2059                         obj->p[0] += delta;
2060                         obj->p[1] += delta;
2061                 }
2062
2063                 break;
2064
2065         case OTCircle:
2066                 if (obj->hitPoint[0])
2067                         obj->p[0] = point;
2068                 else if (obj->hitObject)
2069                 {
2070 //this doesn't work. we need to save this on mouse down for this to work correctly!
2071 //                      double oldRadius = obj->radius[0];
2072                         obj->radius[0] = Vector::Magnitude(obj->p[0], point);
2073
2074                         QString text = QObject::tr("Radius: %1");//\nScale: %2%");
2075                         informativeText = text.arg(obj->radius[0], 0, 'd', 4);//.arg(obj->radius[0] / oldRadius * 100.0, 0, 'd', 0);
2076                 }
2077
2078                 break;
2079
2080         case OTArc:
2081                 if (obj->hitPoint[0])
2082                         obj->p[0] = point;
2083                 else if (obj->hitPoint[1])
2084                 {
2085                         // Change the Arc's span (handle #1)
2086                         if (shiftDown)
2087                         {
2088                                 double angle = Vector::Angle(obj->p[0], point);
2089                                 double delta = angle - obj->angle[0];
2090
2091                                 if (delta < 0)
2092                                         delta += TAU;
2093
2094                                 obj->angle[1] -= delta;
2095                                 obj->angle[0] = angle;
2096
2097                                 if (obj->angle[1] < 0)
2098                                         obj->angle[1] += TAU;
2099
2100                                 QString text = QObject::tr("Span: %1") + QChar(0x00B0) + QObject::tr("\n%2") + QChar(0x00B0) + QObject::tr(" - %3") + QChar(0x00B0);
2101                                 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);
2102                                 return;
2103                         }
2104
2105                         double angle = Vector::Angle(obj->p[0], point);
2106                         obj->angle[0] = angle;
2107                         QString text = QObject::tr("Start angle: %1") + QChar(0x00B0);
2108                         informativeText = text.arg(obj->angle[0] * RADIANS_TO_DEGREES, 0, 'd', 4);
2109                 }
2110                 else if (obj->hitPoint[2])
2111                 {
2112                         // Change the Arc's span (handle #2)
2113                         if (shiftDown)
2114                         {
2115                                 double angle = Vector::Angle(obj->p[0], point);
2116                                 obj->angle[1] = angle - obj->angle[0];
2117
2118                                 if (obj->angle[1] < 0)
2119                                         obj->angle[1] += TAU;
2120
2121                                 QString text = QObject::tr("Span: %1") + QChar(0x00B0) + QObject::tr("\n%2") + QChar(0x00B0) + QObject::tr(" - %3") + QChar(0x00B0);
2122                                 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);
2123                                 return;
2124                         }
2125
2126                         double angle = Vector::Angle(obj->p[0], point);
2127                         obj->angle[0] = angle - obj->angle[1];
2128
2129                         if (obj->angle[0] < 0)
2130                                 obj->angle[0] += TAU;
2131
2132                         QString text = QObject::tr("End angle: %1") + QChar(0x00B0);
2133                         informativeText = text.arg((obj->angle[0] + obj->angle[1]) * RADIANS_TO_DEGREES, 0, 'd', 4);
2134                 }
2135                 else if (obj->hitObject)
2136                 {
2137                         if (shiftDown)
2138                         {
2139                                 return;
2140                         }
2141
2142                         obj->radius[0] = Vector::Magnitude(obj->p[0], point);
2143                         QString text = QObject::tr("Radius: %1");
2144                         informativeText = text.arg(obj->radius[0], 0, 'd', 4);
2145                 }
2146
2147                 break;
2148
2149         case OTDimension:
2150                 if (obj->hitPoint[0])
2151                         obj->p[0] = point;
2152                 else if (obj->hitPoint[1])
2153                         obj->p[1] = point;
2154                 else if (obj->hitObject)
2155                 {
2156                         obj->p[0] += delta;
2157                         obj->p[1] += delta;
2158                 }
2159
2160                 break;
2161
2162         case OTContainer:
2163                 // This is shitty, but works for now until I can code up something
2164                 // nicer :-)
2165                 TranslateObject(obj, delta);
2166
2167                 break;
2168         default:
2169                 break;
2170         }
2171 }
2172