]> Shamusworld >> Repos - architektonas/blob - src/drawingview.cpp
Selection of Lines works, ctrl held for multi-select.
[architektonas] / src / drawingview.cpp
1 // drawingview.cpp
2 //
3 // Part of the Architektonas Project
4 // (C) 2011 Underground Software
5 // See the README and GPLv3 files for licensing and warranty information
6 //
7 // JLH = James Hammons <jlhamm@acm.org>
8 //
9 // Who  When        What
10 // ---  ----------  -------------------------------------------------------------
11 // JLH  03/22/2011  Created this file
12 // JLH  09/29/2011  Added middle mouse button panning
13 //
14
15 // FIXED:
16 //
17 // - Redo rendering code to *not* use Qt's transform functions, as they are tied
18 //   to a left-handed system and we need a right-handed one. [DONE]
19 //
20 // STILL TO BE DONE:
21 //
22 // - Lots of stuff
23 //
24
25 // Uncomment this for debugging...
26 //#define DEBUG
27 //#define DEBUGFOO                              // Various tool debugging...
28 //#define DEBUGTP                               // Toolpalette debugging...
29
30 #include "drawingview.h"
31
32 #include <stdint.h>
33 #include "geometry.h"
34 #include "global.h"
35 #include "mathconstants.h"
36 #include "painter.h"
37 #include "structs.h"
38
39
40 #define BACKGROUND_MAX_SIZE     512
41
42 // Class variable
43 //Container DrawingView::document(Vector(0, 0));
44
45
46 DrawingView::DrawingView(QWidget * parent/*= NULL*/): QWidget(parent),
47         // The value in the settings file will override this.
48         useAntialiasing(true), numSelected(0), numHovered(0), shiftDown(false),
49         ctrlDown(false),
50         gridBackground(BACKGROUND_MAX_SIZE, BACKGROUND_MAX_SIZE),
51         scale(1.0), offsetX(-10), offsetY(-10),// document(Vector(0, 0)),
52         gridPixels(0), collided(false)//, toolAction(NULL)
53 {
54 //      document.isTopLevelContainer = true;
55 //wtf? doesn't work except in c++11???  document = { 0 };
56         setBackgroundRole(QPalette::Base);
57         setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
58
59         Global::gridSpacing = 12.0;             // In base units (inch is default)
60
61 #if 0
62         Line * line = new Line(Vector(5, 5), Vector(50, 40), &document);
63         document.Add(line);
64         document.Add(new Line(Vector(50, 40), Vector(10, 83), &document));
65         document.Add(new Line(Vector(10, 83), Vector(17, 2), &document));
66         document.Add(new Circle(Vector(100, 100), 36, &document));
67         document.Add(new Circle(Vector(50, 150), 49, &document));
68         document.Add(new Arc(Vector(300, 300), 32, PI / 4.0, PI * 1.3, &document)),
69         document.Add(new Arc(Vector(200, 200), 60, PI / 2.0, PI * 1.5, &document));
70 #if 1
71         Dimension * dimension = new Dimension(Vector(0, 0), Vector(0, 0), DTLinear, &document);
72         line->SetDimensionOnLine(dimension);
73         document.Add(dimension);
74 #else
75         // Alternate way to do the above...
76         line->SetDimensionOnLine();
77 #endif
78 #else
79         Line * line = new Line;//(Vector(5, 5), Vector(50, 40), &document);
80         line->p1 = Vector(5, 5);
81         line->p2 = Vector(50, 40);
82         line->type = OTLine;
83         line->thickness = 2.0;
84         line->style = LSDash;
85         line->color = 0xFF7F00;
86         document.objects.push_back(line);
87         document.objects.push_back(new Line(Vector(50, 40), Vector(10, 83)));
88         document.objects.push_back(new Line(Vector(10, 83), Vector(17, 2)));
89         document.objects.push_back(new Circle(Vector(100, 100), 36));
90         document.objects.push_back(new Circle(Vector(50, 150), 49));
91         document.objects.push_back(new Arc(Vector(300, 300), 32, PI / 4.0, PI * 1.3)),
92         document.objects.push_back(new Arc(Vector(200, 200), 60, PI / 2.0, PI * 1.5));
93         document.objects.push_back(new Dimension(Vector(50, 40), Vector(5, 5)));
94         document.objects.push_back(new Text(Vector(10, 83), "Here is some awesome text!"));
95 #endif
96
97 /*
98 Here we set the grid size in pixels--12 in this case. Initially, we have our
99 zoom set to make this represent 12 inches at a zoom factor of 25%. (This is
100 arbitrary.) So, to be able to decouple the grid size from the zoom, we need
101 to be able to set the size of the background grid (which we do here at an
102 arbitrary 12 pixels) to anything we want (within reason, of course :-).
103
104 The drawing enforces the grid spacing through the drawing->gridSpacing variable.
105
106         drawing->gridSpacing = 12.0 / Global::zoom;
107
108 Global::zoom is the zoom factor for the drawing, and all mouse clicks are
109 translated to Cartesian coordinates through this. (Initially, Global::zoom is
110 set to 1.0. SCREEN_ZOOM is set to 1.0/4.0.)
111
112 Really, the 100% zoom level can be set at *any* zoom level, it's more of a
113 convenience function than any measure of absolutes. Doing things that way we
114 could rid ourselves of the whole SCREEN_ZOOM parameter and all the attendant
115 shittiness that comes with it.
116
117 However, it seems that SCREEN_ZOOM is used to make text and arrow sizes show up
118 a certain way, which means we should probably create something else in those
119 objects to take its place--like some kind of scale factor. This would seem to
120 imply that certain point sizes actually *do* tie things like fonts to absolute
121 sizes on the screen, but not necessarily because you could have an inch scale
122 with text that is quite small relative to other objects on the screen, which
123 currently you have to zoom in to see (and which blows up the text). Point sizes
124 in an application like this are a bit meaningless; even though an inch is an
125 inch regardless of the zoom level a piece of text can be larger or smaller than
126 this. Maybe this is the case for having a base unit and basing point sizes off
127 of that.
128
129 Here's what's been figured out. Global::zoom is simply the ratio of pixels to
130 base units. What that means is that if you have a 12px grid with a 6" grid size
131 (& base unit of "inches"), Global::zoom becomes 12px / 6" = 2.0 px/in.
132
133 Dimensions now have a "size" parameter to set their absolute size in relation
134 to the base unit. ATM, the arrows are drawn in pixels, but also scaled by
135 Global::zoom *and* size. Same with the dimension text; it's drawn at 10pt and
136 scaled the same way as the arrowheads.
137
138 Need a way to scale line widths as well. :-/ Shouldn't be too difficult, just
139 need a thickness parameter similar to the "size" param for dimensions. (And now
140 we do! :-)
141
142 */
143         SetGridSize(12);        // This is in pixels
144 }
145
146
147 #if 0
148 void DrawingView::SetToolActive(Action * action)
149 {
150         if (action != NULL)
151         {
152                 toolAction = action;
153                 connect(toolAction, SIGNAL(ObjectReady(Object *)), this,
154                         SLOT(AddNewObjectToDocument(Object *)));
155                 connect(toolAction, SIGNAL(NeedRefresh()), this, SLOT(HandleActionUpdate()));
156         }
157 }
158 #endif
159
160
161 void DrawingView::SetGridSize(uint32_t size)
162 {
163         // Sanity check
164         if (size == gridPixels)
165                 return;
166
167         // Recreate the background bitmap
168         gridPixels = size;
169         QPainter pmp(&gridBackground);
170         pmp.fillRect(0, 0, BACKGROUND_MAX_SIZE, BACKGROUND_MAX_SIZE, QColor(240, 240, 240));
171         pmp.setPen(QPen(QColor(210, 210, 255), 2.0, Qt::SolidLine));
172
173         for(int i=0; i<(BACKGROUND_MAX_SIZE-1); i+=gridPixels)
174         {
175                 pmp.drawLine(i, 0, i, BACKGROUND_MAX_SIZE - 1);
176                 pmp.drawLine(0, i, BACKGROUND_MAX_SIZE - 1, i);
177         }
178
179         pmp.end();
180
181         // Set up new BG brush & zoom level (pixels per base unit)
182 //      Painter::zoom = gridPixels / gridSpacing;
183         Global::zoom = gridPixels / Global::gridSpacing;
184         UpdateGridBackground();
185 }
186
187
188 void DrawingView::UpdateGridBackground(void)
189 {
190         // Transform the origin to Qt coordinates
191         Vector pixmapOrigin = Painter::CartesianToQtCoords(Vector());
192         int x = (int)pixmapOrigin.x;
193         int y = (int)pixmapOrigin.y;
194         // Use mod arithmetic to grab the correct swatch of background
195 /*
196 Negative numbers still screw it up... Need to think about what we're
197 trying to do here. The fact that it worked with 72 seems to have been pure luck.
198 It seems the problem is negative numbers: We can't let that happen.
199 When taking away the zero, it pops over 1 px at zero, then goes about 1/2 a
200 grid at x<0.
201
202 The bitmap looks like this:
203
204 +---+---+---+---+---
205 |   |   |   |   |
206 |   |   |   |   |
207 +---+---+---+---+---
208 |   |   |   |   |
209 |   |   |   |   |
210 |   |   |   |   |
211
212 @ x = 1, we want it to look like:
213
214 -+---+---+---+---+---
215  |   |   |   |   |
216  |   |   |   |   |
217 -+---+---+---+---+---
218  |   |   |   |   |
219  |   |   |   |   |
220  |   |   |   |   |
221
222 Which means we need to grab the sample from x = 3. @ x = -1:
223
224 ---+---+---+---+---
225    |   |   |   |
226    |   |   |   |
227 ---+---+---+---+---
228    |   |   |   |
229    |   |   |   |
230    |   |   |   |
231
232 Which means we need to grab the sample from x = 1. Which means we have to take
233 the mirror of the modulus of gridPixels.
234
235 Doing a mod of a negative number is problematic: 1st, the compiler converts the
236 negative number to an unsigned int, then it does the mod. Gets you wrong answers
237 most of the time, unless you use a power of 2. :-P So what we do here is just
238 take the modulus of the negation, which means we don't have to worry about
239 mirroring it later.
240
241 The positive case looks gruesome (and it is) but it boils down to this: We take
242 the modulus of the X coordinate, then mirror it by subtraction from the
243 maximum (in this case, gridPixels). This gives us a number in the range of 1 to
244 gridPixels. But we need the case where the result equalling gridPixels to be
245 zero; so we do another modulus operation on the result to achieve this.
246 */
247         if (x < 0)
248                 x = -x % gridPixels;
249         else
250                 x = (gridPixels - (x % gridPixels)) % gridPixels;
251
252         if (y < 0)
253                 y = -y % gridPixels;
254         else
255                 y = (gridPixels - (y % gridPixels)) % gridPixels;
256
257         // Here we grab a section of the bigger pixmap, so that the background
258         // *looks* like it's scrolling...
259         QPixmap pm = gridBackground.copy(x, y, gridPixels, gridPixels);
260         QPalette pal = palette();
261         pal.setBrush(backgroundRole(), QBrush(pm));
262         setAutoFillBackground(true);
263         setPalette(pal);
264 }
265
266
267 void DrawingView::AddNewObjectToDocument(Object * object)
268 {
269         if (object)
270         {
271 //              object->Reparent(&document);
272 //              document.Add(object);
273                 update();
274         }
275 //printf("DrawingView::AddNewObjectToDocument(). object=%08X\n", object);
276 }
277
278
279 void DrawingView::HandleActionUpdate(void)
280 {
281         update();
282 }
283
284
285 void DrawingView::SetCurrentLayer(int layer)
286 {
287         Global::currentLayer = layer;
288 //printf("DrawingView::CurrentLayer = %i\n", layer);
289 }
290
291
292 QPoint DrawingView::GetAdjustedMousePosition(QMouseEvent * event)
293 {
294         // This is undoing the transform, e.g. going from client coords to local coords.
295         // In essence, the height - y is height + (y * -1), the (y * -1) term doing the
296         // conversion of the y-axis from increasing bottom to top.
297         return QPoint(offsetX + event->x(), offsetY + (size().height() - event->y()));
298 }
299
300
301 QPoint DrawingView::GetAdjustedClientPosition(int x, int y)
302 {
303         // VOODOO ALERT (ON Y COMPONENT!!!!) (eh?)
304         // No voodoo here, it's just grouped wrong to see it. It should be:
305         // -offsetY + (size.height() + (y * -1.0)) <-- this is wrong, offsetY should be positive
306         return QPoint(-offsetX + x, (size().height() - (-offsetY + y)) * +1.0);
307 }
308
309
310 void DrawingView::paintEvent(QPaintEvent * /*event*/)
311 {
312         QPainter qtPainter(this);
313         Painter painter(&qtPainter);
314
315         if (useAntialiasing)
316                 qtPainter.setRenderHint(QPainter::Antialiasing);
317
318         Global::viewportHeight = size().height();
319
320         // Draw coordinate axes
321         painter.SetPen(QPen(Qt::blue, 1.0, Qt::DotLine));
322         painter.DrawLine(0, -16384, 0, 16384);
323         painter.DrawLine(-16384, 0, 16384, 0);
324
325         // The top level document takes care of rendering for us...
326 //      document.Draw(&painter);
327         // Not any more it doesn't...
328         RenderObjects(&painter, &document);
329
330 #if 0
331         if (toolAction)
332         {
333                 painter.SetPen(QPen(QColor(200, 100, 0, 255), 1.0, Qt::DashLine));
334                 painter.DrawCrosshair(oldPoint);
335                 toolAction->Draw(&painter);
336         }
337 #endif
338
339 #if 1
340         if (Global::selectionInProgress)
341         {
342                 painter.SetPen(QPen(QColor(255, 127, 0, 255)));
343                 painter.SetBrush(QBrush(QColor(255, 127, 0, 100)));
344                 painter.DrawRect(Global::selection);
345         }
346 #endif
347 }
348
349
350 void DrawingView::RenderObjects(Painter * painter, Container * c)
351 {
352         std::vector<void *>::iterator i;
353
354         for(i=c->objects.begin(); i!=c->objects.end(); i++)
355         {
356                 Object * obj = (Object *)(*i);
357                 float scaledThickness = Global::scale * obj->thickness;
358                 painter->SetPen(obj->color, Global::zoom * scaledThickness, obj->style);
359                 painter->SetBrush(obj->color);
360
361                 if (obj->selected || obj->hovered)
362                         painter->SetPen(0xFF0000, Global::zoom * scaledThickness, LSDash);
363
364                 switch (obj->type)
365                 {
366                 case OTLine:
367                 {
368                         Line * l = (Line *)obj;
369                         painter->DrawLine(l->p1, l->p2);
370                         break;
371                 }
372                 case OTCircle:
373                 {
374                         Circle * ci = (Circle *)obj;
375                         painter->SetBrush(QBrush(Qt::NoBrush));
376                         painter->DrawEllipse(ci->p1, ci->radius, ci->radius);
377                         break;
378                 }
379                 case OTArc:
380                 {
381                         Arc * a = (Arc *)obj;
382                         painter->DrawArc(a->p1, a->radius, a->angle1, a->angle2);
383                         break;
384                 }
385                 case OTDimension:
386                 {
387                         Dimension * d = (Dimension *)obj;
388
389                         Vector v(d->p1, d->p2);
390                         double angle = v.Angle();
391                         Vector unit = v.Unit();
392                         Vector linePt1 = d->p1, linePt2 = d->p2;
393                         Vector ortho;
394                         double x1, y1, length;
395
396                         if (d->subtype == DTLinearVert)
397                         {
398                                 if ((angle < 0) || (angle > PI))
399                                 {
400                                         x1 = (d->p1.x > d->p2.x ? d->p1.x : d->p2.x);
401                                         y1 = (d->p1.y > d->p2.y ? d->p1.y : d->p2.y);
402                                         ortho = Vector(1.0, 0);
403                                         angle = PI3_OVER_2;
404                                 }
405                                 else
406                                 {
407                                         x1 = (d->p1.x > d->p2.x ? d->p2.x : d->p1.x);
408                                         y1 = (d->p1.y > d->p2.y ? d->p2.y : d->p1.y);
409                                         ortho = Vector(-1.0, 0);
410                                         angle = PI_OVER_2;
411                                 }
412
413                                 linePt1.x = linePt2.x = x1;
414                                 length = fabs(d->p1.y - d->p2.y);
415                         }
416                         else if (d->subtype == DTLinearHorz)
417                         {
418                                 if ((angle < PI_OVER_2) || (angle > PI3_OVER_2))
419                                 {
420                                         x1 = (d->p1.x > d->p2.x ? d->p1.x : d->p2.x);
421                                         y1 = (d->p1.y > d->p2.y ? d->p1.y : d->p2.y);
422                                         ortho = Vector(0, 1.0);
423                                         angle = 0;
424                                 }
425                                 else
426                                 {
427                                         x1 = (d->p1.x > d->p2.x ? d->p2.x : d->p1.x);
428                                         y1 = (d->p1.y > d->p2.y ? d->p2.y : d->p1.y);
429                                         ortho = Vector(0, -1.0);
430                                         angle = PI;
431                                 }
432
433                                 linePt1.y = linePt2.y = y1;
434                                 length = fabs(d->p1.x - d->p2.x);
435                         }
436                         else if (d->subtype == DTLinear)
437                         {
438                                 angle = Vector(linePt1, linePt2).Angle();
439                                 ortho = Vector::Normal(linePt1, linePt2);
440                                 length = v.Magnitude();
441                         }
442
443                         unit = Vector(linePt1, linePt2).Unit();
444
445                         Point p1 = linePt1 + (ortho * 10.0 * scaledThickness);
446                         Point p2 = linePt2 + (ortho * 10.0 * scaledThickness);
447                         Point p3 = linePt1 + (ortho * 16.0 * scaledThickness);
448                         Point p4 = linePt2 + (ortho * 16.0 * scaledThickness);
449                         Point p5 = d->p1 + (ortho * 4.0 * scaledThickness);
450                         Point p6 = d->p2 + (ortho * 4.0 * scaledThickness);
451
452                 /*
453                 The numbers hardcoded into here, what are they?
454                 I believe they are pixels.
455                 */
456                         // Draw extension lines (if certain type)
457                         painter->DrawLine(p3, p5);
458                         painter->DrawLine(p4, p6);
459
460                         // Calculate whether or not the arrowheads are too crowded to put inside
461                         // the extension lines. 9.0 is the length of the arrowhead.
462                         double t = Geometry::ParameterOfLineAndPoint(linePt1, linePt2, linePt2 - (unit * 9.0 * scaledThickness));
463                 //printf("Dimension::Draw(): t = %lf\n", t);
464
465                 // On the screen, it's acting like this is actually 58%...
466                 // This is correct, we want it to happen at > 50%
467                         if (t > 0.58)
468                         {
469                                 // Draw main dimension line + arrowheads
470                                 painter->DrawLine(p1, p2);
471                                 painter->DrawArrowhead(p1, p2, scaledThickness);
472                                 painter->DrawArrowhead(p2, p1, scaledThickness);
473                         }
474                         else
475                         {
476                                 // Draw outside arrowheads
477                                 Point p7 = p1 - (unit * 9.0 * scaledThickness);
478                                 Point p8 = p2 + (unit * 9.0 * scaledThickness);
479                                 painter->DrawArrowhead(p1, p7, scaledThickness);
480                                 painter->DrawArrowhead(p2, p8, scaledThickness);
481                                 painter->DrawLine(p1, p1 - (unit * 14.0 * scaledThickness));
482                                 painter->DrawLine(p2, p2 + (unit * 14.0 * scaledThickness));
483                         }
484
485                         // Draw length of dimension line...
486                         painter->SetFont(QFont("Arial", 8.0 * Global::zoom * scaledThickness));
487                         Point ctr = p2 + (Vector(p2, p1) / 2.0);
488
489                 #if 0
490                         QString dimText = QString("%1\"").arg(Vector(endpoint - position).Magnitude());
491                 #else
492                         QString dimText;
493
494                         if (length < 12.0)
495                                 dimText = QString("%1\"").arg(length);
496                         else
497                         {
498                                 double feet = (double)((int)length / 12);
499                                 double inches = length - (feet * 12.0);
500
501                                 if (inches == 0)
502                                         dimText = QString("%1'").arg(feet);
503                                 else
504                                         dimText = QString("%1' %2\"").arg(feet).arg(inches);
505                         }
506                 #endif
507
508                         painter->DrawAngledText(ctr, angle, dimText, scaledThickness);
509
510                         break;
511                 }
512                 case OTText:
513                 {
514                         Text * t = (Text *)obj;
515                         painter->DrawTextObject(t->p1, t->s.c_str(), scaledThickness);
516                         break;
517                 }
518                 default:
519                         break;
520                 }
521         }
522 }
523
524
525 void DrawingView::DeleteSelectedItems(void)
526 {
527         std::vector<void *>::iterator i = document.objects.begin();
528
529         while (i != document.objects.end())
530         {
531                 Object * obj = (Object *)(*i);
532
533                 if (obj->selected)
534                 {
535                         delete obj;
536                         document.objects.erase(i);
537                 }
538                 else
539                         i++;
540         }
541 }
542
543
544 void DrawingView::ClearSelection(void)
545 {
546         std::vector<void *>::iterator i;
547
548         for(i=document.objects.begin(); i!=document.objects.end(); i++)
549                 ((Object *)(*i))->selected = false;
550 }
551
552
553 void DrawingView::AddHoveredToSelection(void)
554 {
555         std::vector<void *>::iterator i;
556
557         for(i=document.objects.begin(); i!=document.objects.end(); i++)
558         {
559                 if (((Object *)(*i))->hovered)
560                         ((Object *)(*i))->selected = true;
561         }
562 }
563
564
565 void DrawingView::GetSelection(std::vector<void *> & v)
566 {
567         v.empty();
568         std::vector<void *>::iterator i;
569
570         for(i=v.begin(); i!=v.end(); i++)
571         {
572                 if (((Object *)(*i))->selected)
573                         v.push_back(*i);
574         }
575 }
576
577
578 void DrawingView::GetHovered(std::vector<void *> & v)
579 {
580         v.empty();
581         std::vector<void *>::iterator i;
582
583         for(i=v.begin(); i!=v.end(); i++)
584         {
585                 if (((Object *)(*i))->hovered)
586                         v.push_back(*i);
587         }
588 }
589
590
591 void DrawingView::resizeEvent(QResizeEvent * /*event*/)
592 {
593         Global::screenSize = Vector(size().width(), size().height());
594         UpdateGridBackground();
595 }
596
597
598 void DrawingView::mousePressEvent(QMouseEvent * event)
599 {
600         if (event->button() == Qt::LeftButton)
601         {
602                 Vector point = Painter::QtToCartesianCoords(Vector(event->x(), event->y()));
603 //              collided = document.Collided(point);
604                 collided = false;
605
606                 // Do an update if collided with at least *one* object in the document
607 //              if (collided)
608 //                      update();
609                 // Actually, we already know what we're going to click on as all the collision
610                 // detection already happened in the mouse move function...!
611 //printf("MouseDown: ctrl=%s, numHovered=%i\n", (ctrlDown ? "DOWN" : "up"), numHovered);
612
613 #if 0
614                 if (toolAction)
615                 {
616                         if (Global::snapToGrid)
617                                 point = Global::SnapPointToGrid(point);
618
619                         // We always snap to object points, and they take precendence over
620                         // grid points...
621                         if (Global::snapPointIsValid)
622                                 point = Global::snapPoint;
623
624                         toolAction->MouseDown(point);
625                 }
626 #else
627                 if (Global::tool)
628                 {
629                         return;
630                 }
631 #endif
632
633                 if (!ctrlDown)
634                         ClearSelection();
635
636                 if (numHovered > 0)
637                         AddHoveredToSelection();
638
639 #if 1
640                 // Didn't hit any object and not using a tool, so do a selection rectangle
641                 if (!(numHovered || Global::tool))
642                 {
643                         Global::selectionInProgress = true;
644                         Global::selection.setTopLeft(QPointF(point.x, point.y));
645                         Global::selection.setBottomRight(QPointF(point.x, point.y));
646                 }
647 #endif
648         }
649         else if (event->button() == Qt::MiddleButton)
650         {
651                 scrollDrag = true;
652                 oldPoint = Vector(event->x(), event->y());
653                 // Should also change the mouse pointer as well...
654                 setCursor(Qt::SizeAllCursor);
655         }
656 }
657
658
659 void DrawingView::mouseMoveEvent(QMouseEvent * event)
660 {
661         Vector point = Painter::QtToCartesianCoords(Vector(event->x(), event->y()));
662         Global::selection.setBottomRight(QPointF(point.x, point.y));
663         // Only needs to be done here, as mouse down is always preceded by movement
664         Global::snapPointIsValid = false;
665
666         // Scrolling...
667         if (event->buttons() & Qt::MiddleButton)
668         {
669                 point = Vector(event->x(), event->y());
670                 // Since we're using Qt coords for scrolling, we have to adjust them here to
671                 // conform to Cartesian coords, since the origin is using Cartesian. :-)
672                 Vector delta(oldPoint, point);
673                 delta /= Global::zoom;
674                 delta.y = -delta.y;
675                 Global::origin -= delta;
676
677                 UpdateGridBackground();
678                 update();
679                 oldPoint = point;
680                 return;
681         }
682
683 #if 1
684         // Grid processing... (only snap here is left button is down)
685         if ((event->buttons() & Qt::LeftButton) && Global::snapToGrid)
686         {
687                 point = SnapPointToGrid(point);
688         }
689
690         // Snap points on objects always take precedence over the grid, whether
691         // dragging an object or not...
692 //thisnowok
693         if (Global::snapPointIsValid)
694         {
695 // Uncommenting this causes the cursor to become unresponsive after the first
696 // object is added.
697 //              point = Global::snapPoint;
698         }
699 #endif
700
701         // Do checking here to see if object can be selected or not
702         if (Global::selectionInProgress)
703         {
704                 std::vector<void *>::iterator i;
705 //              QRectF bounds;
706                 numSelected = 0;
707
708                 for(i=document.objects.begin(); i!=document.objects.end(); i++)
709                 {
710                         Object * obj = (Object *)(*i);
711                         obj->selected = false;
712 //                      QRectF extents;
713
714                         switch (obj->type)
715                         {
716                         case OTLine:
717                         {
718                                 Line * l = (Line *)obj;
719
720                                 if (Global::selection.contains(l->p1.x, l->p1.y) && Global::selection.contains(l->p2.x, l->p2.y))
721                                         l->selected = true;
722
723                                 break;
724                         }
725                         case OTCircle:
726                         {
727                                 Circle * c = (Circle *)obj;
728
729                                 if (Global::selection.contains(c->p1.x - c->radius, c->p1.y - c->radius) && Global::selection.contains(c->p1.x + c->radius, c->p1.y + c->radius))
730                                         c->selected = true;
731
732                                 break;
733                         }
734                         case OTArc:
735                         {
736                                 Arc * a = (Arc *)obj;
737
738         double start = a->angle1;
739         double end = start + a->angle2;
740         QPointF p1(cos(start), sin(start));
741         QPointF p2(cos(end), sin(end));
742         QRectF bounds(p1, p2);
743
744 #if 1
745         // Swap X/Y coordinates if they're backwards...
746         if (bounds.left() > bounds.right())
747         {
748                 double temp = bounds.left();
749                 bounds.setLeft(bounds.right());
750                 bounds.setRight(temp);
751         }
752
753         if (bounds.bottom() > bounds.top())
754         {
755                 double temp = bounds.bottom();
756                 bounds.setBottom(bounds.top());
757                 bounds.setTop(temp);
758         }
759 #else
760         // Doesn't work as advertised! For shame!
761         bounds = bounds.normalized();
762 #endif
763
764         // If the end of the arc is before the beginning, add 360 degrees to it
765         if (end < start)
766                 end += 2.0 * PI;
767
768         // Adjust the bounds depending on which axes are crossed
769         if ((start < PI_OVER_2) && (end > PI_OVER_2))
770                 bounds.setTop(1.0);
771
772         if ((start < PI) && (end > PI))
773                 bounds.setLeft(-1.0);
774
775         if ((start < (PI + PI_OVER_2)) && (end > (PI + PI_OVER_2)))
776                 bounds.setBottom(-1.0);
777
778         if ((start < (2.0 * PI)) && (end > (2.0 * PI)))
779                 bounds.setRight(1.0);
780
781         if ((start < ((2.0 * PI) + PI_OVER_2)) && (end > ((2.0 * PI) + PI_OVER_2)))
782                 bounds.setTop(1.0);
783
784         if ((start < (3.0 * PI)) && (end > (3.0 * PI)))
785                 bounds.setLeft(-1.0);
786
787         if ((start < ((3.0 * PI) + PI_OVER_2)) && (end > ((3.0 * PI) + PI_OVER_2)))
788                 bounds.setBottom(-1.0);
789
790         bounds.setTopLeft(QPointF(bounds.left() * a->radius, bounds.top() * a->radius));
791         bounds.setBottomRight(QPointF(bounds.right() * a->radius, bounds.bottom() * a->radius));
792         bounds.translate(a->p1.x, a->p1.y);
793
794                                 if (Global::selection.contains(bounds))
795                                         a->selected = true;
796
797                                 break;
798                         }
799                         default:
800                                 break;
801                         }
802
803                         if (obj->selected)
804                                 numSelected++;
805                 }
806         }
807
808 //      oldPoint = point;
809 //we should keep track of the last point here and only pass this down *if* the point
810 //changed...
811
812 #if 0
813         // This returns true if we've moved over an object...
814         if (document.PointerMoved(point)) // <-- This
815         // This is where the object would do automagic dragging & shit. Since we don't
816         // do that anymore, we need a strategy to handle it.
817         {
818
819 /*
820 Now objects handle mouse move snapping as well. The code below mainly works only
821 for tools; we need to fix it so that objects work as well...
822
823 There's a problem with the object point snapping in that it's dependent on the
824 order of the objects in the document. Most likely this is because it counts the
825 selected object last and thus fucks up the algorithm. Need to fix this...
826
827
828 */
829                 // Do object snapping here. Grid snapping on mouse down is done in the
830                 // objects themselves, only because we have to hit test the raw point,
831                 // not the snapped point. There has to be a better way...!
832                 if (document.penultimateObjectHovered)
833                 {
834                         // Two objects are hovered, see if we have an intersection point
835                         if ((document.lastObjectHovered->type == OTLine) && (document.penultimateObjectHovered->type == OTLine))
836                         {
837                                 double t;
838                                 int n = Geometry::Intersects((Line *)document.lastObjectHovered, (Line *)document.penultimateObjectHovered, &t);
839
840                                 if (n == 1)
841                                 {
842                                         Global::snapPoint = document.lastObjectHovered->GetPointAtParameter(t);
843                                         Global::snapPointIsValid = true;
844                                 }
845                         }
846                         else if ((document.lastObjectHovered->type == OTCircle) && (document.penultimateObjectHovered->type == OTCircle))
847                         {
848                                 Point p1, p2;
849                                 int n = Geometry::Intersects((Circle *)document.lastObjectHovered, (Circle *)document.penultimateObjectHovered, 0, 0, 0, 0, &p1, &p2);
850
851                                 if (n == 1)
852                                 {
853                                         Global::snapPoint = p1;
854                                         Global::snapPointIsValid = true;
855                                 }
856                                 else if (n == 2)
857                                 {
858                                         double d1 = Vector(point, p1).Magnitude();
859                                         double d2 = Vector(point, p2).Magnitude();
860
861                                         if (d1 < d2)
862                                                 Global::snapPoint = p1;
863                                         else
864                                                 Global::snapPoint = p2;
865
866                                         Global::snapPointIsValid = true;
867                                 }
868                         }
869                 }
870 //              else
871 //              {
872                         // Otherwise, it was a single object hovered...
873 //              }
874         }
875
876         if (toolAction)
877         {
878                 if (Global::snapToGrid)
879                         point = Global::SnapPointToGrid(point);
880
881                 // We always snap to object points, and they take precendence over
882                 // grid points...
883                 if (Global::snapPointIsValid)
884                         point = Global::snapPoint;
885
886                 toolAction->MouseMoved(point);
887         }
888 #endif
889         bool needUpdate = false;
890
891         // Don't do this kind of checking unless we're not doing a selection rectangle!
892         // Hmm, lines don't stay selected if globally selected... !!! FIX !!! [DONE]
893         // it's because there were extra state variables, the hit* vars...
894         if (!Global::selectionInProgress)
895         {
896         std::vector<void *>::iterator i;
897         numHovered = 0;
898
899         for(i=document.objects.begin(); i!=document.objects.end(); i++)
900         {
901                 Object * obj = (Object *)(*i);
902 //              obj->selected = false;
903
904                 switch (obj->type)
905                 {
906                 case OTLine:
907                 {
908                         Line * l = (Line *)obj;
909
910 //      bool hitPoint1, hitPoint2, hitLine;
911         l->hitPoint[0] = l->hitPoint[1] = l->hitObject = false;
912         Vector lineSegment = l->p2 - l->p1;
913         Vector v1 = point - l->p1;
914         Vector v2 = point - l->p2;
915         double t = Geometry::ParameterOfLineAndPoint(l->p1, l->p2, point);
916         double distance;
917
918         if (t < 0.0)
919                 distance = v1.Magnitude();
920         else if (t > 1.0)
921                 distance = v2.Magnitude();
922         else
923                 // distance = ?Det?(ls, v1) / |ls|
924                 distance = fabs((lineSegment.x * v1.y - v1.x * lineSegment.y)
925                         / lineSegment.Magnitude());
926
927         if ((v1.Magnitude() * Global::zoom) < 8.0)
928         {
929                 l->hitPoint[0] = true;
930 //              snapPoint = l->p1;
931 //              snapPointIsValid = true;
932         }
933         else if ((v2.Magnitude() * Global::zoom) < 8.0)
934         {
935                 l->hitPoint[1] = true;
936 //              snapPoint = l->p2;
937 //              snapPointIsValid = true;
938         }
939         else if ((distance * Global::zoom) < 5.0)
940                 l->hitObject = true;
941
942         bool oldHovered = l->hovered;
943         l->hovered = (l->hitPoint[0] || l->hitPoint[1] || l->hitObject ? true : false);
944
945         if (oldHovered != l->hovered)
946                 needUpdate = true;
947
948                         break;
949                 }
950                 case OTCircle:
951                 {
952                         Circle * c = (Circle *)obj;
953
954
955                         break;
956                 }
957                 default:
958                         break;
959                 }
960
961                 if (obj->hovered)
962                 {
963                         numHovered++;
964 //printf("MouseMove: OBJECT HOVERED (numHovered = %i)\n", numHovered);
965                 }
966         }
967         }
968 //printf("MouseMove: numHovered = %i\n", numHovered);
969
970         // This is used to draw the tool crosshair...
971         oldPoint = point;
972
973 //      if (/*document.NeedsUpdate() ||*/ Global::selectionInProgress /*|| toolAction*/)
974         if (needUpdate || Global::selectionInProgress || Global::tool)
975                 update();
976 }
977
978
979 void DrawingView::mouseReleaseEvent(QMouseEvent * event)
980 {
981         if (event->button() == Qt::LeftButton)
982         {
983 #if 0
984                 document.PointerReleased();
985 #endif
986
987 //We need to update especially if nothing collided and the state needs to change. !!! FIX !!!
988 //could set it up to use the document's update function (assumes that all object updates
989 //are being reported correctly:
990 //              if (document.NeedsUpdate())
991 //              if (collided)
992                         update();       // Do an update if collided with at least *one* object in the document
993
994 #if 0
995                 if (toolAction)
996                         toolAction->MouseReleased();
997 #endif
998
999                 if (Global::selectionInProgress)
1000                 {
1001                         // Select all the stuff inside of selection
1002                         Global::selectionInProgress = false;
1003
1004                         // Clear our vectors
1005                         select.empty();
1006                         hover.empty();
1007
1008                         // Scoop 'em up
1009                         std::vector<void *>::iterator i;
1010
1011                         for(i=document.objects.begin(); i!=document.objects.end(); i++)
1012                         {
1013                                 if (((Object *)(*i))->selected)
1014                                         select.push_back(*i);
1015
1016 //hmm, this is no good, too late to do any good :-P
1017 //                              if ((*i)->hovered)
1018 //                                      hover.push_back(*i);
1019                         }
1020                 }
1021         }
1022         else if (event->button() == Qt::MiddleButton)
1023         {
1024                 scrollDrag = false;
1025                 setCursor(Qt::ArrowCursor);
1026         }
1027 }
1028
1029
1030 void DrawingView::wheelEvent(QWheelEvent * event)
1031 {
1032         double zoomFactor = 1.25;
1033         QSize sizeWin = size();
1034         Vector center(sizeWin.width() / 2.0, sizeWin.height() / 2.0);
1035         center = Painter::QtToCartesianCoords(center);
1036
1037         // This is not centering for some reason. Need to figure out why. :-/
1038         if (event->delta() > 0)
1039         {
1040                 Vector newOrigin = center - ((center - Global::origin) / zoomFactor);
1041                 Global::origin = newOrigin;
1042                 Global::zoom *= zoomFactor;
1043         }
1044         else
1045         {
1046                 Vector newOrigin = center + ((-center + Global::origin) * zoomFactor);
1047                 Global::origin = newOrigin;
1048                 Global::zoom /= zoomFactor;
1049         }
1050
1051 #if 1
1052 //      Global::gridSpacing = gridPixels / Painter::zoom;
1053 //      UpdateGridBackground();
1054         SetGridSize(Global::gridSpacing * Global::zoom);
1055         update();
1056 //      zoomIndicator->setText(QString("Grid: %1\", BU: Inch").arg(Global::gridSpacing));
1057 #endif
1058 }
1059
1060
1061 void DrawingView::keyPressEvent(QKeyEvent * event)
1062 {
1063 #if 0
1064         if (toolAction)
1065                 toolAction->KeyDown(event->key());
1066 #endif
1067         if (event->key() == Qt::Key_Shift)
1068                 shiftDown = true;
1069         else if (event->key() == Qt::Key_Control)
1070                 ctrlDown = true;
1071 }
1072
1073
1074 void DrawingView::keyReleaseEvent(QKeyEvent * event)
1075 {
1076 #if 0
1077         if (toolAction)
1078                 toolAction->KeyReleased(event->key());
1079 #endif
1080         if (event->key() == Qt::Key_Shift)
1081                 shiftDown = false;
1082         else if (event->key() == Qt::Key_Control)
1083                 ctrlDown = false;
1084 }
1085
1086 //
1087 // This looks strange, but it's really quite simple: We want a point that's
1088 // more than half-way to the next grid point to snap there while conversely we
1089 // want a point that's less than half-way to to the next grid point then snap
1090 // to the one before it. So we add half of the grid spacing to the point, then
1091 // divide by it so that we can remove the fractional part, then multiply it
1092 // back to get back to the correct answer.
1093 //
1094 Point DrawingView::SnapPointToGrid(Point point)
1095 {
1096         point += Global::gridSpacing / 2.0;             // *This* adds to Z!!!
1097         point /= Global::gridSpacing;
1098         point.x = floor(point.x);//need to fix this for negative numbers...
1099         point.y = floor(point.y);
1100         point.z = 0;                                    // Make *sure* Z doesn't go anywhere!!!
1101         point *= Global::gridSpacing;
1102         return point;
1103 }
1104