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