]> Shamusworld >> Repos - ttedit/blob - src/editwindow.cpp
Minor fixes to polygon adding, added Tau constants to math package.
[ttedit] / src / editwindow.cpp
1 //
2 // TTEDIT.CPP - The TrueType Editor
3 // by James L. Hammons
4 // (C) 2004 Underground Software
5 //
6 // JLH = James L. Hammons <jlhamm@acm.org>
7 //
8 // Who  When        What
9 // ---  ----------  -----------------------------------------------------------
10 // JLH  08/28/2008  Created this file
11 // JLH  09/02/2008  Separated scrolling from dedicated tool to MMB drag
12 // JLH  03/13/2009  Converted from wxWidgets to Qt
13 //
14
15 // FIXED:
16 //
17 // - Fixed scrolling
18 //
19 // STILL TO BE DONE:
20 //
21 // - Fix bug in Glyphpoints when dragging on an empty canvas or loading a font
22 // - Fix zooming, settings (ini)
23 // - Fix point adding bug 1: should be able to add points to empty canvas
24 // - Fix point adding bug 2: should be able to add point successfully to single
25 //   point on screen
26 // - Add poly multi-select
27 // - Add point multi-select
28 // - Undo system
29 //
30
31 // Uncomment this for debugging...
32 #define DEBUG
33 #define DEBUGFOO            // Various tool debugging...
34 #define DEBUGTP                         // Toolpalette debugging...
35
36 #include "editwindow.h"
37 #include "charwindow.h"
38 #include "debug.h"
39 #include "global.h"
40 #include "mainwindow.h"
41 #include "ttedit.h"
42 #include "vector.h"
43
44
45 EditWindow::EditWindow(QWidget * parent/*= NULL*/): QWidget(parent),
46         tool(TOOLSelect), ptHighlight(-1), oldPtHighlight(-1), ptNextHighlight(-1),
47         oldPtNextHighlight(-1), polyFirstPoint(true), showRotationCenter(false),
48         haveZeroPoint(false), selectionInProgress(false)
49 {
50         setBackgroundRole(QPalette::Base);
51         setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
52
53         toolPalette = new ToolWindow();
54         CreateCursors();
55         setCursor(cur[TOOLSelect]);
56         setMouseTracking(true);
57         ClearSelection();
58 }
59
60
61 QSize EditWindow::minimumSizeHint() const
62 {
63         return QSize(50, 50);
64 }
65
66
67 QSize EditWindow::sizeHint() const
68 {
69         return QSize(400, 400);
70 }
71
72
73 void EditWindow::CreateCursors(void)
74 {
75         int hotx[12] = {  1,  1, 1, 15,  1,  1,  1,  1,  1,  1,  1, 11 };
76         int hoty[12] = {  1,  1, 1, 13,  1,  1,  1,  1,  1,  1,  1, 11 };
77         char cursorName[12][48] = { "select", "select-poly", "select-multi", "zoom",
78                 "add-point", "add-poly", "del-point", "del-poly", "rotate", "rotate",
79                 "select", "scroll" };
80
81         for(int i=0; i<12; i++)
82         {
83                 QPixmap pmTmp(QString(":/res/cursor-%1.png").arg(cursorName[i]));
84                 cur[i] = QCursor(pmTmp, hotx[i], hoty[i]);
85         }
86 }
87
88
89 /*
90 TODO:
91  o  Different colors for polys on selected points
92  o  Different colors for handles on non-selected polys
93  o  Line of sight (dashed, dotted) for off-curve points
94  o  Repaints for press/release of CTRL/SHIFT during point creation
95 */
96 void EditWindow::paintEvent(QPaintEvent * /*event*/)
97 {
98         QPainter qtp(this);
99         Painter painter(&qtp);
100         painter.SetRenderHint(QPainter::Antialiasing);
101
102         Global::viewportHeight = size().height();
103         Global::screenSize = Vector(size().width(), size().height());
104
105         // Draw coordinate axes
106
107         painter.SetPen(QPen(Qt::blue, 1.0, Qt::DotLine));
108         painter.DrawLine(0, -16384, 0, 16384);
109         painter.DrawLine(-16384, 0, 16384, 0);
110
111         // Draw rotation center (if active)
112
113         if (showRotationCenter)
114         {
115                 painter.SetPen(QPen(Qt::red, 2.0, Qt::SolidLine));
116                 painter.DrawLine(rotationCenter.x + 7, rotationCenter.y, rotationCenter.x - 7, rotationCenter.y);
117                 painter.DrawLine(rotationCenter.x, rotationCenter.y + 7, rotationCenter.x, rotationCenter.y - 7);
118         }
119
120         // Draw points
121
122         for(int i=0; i<pts.GetNumPoints(); i++)
123         {
124                 /*if (tool == TOOLMultiSelect)
125                 {
126                         if (selectedPoints[i])
127                         {
128                                 qtp.setPen(QPen(Qt::red, 1.0, Qt::SolidLine));
129                         }
130                         else
131                         {
132                                 qtp.setPen(QPen(Qt::black, 1.0, Qt::SolidLine));
133                         }
134                 }
135                 else*/
136                 if (((i == ptHighlight) && (tool != TOOLMultiSelect)) || ((tool == TOOLMultiSelect) && selectedPoints[i]))
137                 {
138                         painter.SetPen(QPen(Qt::red, 1.0, Qt::SolidLine));
139
140                         if (pts.GetOnCurve(i))
141                         {
142                                 painter.DrawSquareDotN(pts.GetXY(i), 7);
143                                 painter.DrawSquareDotN(pts.GetXY(i), 9);
144                         }
145                         else
146                         {
147                                 painter.DrawRoundDotN(pts.GetXY(i), 7);
148                                 painter.DrawRoundDotN(pts.GetXY(i), 9);
149                         }
150                 }
151                 else if ((i == ptHighlight || i == ptNextHighlight) && tool == TOOLAddPt)
152                 {
153                         painter.SetPen(QPen(Qt::green, 1.0, Qt::SolidLine));
154
155                         if (pts.GetOnCurve(i))
156                         {
157                                 painter.DrawSquareDotN(pts.GetXY(i), 7);
158                                 painter.DrawSquareDotN(pts.GetXY(i), 9);
159                         }
160                         else
161                         {
162                                 painter.DrawRoundDotN(pts.GetXY(i), 7);
163                                 painter.DrawRoundDotN(pts.GetXY(i), 9);
164                         }
165                 }
166                 else
167                 {
168                         painter.SetPen(QPen(Qt::black, 1.0, Qt::SolidLine));
169
170 #if 1
171                         if (pts.GetOnCurve(i))
172                                 painter.DrawSquareDot(pts.GetXY(i));
173                         else
174                                 painter.DrawRoundDot(pts.GetXY(i));
175 #else
176                         (pts.GetOnCurve(i) ? DrawSquareDot(p, pts.GetX(i), pts.GetY(i))
177                                 : DrawRoundDot(p, pts.GetX(i), pts.GetY(i)));
178 #endif
179                 }
180
181                 if (tool == TOOLDelPt && i == ptHighlight)
182                 {
183                         painter.SetPen(QPen(Qt::red, 1.0, Qt::SolidLine));
184                         painter.DrawLine(pts.GetX(i) - 5, pts.GetY(i) - 5, pts.GetX(i) + 5, pts.GetY(i) + 5);
185                         painter.DrawLine(pts.GetX(i) + 5, pts.GetY(i) - 5, pts.GetX(i) - 5, pts.GetY(i) + 5);
186                 }
187         }
188
189         // Draw highlighted point on poly add tool
190         if (tool == TOOLAddPoly)
191         {
192                 painter.SetPen(QPen(Qt::red, 1.0, Qt::SolidLine));
193
194                 if (addPointOnCurve)
195                 {
196                         painter.DrawSquareDotN(addPoint, 7);
197                         painter.DrawSquareDotN(addPoint, 9);
198                 }
199                 else
200                 {
201                         painter.DrawRoundDotN(addPoint, 7);
202                         painter.DrawRoundDotN(addPoint, 9);
203                 }
204         }
205
206         // Draw curve formed by points
207
208         painter.SetPen(QPen(Qt::black, 1.0, Qt::SolidLine));
209         DrawGlyph(painter, pts);
210
211         if (haveZeroPoint)
212         {
213                 // Rotation code
214                 GlyphPoints rotated = pts;
215
216                 if (tool == TOOLRotate)
217                         rotated.RotatePoints(rotationAngle, IPoint(rotationCenter.x, rotationCenter.y));
218                 else if (tool == TOOLRotatePoly)
219                 {
220                         uint16_t poly = rotated.GetPolyForPointNumber(ptHighlight);
221                         rotated.RotatePolyAroundCentroid(poly, rotationAngle);
222                 }
223
224                 painter.SetPen(QPen(QColor(255, 0, 255), 1.0, Qt::SolidLine));
225                 DrawGlyph(painter, rotated);
226         }
227
228         if (selectionInProgress)
229         {
230                 painter.SetPen(QPen(QColor(255, 127, 0, 255)));
231                 painter.SetBrush(QBrush(QColor(255, 127, 0, 100)));
232                 painter.DrawRect(selection);
233         }
234 }
235
236
237 void EditWindow::DrawGlyph(Painter & p, GlyphPoints & glyph)
238 {
239         for(int poly=0; poly<glyph.GetNumPolys(); poly++)
240         {
241 #if 0
242                 if (glyph.GetNumPoints(poly) < 3)
243                         continue;
244
245                 // Initial move: If our start point is on curve, then go to it. Otherwise,
246                 // check previous point. If it's on curve, go to it otherwise go the
247                 // midpoint between start point and previous (since it's between two curve
248                 // control points).
249                 IPoint pt = (glyph.GetOnCurve(poly, 0)
250                         ? glyph.GetPoint(poly, 0) : (glyph.GetPrevOnCurve(poly, 0)
251                                 ? glyph.GetPrevPoint(poly, 0) : glyph.GetMidpointToPrev(poly, 0)));
252
253 // Need to add separate color handling here for polys that are being manipulated...
254
255                 for(int i=0; i<glyph.GetNumPoints(poly); i++)
256                 {
257                         // If this point and then next are both on curve, we have a line...
258                         if (glyph.GetOnCurve(poly, i) && glyph.GetNextOnCurve(poly, i))
259                         {
260                                 IPoint pt2 = glyph.GetNextPoint(poly, i);
261                                 p.drawLine(pt.x, pt.y, pt2.x, pt2.y);
262                                 pt = pt2;
263                         }
264                         else
265                         {
266                                 // Skip point if it's on curve (start of curve--it's already
267                                 // been plotted so we don't need to handle it...)
268                                 if (glyph.GetOnCurve(poly, i))
269                                         continue;
270
271                                 // We are now guaranteed that we are sitting on a curve control point
272                                 // (off curve). Figure the extent of the curve: If the following is a
273                                 // curve control point, then use the midpoint to it otherwise go to
274                                 // the next point since it's on curve.
275                                 IPoint pt2 = (glyph.GetNextOnCurve(poly, i)
276                                         ? glyph.GetNextPoint(poly, i) : glyph.GetMidpointToNext(poly, i));
277
278                                 Bezier(p, pt, glyph.GetPoint(poly, i), pt2);
279                                 pt = pt2;
280                         }
281                 }
282 #else
283                 DrawGlyphPoly(p, glyph, poly);
284 #endif
285         }
286 }
287
288
289 /*
290 So, to make it draw the point the pointer is pointing at, we need to do something. Either patch the GlyphPoints to handle it, or insert the point into the GlyphPoints and delete it if the user changes tools. Either way, need
291 to change the color of the line(s) drawn to the point to signal to the user
292 that it isn't finalized until they click the button.
293 */
294 void EditWindow::DrawGlyphPoly(Painter & p, GlyphPoints & glyph, uint16_t poly)
295 {
296         // Sanity check
297         if (glyph.GetNumPoints(poly) < 3)
298                 return;
299
300         IPoint p1 = glyph.GetPrevPoint(poly, 0);
301         IPoint p2 = glyph.GetPoint(poly, 0);
302
303         // Inject the new poly point into the current polygon
304         if ((tool == TOOLAddPoly) && (poly == (glyph.GetNumPolys() - 1)))
305         {
306                 p1 = IPoint(addPoint.x, addPoint.y, addPointOnCurve);
307         }
308
309         for(int i=0; i<glyph.GetNumPoints(poly); i++)
310         {
311                 IPoint p3 = glyph.GetNextPoint(poly, i);
312
313                 if ((tool == TOOLAddPoly) && (poly == (glyph.GetNumPolys() - 1))
314                         && (i == (glyph.GetNumPoints(poly) - 1)))
315                 {
316                         p3 = IPoint(addPoint.x, addPoint.y, addPointOnCurve);
317                         p.SetPen(QPen(Qt::green, 1.0, Qt::SolidLine));
318                         DrawGlyphSegment(p, p1, p2, p3);
319                         p1 = p2;
320                         p2 = p3;
321                         p3 = glyph.GetNextPoint(poly, i);
322                 }
323
324                 DrawGlyphSegment(p, p1, p2, p3);
325
326                 p1 = p2;
327                 p2 = p3;
328         }
329 }
330
331
332 //
333 // Draw a glyph segment given 3 points
334 //
335 void EditWindow::DrawGlyphSegment(Painter & p, IPoint p1, IPoint p2, IPoint p3)
336 {
337         if (p2.onCurve)
338         {
339                 // Skip drawing if the middle point is on curve and the last is off
340                 if (p3.onCurve)
341                         p.DrawLine(p2, p3);
342         }
343         else
344         {
345                 // The middle point is off curve, and so we need to draw a Bezier curve.
346                 // Also, depending on whether or not the previous or follow points are
347                 // off curve, we need to draw to the midpoints if so.
348                 IPoint mid12 = IPoint((p1.x + p2.x) / 2, (p1.y + p2.y) / 2);
349                 IPoint mid23 = IPoint((p2.x + p3.x) / 2, (p2.y + p3.y) / 2);
350                 p.DrawBezier((p1.onCurve ? p1 : mid12), p2, (p3.onCurve ? p3 : mid23));
351         }
352 }
353
354
355 void EditWindow::ClearSelection(void)
356 {
357         for(int i=0; i<65536; i++)
358                 selectedPoints[i] = false;
359 }
360
361
362 void EditWindow::mousePressEvent(QMouseEvent * event)
363 {
364         if (event->button() == Qt::RightButton)
365         {
366                 toolPalette->move(event->globalPos());
367                 toolPalette->setVisible(true);
368                 setCursor(cur[TOOLSelect]);
369                 toolPalette->prevTool = TOOLSelect;
370         }
371         else if (event->button() == Qt::MidButton)
372         {
373                 // Scrolling cursor
374                 setCursor(cur[11]);
375                 ptPrevious = Vector(event->x(), event->y());
376                 ptPrevious /= Global::zoom;
377         }
378         else if (event->button() == Qt::LeftButton)
379         {
380                 if (tool == TOOLScroll || tool == TOOLZoom)
381 ;//meh                  CaptureMouse();                                         // Make sure we capture the mouse when in scroll/zoom mode
382                 else if (tool == TOOLMultiSelect)
383                 {
384 //                      QPoint pt = GetAdjustedMousePosition(event);
385                         Vector pt = Painter::QtToCartesianCoords(Vector(event->x(), event->y()));
386                         selectionInProgress = true;
387                         selection.setTopLeft(QPoint(pt.x, pt.y));
388                         selection.setBottomRight(QPoint(pt.x, pt.y));
389                 }
390                 else if (tool == TOOLAddPt)             // "Add Point" tool
391                 {
392 //                      QPoint pt = GetAdjustedMousePosition(event);
393                         Vector pt = Painter::QtToCartesianCoords(Vector(event->x(), event->y()));
394                         IPoint pointToAdd(pt.x, pt.y, ((event->modifiers() == Qt::ShiftModifier || event->modifiers() == Qt::ControlModifier) ? false : true));
395
396                         if (pts.GetNumPoints() < 2)
397                         {
398 //                              pts += IPoint(pt.x(), pt.y(), ((event->modifiers() == Qt::ShiftModifier || event->modifiers() == Qt::ControlModifier) ? false : true));
399                                 pts += pointToAdd;
400                                 ptHighlight = pts.GetNumPoints() - 1;
401                         }
402                         else
403                         {
404 //                              QPoint pt = GetAdjustedMousePosition(event);
405 //                              pts.InsertPoint(pts.GetNext(ptHighlight), pt.x(), pt.y(), ((event->modifiers() == Qt::ShiftModifier || event->modifiers() == Qt::ControlModifier) ? false : true));
406                                 pts.InsertPoint(pts.GetNext(ptHighlight), pointToAdd);
407                                 ptHighlight = ptNextHighlight;
408 //                              update();
409                         }
410
411                         update();
412                 }
413 // Moved to mouse up routine, so we can slide the point around
414 #if 0
415                 else if (tool == TOOLAddPoly)   // "Add Poly" tool
416                 {
417 #ifdef DEBUGFOO
418 WriteLogMsg("Adding point... # polys: %u, # points: %u", pts.GetNumPolys(), pts.GetNumPoints());
419 #endif
420                         if (polyFirstPoint)
421                         {
422                                 polyFirstPoint = false;
423                                 pts.AddNewPolyAtEnd();
424                         }
425
426 //                      QPoint pt = GetAdjustedMousePosition(event);
427                         Vector pt = Painter::QtToCartesianCoords(Vector(event->x(), event->y()));
428 //printf("GetAdjustedMousePosition = %i, %i\n", pt.x(), pt.y());
429                         // Append a point to the end of the structure
430                         pts += IPoint(pt.x, pt.y, ((event->modifiers() == Qt::ShiftModifier || event->modifiers() == Qt::ControlModifier) ? false : true));
431                         ptHighlight = pts.GetNumPoints() - 1;
432                         update();
433 #ifdef DEBUGFOO
434 WriteLogMsg(" --> [# polys: %u, # points: %u]\n", pts.GetNumPolys(), pts.GetNumPoints());
435 #endif
436                 }
437 #endif
438                 else if (tool == TOOLSelect || tool == TOOLPolySelect)
439                 {
440                         if (pts.GetNumPoints() > 0)
441                         {
442 //                              pt = GetAdjustedClientPosition(pts.GetX(ptHighlight), pts.GetY(ptHighlight));
443                                 Vector pt = Painter::CartesianToQtCoords(Vector(pts.GetX(ptHighlight), pts.GetY(ptHighlight)));
444 //printf("GetAdjustedClientPosition = %lf, %lf\n", pt.x, pt.y);
445                                 QPoint warp(pt.x, pt.y);
446                                 QCursor::setPos(mapToGlobal(warp));
447
448                                 if (event->modifiers() == Qt::ShiftModifier || event->modifiers() == Qt::ControlModifier)
449                                 {
450                                         pts.SetOnCurve(ptHighlight, !pts.GetOnCurve(ptHighlight));
451                                         update();
452                                 }
453                         }
454                 }
455                 else if (tool == TOOLDelPt)
456                 {
457                         if (pts.GetNumPoints() > 0)
458 //Or could use:
459 //                      if (ptHighlight != -1)
460                         {
461 //This assumes that WM_MOUSEMOVE happens before this!
462 //The above commented out line should take care of this contingency... !!! FIX !!!
463                                 pts.DeletePoint(ptHighlight);
464                                 update();
465                         }
466                 }
467                 else if (tool == TOOLRotate)
468                 {
469                         // I think what's needed here is to keep the initial mouse click,
470                         // paint the rotation center, then use the 1st mouse move event to
471                         // establish the rotation "zero line", which becomes the line of
472                         // reference to all subsequent mouse moves.
473 //                      rotationCenter = GetAdjustedMousePosition(event);
474                         rotationCenter = Painter::QtToCartesianCoords(Vector(event->x(), event->y()));
475                         showRotationCenter = true;
476                         haveZeroPoint = false;
477                         rotationAngle = 0;
478                         update();
479                 }
480                 else if (tool == TOOLRotatePoly)
481                 {
482                         IPoint centroid = pts.GetPolyCentroid(pts.GetPolyForPointNumber(ptHighlight));
483                         rotationCenter = Vector(centroid.x, centroid.y);
484                         showRotationCenter = true;
485 //                      pt = GetAdjustedClientPosition(pts.GetX(ptHighlight), pts.GetY(ptHighlight));
486                         Vector pt = Painter::CartesianToQtCoords(Vector(pts.GetX(ptHighlight), pts.GetY(ptHighlight)));
487                         QCursor::setPos(mapToGlobal(QPoint(pt.x, pt.y)));
488                         rotationZeroPoint = Vector(pts.GetX(ptHighlight), pts.GetY(ptHighlight));
489                         haveZeroPoint = true;
490                         rotationAngle = 0;
491                         update();
492                 }
493                 else if (tool == TOOLFlipWinding)
494                 {
495                         pts.InvertPolyDrawSequence(pts.GetPolyForPointNumber(ptHighlight));
496 //                      pt = GetAdjustedClientPosition(pts.GetX(ptHighlight), pts.GetY(ptHighlight));
497                         Vector pt = Painter::CartesianToQtCoords(Vector(pts.GetX(ptHighlight), pts.GetY(ptHighlight)));
498                         QCursor::setPos(mapToGlobal(QPoint(pt.x, pt.y)));
499                         update();
500                 }
501         }
502
503         event->accept();
504 }
505
506
507 void EditWindow::mouseMoveEvent(QMouseEvent * event)
508 {
509         if (event->buttons() == Qt::RightButton)
510         {
511                 ToolType newTool = toolPalette->FindSelectedTool();
512
513                 if (newTool != toolPalette->prevTool)
514                 {
515                         toolPalette->prevTool = newTool;
516                         toolPalette->repaint();
517                 }
518         }
519         else if (event->buttons() == Qt::MidButton)
520         {
521                 // Calc offset from previous point
522                 Vector pt(event->x(), event->y());
523                 pt /= Global::zoom;
524                 ptOffset = Vector(pt.x - ptPrevious.x, -(pt.y - ptPrevious.y));
525
526 // Then multiply it by the scaling factor. Whee!
527                 // This looks wacky because we're using screen coords for the offset...
528                 // Otherwise, we would subtract both offsets!
529 //              offsetX -= ptOffset.x, offsetY += ptOffset.y;
530                 Global::origin -= ptOffset;
531                 ptPrevious = pt;
532                 update();
533         }
534         else if (event->buttons() == Qt::LeftButton)
535         {
536                 if (tool == TOOLAddPt || tool == TOOLAddPoly || tool == TOOLSelect)
537                 {
538                         // Bail out if we have the select tool and no points yet...
539                         if ((tool == TOOLSelect) && (pts.GetNumPoints() == 0))
540                                 return;
541
542 //                      QPoint pt2 = GetAdjustedMousePosition(event);
543                         Vector pt2 = Painter::QtToCartesianCoords(Vector(event->x(), event->y()));
544
545                         if (tool != TOOLSelect)
546                         {
547                                 addPoint = pt2;
548                                 ptHighlight = -1;
549                                 // Prolly should move this to the key handlers below...
550 //now we do! :-D
551 //                              addPointOnCurve = ((event->modifiers() == Qt::ShiftModifier) || (event->modifiers() == Qt::ControlModifier) ? false : true);
552                         }
553                         else
554                                 pts.SetXY(ptHighlight, pt2.x, pt2.y);
555
556                         update();
557                 }
558                 else if (tool == TOOLPolySelect)
559                 {
560                         if (pts.GetNumPoints() > 0)
561                         {
562 //                              QPoint pt2 = GetAdjustedMousePosition(event);
563                                 Vector pt2 = Painter::QtToCartesianCoords(Vector(event->x(), event->y()));
564                                 // Should also set onCurve here as well, depending on keystate
565 //Or should we?
566 //Would be nice, but we'd need to trap the keyPressEvent() as well, otherwise pressing/releasing
567 //the hotkey would show no change until the user moved their mouse.
568                                 pts.OffsetPoly(pts.GetPoly(ptHighlight), pt2.x - pts.GetX(ptHighlight), pt2.y - pts.GetY(ptHighlight));
569                                 update();
570                         }
571                 }
572                 else if (tool == TOOLRotate || tool == TOOLRotatePoly)
573                 {
574                         if (pts.GetNumPoints() > 0)
575                         {
576                                 if (!haveZeroPoint)
577                                 {
578 //                                      rotationZeroPoint = GetAdjustedMousePosition(event);
579                                         rotationZeroPoint = Painter::QtToCartesianCoords(Vector(event->x(), event->y()));
580                                         haveZeroPoint = true;
581                                 }
582                                 else
583                                 {
584                                         // Figure out the angle between the "zero" vector and the current one,
585                                         // then rotate all points relative to the "zero" vector (done by paint())
586 //                                      QPoint currentPoint = GetAdjustedMousePosition(event);
587                                         Vector currentPoint = Painter::QtToCartesianCoords(Vector(event->x(), event->y()));
588                                         Vector v1(rotationZeroPoint.x, rotationZeroPoint.y, 0,
589                                                 rotationCenter.x, rotationCenter.y, 0);
590                                         Vector v2(currentPoint.x, currentPoint.y, 0,
591                                                 rotationCenter.x, rotationCenter.y, 0);
592 //                                      rotationAngle = v1.Angle(v2);
593                                         rotationAngle = v2.Angle(v1);
594
595                                         QString s;
596                                         s.sprintf("%.3f degrees", rotationAngle * 180.0 / 3.14159265358979323);
597                                         ((TTEdit *)qApp)->mainWindow->statusBar()->showMessage(s);
598                                 }
599
600                                 update();
601                         }
602                 }
603                 else if (tool == TOOLMultiSelect)
604                 {
605 //                      QPoint pt = GetAdjustedMousePosition(event);
606                         Vector pt = Painter::QtToCartesianCoords(Vector(event->x(), event->y()));
607                         selection.setBottomRight(QPoint(pt.x, pt.y));
608
609                         for(int i=0; i<pts.GetNumPoints(); i++)
610                         {
611                                 if (selection.contains(QPoint(pts.GetX(i), pts.GetY(i))))
612                                         selectedPoints[i] = true;
613                                 else
614                                         selectedPoints[i] = false;
615                         }
616
617                         update();
618                 }
619         }
620         else if (event->buttons() == Qt::NoButton)
621         {
622                 // Moving, not dragging...
623                 if (tool == TOOLSelect || tool == TOOLDelPt || tool == TOOLAddPt
624                         || tool == TOOLPolySelect || tool == TOOLRotatePoly || tool == TOOLFlipWinding)
625                 {
626 //                      QPoint pt2 = GetAdjustedMousePosition(event);
627                         Vector pt2 = Painter::QtToCartesianCoords(Vector(event->x(), event->y()));
628                         double closest = 1.0e+99;
629
630                         for(int i=0; i<pts.GetNumPoints(); i++)
631                         {
632                                 double dist = ((pt2.x - pts.GetX(i)) * (pt2.x - pts.GetX(i)))
633                                         + ((pt2.y - pts.GetY(i)) * (pt2.y - pts.GetY(i)));
634
635                                 if (dist < closest)
636                                         closest = dist, ptHighlight = i;
637                         }
638
639                         if (ptHighlight != oldPtHighlight)
640                         {
641                                 oldPtHighlight = ptHighlight;
642                                 update();
643                         }
644
645                         // What follows here looks like voodoo, but is really simple. What
646                         // we do is check to see if the mouse point has a perpendicular
647                         // intersection with any of the line segments. If it does,
648                         // calculate the length of the perpendicular and choose the
649                         // smallest length. If there is no perpendicular, then choose the
650                         // length of line connecting the closer of either the first
651                         // endpoint or the second and choose the smallest of those.
652
653                         // There is one bit of math that looks like voodoo to me ATM--will
654                         // explain once I understand it better (the calculation of the
655                         // length of the perpendicular).
656
657                         if (pts.GetNumPoints() > 1 && tool == TOOLAddPt)
658                         {
659                                 double smallest = 1.0e+99;
660
661                                 for(int i=0; i<pts.GetNumPoints(); i++)
662                                 {
663                                         int32_t p1x = pts.GetX(i), p1y = pts.GetY(i),
664                                                 p2x = pts.GetX(pts.GetNext(i)), p2y = pts.GetY(pts.GetNext(i));
665
666                                         Vector ls(p2x, p2y, 0, p1x, p1y, 0),
667                                                 v1(pt2.x, pt2.y, 0, p1x, p1y, 0),
668                                                 v2(pt2.x, pt2.y, 0, p2x, p2y, 0);
669                                         double pp = ls.Dot(v1) / ls.Magnitude(), dist;
670 // Geometric interpretation:
671 // pp is the paremeterized point on the vector ls where the perpendicular
672 // intersects ls. If pp < 0, then the perpendicular lies beyond the 1st
673 // endpoint. If pp > length of ls, then the perpendicular lies beyond the 2nd
674 // endpoint.
675
676                                         if (pp < 0.0)
677                                                 dist = v1.Magnitude();
678                                         else if (pp > ls.Magnitude())
679                                                 dist = v2.Magnitude();
680                                         else                                    // distance = ?Det?(ls, v1) / |ls|
681                                                 dist = fabs((ls.x * v1.y - v1.x * ls.y) / ls.Magnitude());
682
683 //The answer to the above looks like it might be found here:
684 //
685 //If the segment endpoints are s and e, and the point is p, then the test for
686 //the perpendicular intercepting the segment is equivalent to insisting that
687 //the two dot products {s-e}.{s-p} and {e-s}.{e-p} are both non-negative.
688 //Perpendicular distance from the point to the segment is computed by first
689 //computing the area of the triangle the three points form, then dividing by
690 //the length of the segment.  Distances are done just by the Pythagorean
691 //theorem. Twice the area of the triangle formed by three points is the
692 //determinant of the following matrix:
693 //
694 //sx sy 1
695 //ex ey 1
696 //px py 1
697 //
698 //By translating the start point to the origin, this can be rewritten as:
699 //By subtracting row 1 from all rows, you get the following:
700 //[because sx = sy = 0. you could leave out the -sx/y terms below. because we
701 //subtracted row 1 from all rows (including row 1) row 1 turns out to be zero.
702 //duh!]
703 //
704 //0         0         0
705 //(ex - sx) (ey - sy) 0
706 //(px - sx) (py - sy) 0
707 //
708 //which greatly simplifies the calculation of the determinant.
709
710                                         if (dist < smallest)
711                                                 smallest = dist, ptNextHighlight = pts.GetNext(i), ptHighlight = i;
712                                 }
713
714                                 if (ptNextHighlight != oldPtNextHighlight)
715                                 {
716                                         oldPtNextHighlight = ptNextHighlight;
717                                         update();
718                                 }
719                         }
720                 }
721
722                 ptPrevious = Vector(event->x(), event->y());
723                 addPoint = Painter::QtToCartesianCoords(Vector(event->x(), event->y()));
724 //handled by real key handlers now...
725 //              addPointOnCurve = ((event->modifiers() == Qt::ShiftModifier) || (event->modifiers() == Qt::ControlModifier) ? false : true);
726
727                 if (tool == TOOLAddPoly)
728                         update();
729         }
730
731         event->accept();
732 }
733
734
735 void EditWindow::mouseReleaseEvent(QMouseEvent * event)
736 {
737         if (event->button() == Qt::RightButton)
738         {
739                 ToolType newTool = toolPalette->FindSelectedTool();
740
741                 // We only change the tool if a new one was actually selected. Otherwise, we do nothing.
742                 if (newTool != TOOLNone)
743                 {
744                         tool = newTool;
745
746                         if (tool == TOOLScroll || tool == TOOLZoom || tool == TOOLAddPoly
747                                 || tool == TOOLDelPoly)
748                                 ptHighlight = -1;
749
750                         if (tool == TOOLAddPoly)
751                                 polyFirstPoint = true;
752                 }
753
754                 toolPalette->setVisible(false);
755                 setCursor(cur[tool]);
756                 // Just in case we changed highlighting style with the new tool...
757                 update();
758         }
759         else if (event->button() == Qt::MidButton)
760         {
761                 setCursor(cur[tool]);                                           // Restore previous cursor
762         }
763         else if (event->button() == Qt::LeftButton)
764         {
765                 if (showRotationCenter)
766                 {
767                         showRotationCenter = false;
768                         haveZeroPoint = false;
769
770                         if (tool == TOOLRotate)
771                                 pts.RotatePoints(rotationAngle, IPoint(rotationCenter.x, rotationCenter.y));
772                         else
773                         {
774                                 uint16_t poly = pts.GetPolyForPointNumber(ptHighlight);
775                                 pts.RotatePolyAroundCentroid(poly, rotationAngle);
776                         }
777
778                         update();
779                         ((TTEdit *)qApp)->mainWindow->statusBar()->showMessage("");
780                 }
781
782 //              if (tool == TOOLScroll || tool == TOOLZoom)
783 //                      ReleaseMouse();
784 //this is prolly too much
785                 ((TTEdit *)qApp)->charWnd->MakePathFromPoints(&pts);
786                 ((TTEdit *)qApp)->charWnd->update();
787
788                 if (tool == TOOLMultiSelect)
789                 {
790                         selectionInProgress = false;
791                         update();
792                 }
793                 else if (tool == TOOLAddPoly)   // "Add Poly" tool
794                 {
795 #ifdef DEBUGFOO
796 WriteLogMsg("Adding point... # polys: %u, # points: %u", pts.GetNumPolys(), pts.GetNumPoints());
797 #endif
798                         if (polyFirstPoint)
799                         {
800                                 polyFirstPoint = false;
801                                 pts.AddNewPolyAtEnd();
802                         }
803
804 //                      QPoint pt = GetAdjustedMousePosition(event);
805                         Vector pt = Painter::QtToCartesianCoords(Vector(event->x(), event->y()));
806 //printf("GetAdjustedMousePosition = %i, %i\n", pt.x(), pt.y());
807                         // Append a point to the end of the structure
808                         pts += IPoint(pt.x, pt.y, addPointOnCurve);
809 //                      ptHighlight = pts.GetNumPoints() - 1;
810                         update();
811 #ifdef DEBUGFOO
812 WriteLogMsg(" --> [# polys: %u, # points: %u]\n", pts.GetNumPolys(), pts.GetNumPoints());
813 #endif
814                 }
815         }
816
817         event->accept();
818 }
819
820
821 void EditWindow::keyPressEvent(QKeyEvent * event)
822 {
823         // We do this here because of the ptHighlight nonsense. If we're in
824         // the add poly tool, we'll never see this if it's under the 'sanity'
825         // check (which is needed for the arrow key shite, but still...)
826         if ((event->key() == Qt::Key_Shift) || (event->key() == Qt::Key_Control))
827         {
828                 addPointOnCurve = false;
829                 update();
830         }
831
832         // Sanity checking...
833         if (ptHighlight == -1)
834                 return;
835
836         if (event->key() == Qt::Key_Up)
837         {
838                 pts.SetXY(ptHighlight, pts.GetX(ptHighlight), pts.GetY(ptHighlight) + 1);
839         }
840         else if (event->key() == Qt::Key_Down)
841                 pts.SetXY(ptHighlight, pts.GetX(ptHighlight), pts.GetY(ptHighlight) - 1);
842         else if (event->key() == Qt::Key_Right)
843                 pts.SetXY(ptHighlight, pts.GetX(ptHighlight) + 1, pts.GetY(ptHighlight));
844         else if (event->key() == Qt::Key_Left)
845                 pts.SetXY(ptHighlight, pts.GetX(ptHighlight) - 1, pts.GetY(ptHighlight));
846         else
847                 return;
848
849 //Not need but you need to call the base class for some reason??
850 //      event->accept();
851         update();
852         ((TTEdit *)qApp)->charWnd->MakePathFromPoints(&pts);
853         ((TTEdit *)qApp)->charWnd->update();
854 }
855
856
857 void EditWindow::keyReleaseEvent(QKeyEvent * event)
858 {
859         if ((event->key() == Qt::Key_Shift) || (event->key() == Qt::Key_Control))
860         {
861                 addPointOnCurve = true;
862         }
863         else
864                 return;
865
866         update();
867 }
868