]> Shamusworld >> Repos - ttedit/blob - src/editwindow.cpp
First stab at showing effect of added points on a given poly.
[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 curve formed by points
190
191         painter.SetPen(QPen(Qt::black, 1.0, Qt::SolidLine));
192         DrawGlyph(painter, pts);
193
194         if (haveZeroPoint)
195         {
196                 // Rotation code
197                 GlyphPoints rotated = pts;
198
199                 if (tool == TOOLRotate)
200                         rotated.RotatePoints(rotationAngle, IPoint(rotationCenter.x, rotationCenter.y));
201                 else if (tool == TOOLRotatePoly)
202                 {
203                         uint16_t poly = rotated.GetPolyForPointNumber(ptHighlight);
204                         rotated.RotatePolyAroundCentroid(poly, rotationAngle);
205                 }
206
207                 painter.SetPen(QPen(QColor(255, 0, 255), 1.0, Qt::SolidLine));
208                 DrawGlyph(painter, rotated);
209         }
210
211         if (selectionInProgress)
212         {
213                 painter.SetPen(QPen(QColor(255, 127, 0, 255)));
214                 painter.SetBrush(QBrush(QColor(255, 127, 0, 100)));
215                 painter.DrawRect(selection);
216         }
217 }
218
219
220 void EditWindow::DrawGlyph(Painter & p, GlyphPoints & glyph)
221 {
222         for(int poly=0; poly<glyph.GetNumPolys(); poly++)
223         {
224 #if 0
225                 if (glyph.GetNumPoints(poly) < 3)
226                         continue;
227
228                 // Initial move: If our start point is on curve, then go to it. Otherwise,
229                 // check previous point. If it's on curve, go to it otherwise go the
230                 // midpoint between start point and previous (since it's between two curve
231                 // control points).
232                 IPoint pt = (glyph.GetOnCurve(poly, 0)
233                         ? glyph.GetPoint(poly, 0) : (glyph.GetPrevOnCurve(poly, 0)
234                                 ? glyph.GetPrevPoint(poly, 0) : glyph.GetMidpointToPrev(poly, 0)));
235
236 // Need to add separate color handling here for polys that are being manipulated...
237
238                 for(int i=0; i<glyph.GetNumPoints(poly); i++)
239                 {
240                         // If this point and then next are both on curve, we have a line...
241                         if (glyph.GetOnCurve(poly, i) && glyph.GetNextOnCurve(poly, i))
242                         {
243                                 IPoint pt2 = glyph.GetNextPoint(poly, i);
244                                 p.drawLine(pt.x, pt.y, pt2.x, pt2.y);
245                                 pt = pt2;
246                         }
247                         else
248                         {
249                                 // Skip point if it's on curve (start of curve--it's already
250                                 // been plotted so we don't need to handle it...)
251                                 if (glyph.GetOnCurve(poly, i))
252                                         continue;
253
254                                 // We are now guaranteed that we are sitting on a curve control point
255                                 // (off curve). Figure the extent of the curve: If the following is a
256                                 // curve control point, then use the midpoint to it otherwise go to
257                                 // the next point since it's on curve.
258                                 IPoint pt2 = (glyph.GetNextOnCurve(poly, i)
259                                         ? glyph.GetNextPoint(poly, i) : glyph.GetMidpointToNext(poly, i));
260
261                                 Bezier(p, pt, glyph.GetPoint(poly, i), pt2);
262                                 pt = pt2;
263                         }
264                 }
265 #else
266                 DrawGlyphPoly(p, glyph, poly);
267 #endif
268         }
269 }
270
271
272 /*
273 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
274 to change the color of the line(s) drawn to the point to signal to the user
275 that it isn't finalized until they click the button.
276 */
277 void EditWindow::DrawGlyphPoly(Painter & p, GlyphPoints & glyph, uint16_t poly)
278 {
279         // Sanity check
280         if (glyph.GetNumPoints(poly) < 3)
281                 return;
282
283         IPoint p1 = glyph.GetPrevPoint(poly, 0);
284         IPoint p2 = glyph.GetPoint(poly, 0);
285
286         // Inject the new poly point into the current polygon
287         if ((tool == TOOLAddPoly) && (poly == (glyph.GetNumPolys() - 1)))
288         {
289                 p1 = IPoint(addPoint.x, addPoint.y, addPointOnCurve);
290         }
291
292         for(int i=0; i<glyph.GetNumPoints(poly); i++)
293         {
294                 IPoint p3 = glyph.GetNextPoint(poly, i);
295
296                 if ((tool == TOOLAddPoly) && (poly == (glyph.GetNumPolys() - 1))
297                         && (i == (glyph.GetNumPoints(poly) - 1)))
298                 {
299                         p3 = IPoint(addPoint.x, addPoint.y, addPointOnCurve);
300                         p.SetPen(QPen(Qt::green, 1.0, Qt::SolidLine));
301                         DrawGlyphSegment(p, p1, p2, p3);
302                         p1 = p2;
303                         p2 = p3;
304                         p3 = glyph.GetNextPoint(poly, i);
305                 }
306
307                 DrawGlyphSegment(p, p1, p2, p3);
308
309                 p1 = p2;
310                 p2 = p3;
311         }
312 }
313
314
315 //
316 // Draw a glyph segment given 3 points
317 //
318 void EditWindow::DrawGlyphSegment(Painter & p, IPoint p1, IPoint p2, IPoint p3)
319 {
320         if (p2.onCurve)
321         {
322                 // Skip drawing if the middle point is on curve and the last is off
323                 if (p3.onCurve)
324                         p.DrawLine(p2, p3);
325         }
326         else
327         {
328                 // The middle point is off curve, and so we need to draw a Bezier curve.
329                 // Also, depending on whether or not the previous or follow points are
330                 // off curve, we need to draw to the midpoints if so.
331                 IPoint mid12 = IPoint((p1.x + p2.x) / 2, (p1.y + p2.y) / 2);
332                 IPoint mid23 = IPoint((p2.x + p3.x) / 2, (p2.y + p3.y) / 2);
333                 p.DrawBezier((p1.onCurve ? p1 : mid12), p2, (p3.onCurve ? p3 : mid23));
334         }
335 }
336
337
338 void EditWindow::ClearSelection(void)
339 {
340         for(int i=0; i<65536; i++)
341                 selectedPoints[i] = false;
342 }
343
344
345 void EditWindow::mousePressEvent(QMouseEvent * event)
346 {
347         if (event->button() == Qt::RightButton)
348         {
349                 toolPalette->move(event->globalPos());
350                 toolPalette->setVisible(true);
351                 setCursor(cur[TOOLSelect]);
352                 toolPalette->prevTool = TOOLSelect;
353         }
354         else if (event->button() == Qt::MidButton)
355         {
356                 // Scrolling cursor
357                 setCursor(cur[11]);
358                 ptPrevious = Vector(event->x(), event->y());
359                 ptPrevious /= Global::zoom;
360         }
361         else if (event->button() == Qt::LeftButton)
362         {
363                 if (tool == TOOLScroll || tool == TOOLZoom)
364 ;//meh                  CaptureMouse();                                         // Make sure we capture the mouse when in scroll/zoom mode
365                 else if (tool == TOOLMultiSelect)
366                 {
367 //                      QPoint pt = GetAdjustedMousePosition(event);
368                         Vector pt = Painter::QtToCartesianCoords(Vector(event->x(), event->y()));
369                         selectionInProgress = true;
370                         selection.setTopLeft(QPoint(pt.x, pt.y));
371                         selection.setBottomRight(QPoint(pt.x, pt.y));
372                 }
373                 else if (tool == TOOLAddPt)             // "Add Point" tool
374                 {
375 //                      QPoint pt = GetAdjustedMousePosition(event);
376                         Vector pt = Painter::QtToCartesianCoords(Vector(event->x(), event->y()));
377                         IPoint pointToAdd(pt.x, pt.y, ((event->modifiers() == Qt::ShiftModifier || event->modifiers() == Qt::ControlModifier) ? false : true));
378
379                         if (pts.GetNumPoints() < 2)
380                         {
381 //                              pts += IPoint(pt.x(), pt.y(), ((event->modifiers() == Qt::ShiftModifier || event->modifiers() == Qt::ControlModifier) ? false : true));
382                                 pts += pointToAdd;
383                                 ptHighlight = pts.GetNumPoints() - 1;
384                         }
385                         else
386                         {
387 //                              QPoint pt = GetAdjustedMousePosition(event);
388 //                              pts.InsertPoint(pts.GetNext(ptHighlight), pt.x(), pt.y(), ((event->modifiers() == Qt::ShiftModifier || event->modifiers() == Qt::ControlModifier) ? false : true));
389                                 pts.InsertPoint(pts.GetNext(ptHighlight), pointToAdd);
390                                 ptHighlight = ptNextHighlight;
391 //                              update();
392                         }
393
394                         update();
395                 }
396                 else if (tool == TOOLAddPoly)   // "Add Poly" tool
397                 {
398 #ifdef DEBUGFOO
399 WriteLogMsg("Adding point... # polys: %u, # points: %u", pts.GetNumPolys(), pts.GetNumPoints());
400 #endif
401                         if (polyFirstPoint)
402                         {
403                                 polyFirstPoint = false;
404                                 pts.AddNewPolyAtEnd();
405                         }
406
407 //                      QPoint pt = GetAdjustedMousePosition(event);
408                         Vector pt = Painter::QtToCartesianCoords(Vector(event->x(), event->y()));
409 //printf("GetAdjustedMousePosition = %i, %i\n", pt.x(), pt.y());
410                         // Append a point to the end of the structure
411                         pts += IPoint(pt.x, pt.y, ((event->modifiers() == Qt::ShiftModifier || event->modifiers() == Qt::ControlModifier) ? false : true));
412                         ptHighlight = pts.GetNumPoints() - 1;
413                         update();
414 #ifdef DEBUGFOO
415 WriteLogMsg(" --> [# polys: %u, # points: %u]\n", pts.GetNumPolys(), pts.GetNumPoints());
416 #endif
417                 }
418                 else if (tool == TOOLSelect || tool == TOOLPolySelect)
419                 {
420                         if (pts.GetNumPoints() > 0)
421                         {
422 //                              pt = GetAdjustedClientPosition(pts.GetX(ptHighlight), pts.GetY(ptHighlight));
423                                 Vector pt = Painter::CartesianToQtCoords(Vector(pts.GetX(ptHighlight), pts.GetY(ptHighlight)));
424 //printf("GetAdjustedClientPosition = %lf, %lf\n", pt.x, pt.y);
425                                 QPoint warp(pt.x, pt.y);
426                                 QCursor::setPos(mapToGlobal(warp));
427
428                                 if (event->modifiers() == Qt::ShiftModifier || event->modifiers() == Qt::ControlModifier)
429                                 {
430                                         pts.SetOnCurve(ptHighlight, !pts.GetOnCurve(ptHighlight));
431                                         update();
432                                 }
433                         }
434                 }
435                 else if (tool == TOOLDelPt)
436                 {
437                         if (pts.GetNumPoints() > 0)
438 //Or could use:
439 //                      if (ptHighlight != -1)
440                         {
441 //This assumes that WM_MOUSEMOVE happens before this!
442 //The above commented out line should take care of this contingency... !!! FIX !!!
443                                 pts.DeletePoint(ptHighlight);
444                                 update();
445                         }
446                 }
447                 else if (tool == TOOLRotate)
448                 {
449                         // I think what's needed here is to keep the initial mouse click,
450                         // paint the rotation center, then use the 1st mouse move event to
451                         // establish the rotation "zero line", which becomes the line of
452                         // reference to all subsequent mouse moves.
453 //                      rotationCenter = GetAdjustedMousePosition(event);
454                         rotationCenter = Painter::QtToCartesianCoords(Vector(event->x(), event->y()));
455                         showRotationCenter = true;
456                         haveZeroPoint = false;
457                         rotationAngle = 0;
458                         update();
459                 }
460                 else if (tool == TOOLRotatePoly)
461                 {
462                         IPoint centroid = pts.GetPolyCentroid(pts.GetPolyForPointNumber(ptHighlight));
463                         rotationCenter = Vector(centroid.x, centroid.y);
464                         showRotationCenter = true;
465 //                      pt = GetAdjustedClientPosition(pts.GetX(ptHighlight), pts.GetY(ptHighlight));
466                         Vector pt = Painter::CartesianToQtCoords(Vector(pts.GetX(ptHighlight), pts.GetY(ptHighlight)));
467                         QCursor::setPos(mapToGlobal(QPoint(pt.x, pt.y)));
468                         rotationZeroPoint = Vector(pts.GetX(ptHighlight), pts.GetY(ptHighlight));
469                         haveZeroPoint = true;
470                         rotationAngle = 0;
471                         update();
472                 }
473                 else if (tool == TOOLFlipWinding)
474                 {
475                         pts.InvertPolyDrawSequence(pts.GetPolyForPointNumber(ptHighlight));
476 //                      pt = GetAdjustedClientPosition(pts.GetX(ptHighlight), pts.GetY(ptHighlight));
477                         Vector pt = Painter::CartesianToQtCoords(Vector(pts.GetX(ptHighlight), pts.GetY(ptHighlight)));
478                         QCursor::setPos(mapToGlobal(QPoint(pt.x, pt.y)));
479                         update();
480                 }
481         }
482
483         event->accept();
484 }
485
486
487 void EditWindow::mouseMoveEvent(QMouseEvent * event)
488 {
489         if (event->buttons() == Qt::RightButton)
490         {
491                 ToolType newTool = toolPalette->FindSelectedTool();
492
493                 if (newTool != toolPalette->prevTool)
494                 {
495                         toolPalette->prevTool = newTool;
496                         toolPalette->repaint();
497                 }
498         }
499         else if (event->buttons() == Qt::MidButton)
500         {
501                 // Calc offset from previous point
502                 Vector pt(event->x(), event->y());
503                 pt /= Global::zoom;
504                 ptOffset = Vector(pt.x - ptPrevious.x, -(pt.y - ptPrevious.y));
505
506 // Then multiply it by the scaling factor. Whee!
507                 // This looks wacky because we're using screen coords for the offset...
508                 // Otherwise, we would subtract both offsets!
509 //              offsetX -= ptOffset.x, offsetY += ptOffset.y;
510                 Global::origin -= ptOffset;
511                 ptPrevious = pt;
512                 update();
513         }
514         else if (event->buttons() == Qt::LeftButton)
515         {
516                 if (tool == TOOLAddPt || tool == TOOLAddPoly || tool == TOOLSelect)
517                 {
518                         // Bail out if we have the select tool and no points yet...
519                         if (tool == TOOLSelect && pts.GetNumPoints() == 0)
520                                 return;
521
522 //                      QPoint pt2 = GetAdjustedMousePosition(event);
523                         Vector pt2 = Painter::QtToCartesianCoords(Vector(event->x(), event->y()));
524
525                         if (tool != TOOLSelect)
526                         {
527                                 addPoint = pt2;
528                                 // Prolly should move this to the key handlers below...
529                                 addPointOnCurve = ((event->modifiers() == Qt::ShiftModifier) || (event->modifiers() == Qt::ControlModifier) ? false : true);
530                         }
531                         else
532                                 pts.SetXY(ptHighlight, pt2.x, pt2.y);
533
534                         update();
535                 }
536                 else if (tool == TOOLPolySelect)
537                 {
538                         if (pts.GetNumPoints() > 0)
539                         {
540 //                              QPoint pt2 = GetAdjustedMousePosition(event);
541                                 Vector pt2 = Painter::QtToCartesianCoords(Vector(event->x(), event->y()));
542                                 // Should also set onCurve here as well, depending on keystate
543 //Or should we?
544 //Would be nice, but we'd need to trap the keyPressEvent() as well, otherwise pressing/releasing
545 //the hotkey would show no change until the user moved their mouse.
546                                 pts.OffsetPoly(pts.GetPoly(ptHighlight), pt2.x - pts.GetX(ptHighlight), pt2.y - pts.GetY(ptHighlight));
547                                 update();
548                         }
549                 }
550                 else if (tool == TOOLRotate || tool == TOOLRotatePoly)
551                 {
552                         if (pts.GetNumPoints() > 0)
553                         {
554                                 if (!haveZeroPoint)
555                                 {
556 //                                      rotationZeroPoint = GetAdjustedMousePosition(event);
557                                         rotationZeroPoint = Painter::QtToCartesianCoords(Vector(event->x(), event->y()));
558                                         haveZeroPoint = true;
559                                 }
560                                 else
561                                 {
562                                         // Figure out the angle between the "zero" vector and the current one,
563                                         // then rotate all points relative to the "zero" vector (done by paint())
564 //                                      QPoint currentPoint = GetAdjustedMousePosition(event);
565                                         Vector currentPoint = Painter::QtToCartesianCoords(Vector(event->x(), event->y()));
566                                         Vector v1(rotationZeroPoint.x, rotationZeroPoint.y, 0,
567                                                 rotationCenter.x, rotationCenter.y, 0);
568                                         Vector v2(currentPoint.x, currentPoint.y, 0,
569                                                 rotationCenter.x, rotationCenter.y, 0);
570 //                                      rotationAngle = v1.Angle(v2);
571                                         rotationAngle = v2.Angle(v1);
572
573                                         QString s;
574                                         s.sprintf("%.3f degrees", rotationAngle * 180.0 / 3.14159265358979323);
575                                         ((TTEdit *)qApp)->mainWindow->statusBar()->showMessage(s);
576                                 }
577
578                                 update();
579                         }
580                 }
581                 else if (tool == TOOLMultiSelect)
582                 {
583 //                      QPoint pt = GetAdjustedMousePosition(event);
584                         Vector pt = Painter::QtToCartesianCoords(Vector(event->x(), event->y()));
585                         selection.setBottomRight(QPoint(pt.x, pt.y));
586
587                         for(int i=0; i<pts.GetNumPoints(); i++)
588                         {
589                                 if (selection.contains(QPoint(pts.GetX(i), pts.GetY(i))))
590                                         selectedPoints[i] = true;
591                                 else
592                                         selectedPoints[i] = false;
593                         }
594
595                         update();
596                 }
597         }
598         else if (event->buttons() == Qt::NoButton)
599         {
600                 // Moving, not dragging...
601                 if (tool == TOOLSelect || tool == TOOLDelPt || tool == TOOLAddPt
602                         || tool == TOOLPolySelect || tool == TOOLRotatePoly || tool == TOOLFlipWinding)
603                 {
604 //                      QPoint pt2 = GetAdjustedMousePosition(event);
605                         Vector pt2 = Painter::QtToCartesianCoords(Vector(event->x(), event->y()));
606                         double closest = 1.0e+99;
607
608                         for(int i=0; i<pts.GetNumPoints(); i++)
609                         {
610                                 double dist = ((pt2.x - pts.GetX(i)) * (pt2.x - pts.GetX(i)))
611                                         + ((pt2.y - pts.GetY(i)) * (pt2.y - pts.GetY(i)));
612
613                                 if (dist < closest)
614                                         closest = dist, ptHighlight = i;
615                         }
616
617                         if (ptHighlight != oldPtHighlight)
618                         {
619                                 oldPtHighlight = ptHighlight;
620                                 update();
621                         }
622
623                         // What follows here looks like voodoo, but is really simple. What
624                         // we do is check to see if the mouse point has a perpendicular
625                         // intersection with any of the line segments. If it does,
626                         // calculate the length of the perpendicular and choose the
627                         // smallest length. If there is no perpendicular, then choose the
628                         // length of line connecting the closer of either the first
629                         // endpoint or the second and choose the smallest of those.
630
631                         // There is one bit of math that looks like voodoo to me ATM--will
632                         // explain once I understand it better (the calculation of the
633                         // length of the perpendicular).
634
635                         if (pts.GetNumPoints() > 1 && tool == TOOLAddPt)
636                         {
637                                 double smallest = 1.0e+99;
638
639                                 for(int i=0; i<pts.GetNumPoints(); i++)
640                                 {
641                                         int32_t p1x = pts.GetX(i), p1y = pts.GetY(i),
642                                                 p2x = pts.GetX(pts.GetNext(i)), p2y = pts.GetY(pts.GetNext(i));
643
644                                         Vector ls(p2x, p2y, 0, p1x, p1y, 0),
645                                                 v1(pt2.x, pt2.y, 0, p1x, p1y, 0),
646                                                 v2(pt2.x, pt2.y, 0, p2x, p2y, 0);
647                                         double pp = ls.Dot(v1) / ls.Magnitude(), dist;
648 // Geometric interpretation:
649 // pp is the paremeterized point on the vector ls where the perpendicular
650 // intersects ls. If pp < 0, then the perpendicular lies beyond the 1st
651 // endpoint. If pp > length of ls, then the perpendicular lies beyond the 2nd
652 // endpoint.
653
654                                         if (pp < 0.0)
655                                                 dist = v1.Magnitude();
656                                         else if (pp > ls.Magnitude())
657                                                 dist = v2.Magnitude();
658                                         else                                    // distance = ?Det?(ls, v1) / |ls|
659                                                 dist = fabs((ls.x * v1.y - v1.x * ls.y) / ls.Magnitude());
660
661 //The answer to the above looks like it might be found here:
662 //
663 //If the segment endpoints are s and e, and the point is p, then the test for
664 //the perpendicular intercepting the segment is equivalent to insisting that
665 //the two dot products {s-e}.{s-p} and {e-s}.{e-p} are both non-negative.
666 //Perpendicular distance from the point to the segment is computed by first
667 //computing the area of the triangle the three points form, then dividing by
668 //the length of the segment.  Distances are done just by the Pythagorean
669 //theorem. Twice the area of the triangle formed by three points is the
670 //determinant of the following matrix:
671 //
672 //sx sy 1
673 //ex ey 1
674 //px py 1
675 //
676 //By translating the start point to the origin, this can be rewritten as:
677 //By subtracting row 1 from all rows, you get the following:
678 //[because sx = sy = 0. you could leave out the -sx/y terms below. because we
679 //subtracted row 1 from all rows (including row 1) row 1 turns out to be zero.
680 //duh!]
681 //
682 //0         0         0
683 //(ex - sx) (ey - sy) 0
684 //(px - sx) (py - sy) 0
685 //
686 //which greatly simplifies the calculation of the determinant.
687
688                                         if (dist < smallest)
689                                                 smallest = dist, ptNextHighlight = pts.GetNext(i), ptHighlight = i;
690                                 }
691
692                                 if (ptNextHighlight != oldPtNextHighlight)
693                                 {
694                                         oldPtNextHighlight = ptNextHighlight;
695                                         update();
696                                 }
697                         }
698                 }
699
700                 ptPrevious = Vector(event->x(), event->y());
701                 addPoint = Painter::QtToCartesianCoords(Vector(event->x(), event->y()));
702 //              addPointOnCurve = ((event->modifiers() == Qt::ShiftModifier) || (event->modifiers() == Qt::ControlModifier) ? false : true);
703
704                 if (tool == TOOLAddPoly)
705                         update();
706         }
707
708         event->accept();
709 }
710
711
712 void EditWindow::mouseReleaseEvent(QMouseEvent * event)
713 {
714         if (event->button() == Qt::RightButton)
715         {
716                 ToolType newTool = toolPalette->FindSelectedTool();
717
718                 // We only change the tool if a new one was actually selected. Otherwise, we do nothing.
719                 if (newTool != TOOLNone)
720                 {
721                         tool = newTool;
722
723                         if (tool == TOOLScroll || tool == TOOLZoom || tool == TOOLAddPoly
724                                 || tool == TOOLDelPoly)
725                                 ptHighlight = -1;
726
727                         if (tool == TOOLAddPoly)
728                                 polyFirstPoint = true;
729                 }
730
731                 toolPalette->setVisible(false);
732                 setCursor(cur[tool]);
733                 // Just in case we changed highlighting style with the new tool...
734                 update();
735         }
736         else if (event->button() == Qt::MidButton)
737         {
738                 setCursor(cur[tool]);                                           // Restore previous cursor
739         }
740         else if (event->button() == Qt::LeftButton)
741         {
742                 if (showRotationCenter)
743                 {
744                         showRotationCenter = false;
745                         haveZeroPoint = false;
746
747                         if (tool == TOOLRotate)
748                                 pts.RotatePoints(rotationAngle, IPoint(rotationCenter.x, rotationCenter.y));
749                         else
750                         {
751                                 uint16_t poly = pts.GetPolyForPointNumber(ptHighlight);
752                                 pts.RotatePolyAroundCentroid(poly, rotationAngle);
753                         }
754
755                         update();
756                         ((TTEdit *)qApp)->mainWindow->statusBar()->showMessage("");
757                 }
758
759 //              if (tool == TOOLScroll || tool == TOOLZoom)
760 //                      ReleaseMouse();
761 //this is prolly too much
762                 ((TTEdit *)qApp)->charWnd->MakePathFromPoints(&pts);
763                 ((TTEdit *)qApp)->charWnd->update();
764
765                 if (tool == TOOLMultiSelect)
766                 {
767                         selectionInProgress = false;
768                         update();
769                 }
770         }
771
772         event->accept();
773 }
774
775
776 void EditWindow::keyPressEvent(QKeyEvent * event)
777 {
778         // Sanity checking...
779         if (ptHighlight == -1)
780                 return;
781
782         if (event->key() == Qt::Key_Up)
783         {
784                 pts.SetXY(ptHighlight, pts.GetX(ptHighlight), pts.GetY(ptHighlight) + 1);
785         }
786         else if (event->key() == Qt::Key_Down)
787                 pts.SetXY(ptHighlight, pts.GetX(ptHighlight), pts.GetY(ptHighlight) - 1);
788         else if (event->key() == Qt::Key_Right)
789                 pts.SetXY(ptHighlight, pts.GetX(ptHighlight) + 1, pts.GetY(ptHighlight));
790         else if (event->key() == Qt::Key_Left)
791                 pts.SetXY(ptHighlight, pts.GetX(ptHighlight) - 1, pts.GetY(ptHighlight));
792         else if ((event->key() == Qt::Key_Shift) || (event->key() == Qt::Key_Control))
793         {
794                 addPointOnCurve = false;
795         }
796         else
797                 return;
798
799 //      event->accept();
800         update();
801         ((TTEdit *)qApp)->charWnd->MakePathFromPoints(&pts);
802         ((TTEdit *)qApp)->charWnd->update();
803 }
804
805
806 void EditWindow::keyReleaseEvent(QKeyEvent * event)
807 {
808         if ((event->key() == Qt::Key_Shift) || (event->key() == Qt::Key_Control))
809         {
810                 addPointOnCurve = true;
811         }
812         else
813                 return;
814
815         update();
816 }
817