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