]> Shamusworld >> Repos - ttedit/blob - src/editwindow.cpp
Add ability to move points with the arrow keys.
[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 "graphicprimitives.h"
40 #include "mainwindow.h"
41 #include "ttedit.h"
42 #include "vector.h"
43
44
45 EditWindow::EditWindow(QWidget * parent/*= NULL*/): QWidget(parent),
46         scale(1.0), offsetX(-10), offsetY(-10), tool(TOOLSelect),
47         ptHighlight(-1), oldPtHighlight(-1), ptNextHighlight(-1), oldPtNextHighlight(-1),
48         polyFirstPoint(true), showRotationCenter(false), haveZeroPoint(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 }
58
59
60 QSize EditWindow::minimumSizeHint() const
61 {
62         return QSize(50, 50);
63 }
64
65
66 QSize EditWindow::sizeHint() const
67 {
68         return QSize(400, 400);
69 }
70
71
72 void EditWindow::CreateCursors(void)
73 {
74         int hotx[11] = {  1,  1, 11, 15,  1,  1,  1,  1,  1,  1,  1 };
75         int hoty[11] = {  1,  1, 11, 13,  1,  1,  1,  1,  1,  1,  1 };
76         char cursorName[11][48] = { "select", "select-poly", "scroll", "zoom", "add-point",
77                 "add-poly", "del-point", "del-poly", "rotate", "rotate", "select" };
78
79         for(int i=0; i<11; i++)
80         {
81                 QString s;
82                 s.sprintf(":/res/cursor-%s.png", cursorName[i]);
83                 QPixmap pmTmp(s);
84                 cur[i] = QCursor(pmTmp, hotx[i], hoty[i]);
85         }
86 }
87
88
89 QPoint EditWindow::GetAdjustedMousePosition(QMouseEvent * event)
90 {
91         QSize winSize = size();
92         // This is undoing the transform, e.g. going from client coords to local coords.
93         // In essence, the height - y is height + (y * -1), the (y * -1) term doing the
94         // conversion of the y-axis from increasing bottom to top.
95         return QPoint(offsetX + event->x(), offsetY + (winSize.height() - event->y()));
96 }
97
98
99 QPoint EditWindow::GetAdjustedClientPosition(int x, int y)
100 {
101         QSize winSize = size();
102
103         // VOODOO ALERT (ON Y COMPONENT!!!!)
104         return QPoint(-offsetX + x, (winSize.height() - (-offsetY + y)) * +1.0);
105 }
106
107
108 /*
109 TODO:
110  o  Different colors for polys on selected points
111  o  Different colors for handles on non-selected polys
112  o  Line of sight (dashed, dotted) for off-curve points
113  o  Repaints for press/release of CTRL/SHIFT during point creation
114 */
115 void EditWindow::paintEvent(QPaintEvent * /*event*/)
116 {
117         QPainter p(this);
118 //hm, causes lockup (or does it???)
119         p.setRenderHint(QPainter::Antialiasing);
120
121         QSize winSize = size();
122
123         p.translate(QPoint(-offsetX, winSize.height() - (-offsetY)));
124         p.scale(1.0, -1.0);
125
126 // Scrolling can be done by using OffsetViewportOrgEx
127 // Scaling can be done by adjusting SetWindowExtEx (it's denominator of txform)
128 // you'd use: % = ViewportExt / WindowExt
129 // But it makes the window look like crap: fuggetuboutit.
130 // Instead, we have to scale EVERYTHING by hand. Crap!
131 // It's not *that* bad, but not as convenient either...
132
133         p.setPen(QPen(Qt::blue, 1.0, Qt::DotLine));
134
135         // Draw coordinate axes
136
137         p.drawLine(0, -16384, 0, 16384);
138         p.drawLine(-16384, 0, 16384, 0);
139
140         // Draw rotation center (if active)
141
142         if (showRotationCenter)
143         {
144                 p.setPen(QPen(Qt::red, 2.0, Qt::SolidLine));
145                 p.drawLine(rotationCenter.x() + 7, rotationCenter.y(), rotationCenter.x() - 7, rotationCenter.y());
146                 p.drawLine(rotationCenter.x(), rotationCenter.y() + 7, rotationCenter.x(), rotationCenter.y() - 7);
147         }
148
149         // Draw points
150
151         for(int i=0; i<pts.GetNumPoints(); i++)
152         {
153                 if (i == ptHighlight)
154                 {
155                         p.setPen(QPen(Qt::red, 1.0, Qt::SolidLine));
156
157                         if (pts.GetOnCurve(i))
158                         {
159                                 DrawSquareDotN(p, pts.GetX(i), pts.GetY(i), 7);
160                                 DrawSquareDotN(p, pts.GetX(i), pts.GetY(i), 9);
161                         }
162                         else
163                         {
164                                 DrawRoundDotN(p, pts.GetX(i), pts.GetY(i), 7);
165                                 DrawRoundDotN(p, pts.GetX(i), pts.GetY(i), 9);
166                         }
167                 }
168                 else if ((i == ptHighlight || i == ptNextHighlight) && tool == TOOLAddPt)
169                 {
170                         p.setPen(QPen(Qt::green, 1.0, Qt::SolidLine));
171
172                         if (pts.GetOnCurve(i))
173                         {
174                                 DrawSquareDotN(p, pts.GetX(i), pts.GetY(i), 7);
175                                 DrawSquareDotN(p, pts.GetX(i), pts.GetY(i), 9);
176                         }
177                         else
178                         {
179                                 DrawRoundDotN(p, pts.GetX(i), pts.GetY(i), 7);
180                                 DrawRoundDotN(p, pts.GetX(i), pts.GetY(i), 9);
181                         }
182                 }
183                 else
184                 {
185                         p.setPen(QPen(Qt::black, 1.0, Qt::SolidLine));
186
187                         if (pts.GetOnCurve(i))
188                                 DrawSquareDot(p, pts.GetX(i), pts.GetY(i));
189                         else
190                                 DrawRoundDot(p, pts.GetX(i), pts.GetY(i));
191                 }
192
193                 if (tool == TOOLDelPt && i == ptHighlight)
194                 {
195                         p.setPen(QPen(Qt::red, 1.0, Qt::SolidLine));
196                         p.drawLine(pts.GetX(i) - 5, pts.GetY(i) - 5, pts.GetX(i) + 5, pts.GetY(i) + 5);
197                         p.drawLine(pts.GetX(i) + 5, pts.GetY(i) - 5, pts.GetX(i) - 5, pts.GetY(i) + 5);
198                 }
199         }
200
201         // Draw curve formed by points
202
203         p.setPen(QPen(Qt::black, 1.0, Qt::SolidLine));
204         DrawGlyph(p, pts);
205
206         if (haveZeroPoint)
207         {
208                 // Rotation code
209                 GlyphPoints rotated = pts;
210
211                 if (tool == TOOLRotate)
212                         rotated.RotatePoints(rotationAngle, IPoint(rotationCenter.x(), rotationCenter.y()));
213                 else if (tool == TOOLRotatePoly)
214                 {
215                         uint16_t poly = rotated.GetPolyForPointNumber(ptHighlight);
216                         rotated.RotatePolyAroundCentroid(poly, rotationAngle);
217                 }
218
219                 p.setPen(QPen(QColor(255, 0, 255), 1.0, Qt::SolidLine));
220                 DrawGlyph(p, rotated);
221         }
222 }
223
224
225 void EditWindow::DrawGlyph(QPainter & p, GlyphPoints & glyph)
226 {
227         for(int poly=0; poly<glyph.GetNumPolys(); poly++)
228         {
229 #if 0
230                 if (glyph.GetNumPoints(poly) < 3)
231                         continue;
232
233                 // Initial move: If our start point is on curve, then go to it. Otherwise,
234                 // check previous point. If it's on curve, go to it otherwise go the
235                 // midpoint between start point and previous (since it's between two curve
236                 // control points).
237                 IPoint pt = (glyph.GetOnCurve(poly, 0)
238                         ? glyph.GetPoint(poly, 0) : (glyph.GetPrevOnCurve(poly, 0)
239                                 ? glyph.GetPrevPoint(poly, 0) : glyph.GetMidpointToPrev(poly, 0)));
240
241 // Need to add separate color handling here for polys that are being manipulated...
242
243                 for(int i=0; i<glyph.GetNumPoints(poly); i++)
244                 {
245                         // If this point and then next are both on curve, we have a line...
246                         if (glyph.GetOnCurve(poly, i) && glyph.GetNextOnCurve(poly, i))
247                         {
248                                 IPoint pt2 = glyph.GetNextPoint(poly, i);
249                                 p.drawLine(pt.x, pt.y, pt2.x, pt2.y);
250                                 pt = pt2;
251                         }
252                         else
253                         {
254                                 // Skip point if it's on curve (start of curve--it's already
255                                 // been plotted so we don't need to handle it...)
256                                 if (glyph.GetOnCurve(poly, i))
257                                         continue;
258
259                                 // We are now guaranteed that we are sitting on a curve control point
260                                 // (off curve). Figure the extent of the curve: If the following is a
261                                 // curve control point, then use the midpoint to it otherwise go to
262                                 // the next point since it's on curve.
263                                 IPoint pt2 = (glyph.GetNextOnCurve(poly, i)
264                                         ? glyph.GetNextPoint(poly, i) : glyph.GetMidpointToNext(poly, i));
265
266                                 Bezier(p, pt, glyph.GetPoint(poly, i), pt2);
267                                 pt = pt2;
268                         }
269                 }
270 #else
271                 DrawGlyphPoly(p, glyph, poly);
272 #endif
273         }
274 }
275
276
277 void EditWindow::DrawGlyphPoly(QPainter & p, GlyphPoints & glyph, uint16_t poly)
278 {
279         // Sanity check
280         if (glyph.GetNumPoints(poly) < 3)
281                 return;
282
283         // Initial move: If our start point is on curve, then go to it. Otherwise,
284         // check previous point. If it's on curve, go to it otherwise go the
285         // midpoint between start point and previous (since it's between two curve
286         // control points).
287         IPoint pt = (glyph.GetOnCurve(poly, 0)
288                 ? glyph.GetPoint(poly, 0) : (glyph.GetPrevOnCurve(poly, 0)
289                         ? glyph.GetPrevPoint(poly, 0) : glyph.GetMidpointToPrev(poly, 0)));
290
291         for(int i=0; i<glyph.GetNumPoints(poly); i++)
292         {
293                 // If this point and then next are both on curve, we have a line...
294                 if (glyph.GetOnCurve(poly, i) && glyph.GetNextOnCurve(poly, i))
295                 {
296                         IPoint pt2 = glyph.GetNextPoint(poly, i);
297                         p.drawLine(pt.x, pt.y, pt2.x, pt2.y);
298                         pt = pt2;
299                 }
300                 else
301                 {
302                         // Skip point if it's on curve (start of curve--it's already
303                         // been plotted so we don't need to handle it...)
304                         if (glyph.GetOnCurve(poly, i))
305                                 continue;
306
307                         // We are now guaranteed that we are sitting on a curve control point
308                         // (off curve). Figure the extent of the curve: If the following is a
309                         // curve control point, then use the midpoint to it otherwise go to
310                         // the next point since it's on curve.
311                         IPoint pt2 = (glyph.GetNextOnCurve(poly, i)
312                                 ? glyph.GetNextPoint(poly, i) : glyph.GetMidpointToNext(poly, i));
313
314                         Bezier(p, pt, glyph.GetPoint(poly, i), pt2);
315                         pt = pt2;
316                 }
317         }
318 }
319
320
321 void EditWindow::mousePressEvent(QMouseEvent * event)
322 {
323         if (event->button() == Qt::RightButton)
324         {
325                 toolPalette->move(event->globalPos());
326                 toolPalette->setVisible(true);
327                 setCursor(cur[TOOLSelect]);
328                 toolPalette->prevTool = TOOLSelect;
329         }
330         else if (event->button() == Qt::MidButton)
331         {
332                 setCursor(cur[2]);                                                      // Scrolling cursor
333         }
334         else if (event->button() == Qt::LeftButton)
335         {
336                 if (tool == TOOLScroll || tool == TOOLZoom)
337 ;//meh                  CaptureMouse();                                         // Make sure we capture the mouse when in scroll/zoom mode
338                 else if (tool == TOOLAddPt)             // "Add Point" tool
339                 {
340                         QPoint pt = GetAdjustedMousePosition(event);
341                         IPoint pointToAdd(pt.x(), pt.y(), ((event->modifiers() == Qt::ShiftModifier || event->modifiers() == Qt::ControlModifier) ? false : true));
342
343                         if (pts.GetNumPoints() < 2)
344                         {
345 //                              pts += IPoint(pt.x(), pt.y(), ((event->modifiers() == Qt::ShiftModifier || event->modifiers() == Qt::ControlModifier) ? false : true));
346                                 pts += pointToAdd;
347                                 ptHighlight = pts.GetNumPoints() - 1;
348                         }
349                         else
350                         {
351 //                              QPoint pt = GetAdjustedMousePosition(event);
352 //                              pts.InsertPoint(pts.GetNext(ptHighlight), pt.x(), pt.y(), ((event->modifiers() == Qt::ShiftModifier || event->modifiers() == Qt::ControlModifier) ? false : true));
353                                 pts.InsertPoint(pts.GetNext(ptHighlight), pointToAdd);
354                                 ptHighlight = ptNextHighlight;
355 //                              update();
356                         }
357
358                         update();
359                 }
360                 else if (tool == TOOLAddPoly)   // "Add Poly" tool
361                 {
362 #ifdef DEBUGFOO
363 WriteLogMsg("Adding point... # polys: %u, # points: %u", pts.GetNumPolys(), pts.GetNumPoints());
364 #endif
365                         if (polyFirstPoint)
366                         {
367                                 polyFirstPoint = false;
368                                 pts.AddNewPolyAtEnd();
369                         }
370
371                         QPoint pt = GetAdjustedMousePosition(event);
372 //printf("GetAdjustedMousePosition = %i, %i\n", pt.x(), pt.y());
373                         // Append a point to the end of the structure
374                         pts += IPoint(pt.x(), pt.y(), ((event->modifiers() == Qt::ShiftModifier || event->modifiers() == Qt::ControlModifier) ? false : true));
375                         ptHighlight = pts.GetNumPoints() - 1;
376                         update();
377 #ifdef DEBUGFOO
378 WriteLogMsg(" --> [# polys: %u, # points: %u]\n", pts.GetNumPolys(), pts.GetNumPoints());
379 #endif
380                 }
381                 else if (tool == TOOLSelect || tool == TOOLPolySelect)
382                 {
383                         if (pts.GetNumPoints() > 0)
384                         {
385                                 pt = GetAdjustedClientPosition(pts.GetX(ptHighlight), pts.GetY(ptHighlight));
386 //printf("GetAdjustedClientPosition = %i, %i\n", pt.x(), pt.y());
387 //                              WarpPointer(pt.x, pt.y);
388                                 QCursor::setPos(mapToGlobal(pt));
389
390                                 if (event->modifiers() == Qt::ShiftModifier || event->modifiers() == Qt::ControlModifier)
391                                 {
392                                         pts.SetOnCurve(ptHighlight, !pts.GetOnCurve(ptHighlight));
393                                         update();
394                                 }
395                         }
396                 }
397                 else if (tool == TOOLDelPt)
398                 {
399                         if (pts.GetNumPoints() > 0)
400 //Or could use:
401 //                      if (ptHighlight != -1)
402                         {
403 //This assumes that WM_MOUSEMOVE happens before this!
404 //The above commented out line should take care of this contingency... !!! FIX !!!
405                                 pts.DeletePoint(ptHighlight);
406                                 update();
407                         }
408                 }
409                 else if (tool == TOOLRotate)
410                 {
411                         // I think what's needed here is to keep the initial mouse click,
412                         // paint the rotation center, then use the 1st mouse move event to establish
413                         // the rotation "zero line", which becomes the line of reference to all
414                         // subsequent mouse moves.
415                         rotationCenter = GetAdjustedMousePosition(event);
416                         showRotationCenter = true;
417                         haveZeroPoint = false;
418                         rotationAngle = 0;
419                         update();
420                 }
421                 else if (tool == TOOLRotatePoly)
422                 {
423                         IPoint centroid = pts.GetPolyCentroid(pts.GetPolyForPointNumber(ptHighlight));
424                         rotationCenter = QPoint(centroid.x, centroid.y);
425                         showRotationCenter = true;
426                         pt = GetAdjustedClientPosition(pts.GetX(ptHighlight), pts.GetY(ptHighlight));
427                         QCursor::setPos(mapToGlobal(pt));
428                         rotationZeroPoint = QPoint(pts.GetX(ptHighlight), pts.GetY(ptHighlight));
429                         haveZeroPoint = true;
430                         rotationAngle = 0;
431                         update();
432                 }
433                 else if (tool == TOOLFlipWinding)
434                 {
435                         pts.InvertPolyDrawSequence(pts.GetPolyForPointNumber(ptHighlight));
436                         pt = GetAdjustedClientPosition(pts.GetX(ptHighlight), pts.GetY(ptHighlight));
437                         QCursor::setPos(mapToGlobal(pt));
438                         update();
439                 }
440         }
441
442         event->accept();
443 }
444
445
446 void EditWindow::mouseMoveEvent(QMouseEvent * event)
447 {
448         if (event->buttons() == Qt::RightButton)
449         {
450                 ToolType newTool = toolPalette->FindSelectedTool();
451
452                 if (newTool != toolPalette->prevTool)
453                 {
454                         toolPalette->prevTool = newTool;
455                         toolPalette->repaint();
456                 }
457         }
458         else if (event->buttons() == Qt::MidButton)
459         {
460                 // Calc offset from previous point
461                 pt = event->pos();
462                 ptOffset = QPoint(pt.x() - ptPrevious.x(), pt.y() - ptPrevious.y());
463
464 // Then multiply it by the scaling factor. Whee!
465                 // This looks wacky because we're using screen coords for the offset...
466                 // Otherwise, we would subtract both offsets!
467                 offsetX -= ptOffset.x(), offsetY += ptOffset.y();
468                 update();
469                 ptPrevious = pt;
470         }
471         else if (event->buttons() == Qt::LeftButton)
472         {
473                 if (tool == TOOLAddPt || tool == TOOLAddPoly || tool == TOOLSelect)
474                 {
475                         // Bail out if we have the select tool and no points yet...
476                         if (tool == TOOLSelect && pts.GetNumPoints() == 0)
477                                 return;
478
479                         QPoint pt2 = GetAdjustedMousePosition(event);
480                         pts.SetXY(ptHighlight, pt2.x(), pt2.y());
481                         update();
482                 }
483                 else if (tool == TOOLPolySelect)
484                 {
485                         if (pts.GetNumPoints() > 0)
486                         {
487                                 QPoint pt2 = GetAdjustedMousePosition(event);
488                                 // Should also set onCurve here as well, depending on keystate
489 //Or should we?
490 //Would be nice, but we'd need to trap the keyPressEvent() as well, otherwise pressing/releasing
491 //the hotkey would show no change until the user moved their mouse.
492                                 pts.OffsetPoly(pts.GetPoly(ptHighlight), pt2.x() - pts.GetX(ptHighlight), pt2.y() - pts.GetY(ptHighlight));
493                                 update();
494                         }
495                 }
496                 else if (tool == TOOLRotate || tool == TOOLRotatePoly)
497                 {
498                         if (pts.GetNumPoints() > 0)
499                         {
500                                 if (!haveZeroPoint)
501                                 {
502                                         rotationZeroPoint = GetAdjustedMousePosition(event);
503                                         haveZeroPoint = true;
504                                 }
505                                 else
506                                 {
507                                         // Figure out the angle between the "zero" vector and the current one,
508                                         // then rotate all points relative to the "zero" vector (done by paint())
509                                         QPoint currentPoint = GetAdjustedMousePosition(event);
510                                         Vector v1(rotationZeroPoint.x(), rotationZeroPoint.y(), 0,
511                                                 rotationCenter.x(), rotationCenter.y(), 0);
512                                         Vector v2(currentPoint.x(), currentPoint.y(), 0,
513                                                 rotationCenter.x(), rotationCenter.y(), 0);
514 //                                      rotationAngle = v1.Angle(v2);
515                                         rotationAngle = v2.Angle(v1);
516
517                                         QString s;
518                                         s.sprintf("%.3f degrees", rotationAngle * 180.0 / 3.14159265358979323);
519                                         ((TTEdit *)qApp)->mainWindow->statusBar()->showMessage(s);
520                                 }
521
522                                 update();
523                         }
524                 }
525         }
526         else if (event->buttons() == Qt::NoButton)
527         {
528                 // Moving, not dragging...
529                 if (tool == TOOLSelect || tool == TOOLDelPt || tool == TOOLAddPt
530                         || tool == TOOLPolySelect || tool == TOOLRotatePoly || tool == TOOLFlipWinding)
531                 {
532                         QPoint pt2 = GetAdjustedMousePosition(event);
533                         double closest = 1.0e+99;
534
535                         for(int i=0; i<pts.GetNumPoints(); i++)
536                         {
537                                 double dist = ((pt2.x() - pts.GetX(i)) * (pt2.x() - pts.GetX(i)))
538                                         + ((pt2.y() - pts.GetY(i)) * (pt2.y() - pts.GetY(i)));
539
540                                 if (dist < closest)
541                                         closest = dist, ptHighlight = i;
542                         }
543
544                         if (ptHighlight != oldPtHighlight)
545                         {
546                                 oldPtHighlight = ptHighlight;
547                                 update();
548                         }
549
550                         // What follows here looks like voodoo, but is really simple. What we do is
551                         // check to see if the mouse point has a perpendicular intersection with any of
552                         // the line segments. If it does, calculate the length of the perpendicular
553                         // and choose the smallest length. If there is no perpendicular, then choose the
554                         // length of line connecting the closer of either the first endpoint or the
555                         // second and choose the smallest of those.
556
557                         // There is one bit of math that looks like voodoo to me ATM--will explain once
558                         // I understand it better (the calculation of the length of the perpendicular).
559
560                         if (pts.GetNumPoints() > 1 && tool == TOOLAddPt)
561                         {
562                                 double smallest = 1.0e+99;
563
564                                 for(int i=0; i<pts.GetNumPoints(); i++)
565                                 {
566                                         int32_t p1x = pts.GetX(i), p1y = pts.GetY(i),
567                                                 p2x = pts.GetX(pts.GetNext(i)), p2y = pts.GetY(pts.GetNext(i));
568
569                                         Vector ls(p2x, p2y, 0, p1x, p1y, 0), v1(pt2.x(), pt2.y(), 0, p1x, p1y, 0),
570                                                 v2(pt2.x(), pt2.y(), 0, p2x, p2y, 0);
571                                         double pp = ls.Dot(v1) / ls.Magnitude(), dist;
572 // Geometric interpretation:
573 // pp is the paremeterized point on the vector ls where the perpendicular intersects ls.
574 // If pp < 0, then the perpendicular lies beyond the 1st endpoint. If pp > length of ls,
575 // then the perpendicular lies beyond the 2nd endpoint.
576
577                                         if (pp < 0.0)
578                                                 dist = v1.Magnitude();
579                                         else if (pp > ls.Magnitude())
580                                                 dist = v2.Magnitude();
581                                         else                                    // distance = ?Det?(ls, v1) / |ls|
582                                                 dist = fabs((ls.x * v1.y - v1.x * ls.y) / ls.Magnitude());
583
584 //The answer to the above looks like it might be found here:
585 //
586 //If the segment endpoints are s and e, and the point is p, then the test for the perpendicular
587 //intercepting the segment is equivalent to insisting that the two dot products {s-e}.{s-p} and
588 //{e-s}.{e-p} are both non-negative.  Perpendicular distance from the point to the segment is
589 //computed by first computing the area of the triangle the three points form, then dividing by the
590 //length of the segment.  Distances are done just by the Pythagorean theorem.  Twice the area of the
591 //triangle formed by three points is the determinant of the following matrix:
592 //
593 //sx sy 1
594 //ex ey 1
595 //px py 1
596 //
597 //By translating the start point to the origin, this can be rewritten as:
598 //By subtracting row 1 from all rows, you get the following:
599 //[because sx = sy = 0. you could leave out the -sx/y terms below. because we subtracted
600 // row 1 from all rows (including row 1) row 1 turns out to be zero. duh!]
601 //
602 //0         0         0
603 //(ex - sx) (ey - sy) 0
604 //(px - sx) (py - sy) 0
605 //
606 //which greatly simplifies the calculation of the determinant.
607
608                                         if (dist < smallest)
609                                                 smallest = dist, ptNextHighlight = pts.GetNext(i), ptHighlight = i;
610                                 }
611
612                                 if (ptNextHighlight != oldPtNextHighlight)
613                                 {
614                                         oldPtNextHighlight = ptNextHighlight;
615                                         update();
616                                 }
617                         }
618                 }
619
620                 ptPrevious = event->pos();
621         }
622
623         event->accept();
624 }
625
626
627 void EditWindow::mouseReleaseEvent(QMouseEvent * event)
628 {
629         if (event->button() == Qt::RightButton)
630         {
631                 ToolType newTool = toolPalette->FindSelectedTool();
632
633                 // We only change the tool if a new one was actually selected. Otherwise, we do nothing.
634                 if (newTool != TOOLNone)
635                 {
636                         tool = newTool;
637
638                         if (tool == TOOLScroll || tool == TOOLZoom || tool == TOOLAddPoly
639                                 || tool == TOOLDelPoly)
640                                 ptHighlight = -1;
641
642                         if (tool == TOOLAddPoly)
643                                 polyFirstPoint = true;
644                 }
645
646                 toolPalette->setVisible(false);
647                 setCursor(cur[tool]);
648                 // Just in case we changed highlighting style with the new tool...
649                 update();
650         }
651         else if (event->button() == Qt::MidButton)
652         {
653                 setCursor(cur[tool]);                                           // Restore previous cursor
654         }
655         else if (event->button() == Qt::LeftButton)
656         {
657                 if (showRotationCenter)
658                 {
659                         showRotationCenter = false;
660                         haveZeroPoint = false;
661
662                         if (tool == TOOLRotate)
663                                 pts.RotatePoints(rotationAngle, IPoint(rotationCenter.x(), rotationCenter.y()));
664                         else
665                         {
666                                 uint16_t poly = pts.GetPolyForPointNumber(ptHighlight);
667                                 pts.RotatePolyAroundCentroid(poly, rotationAngle);
668                         }
669
670                         update();
671                         ((TTEdit *)qApp)->mainWindow->statusBar()->showMessage("");
672                 }
673
674 //              if (tool == TOOLScroll || tool == TOOLZoom)
675 //                      ReleaseMouse();
676 //this is prolly too much
677                 ((TTEdit *)qApp)->charWnd->MakePathFromPoints(&pts);
678                 ((TTEdit *)qApp)->charWnd->update();
679
680         }
681
682         event->accept();
683 }
684
685
686 void EditWindow::keyPressEvent(QKeyEvent * event)
687 {
688         // Sanity checking...
689         if (ptHighlight == -1)
690                 return;
691
692         if (event->key() == Qt::Key_Up)
693         {
694                 pts.SetXY(ptHighlight, pts.GetX(ptHighlight), pts.GetY(ptHighlight) + 1);
695         }
696         else if (event->key() == Qt::Key_Down)
697                 pts.SetXY(ptHighlight, pts.GetX(ptHighlight), pts.GetY(ptHighlight) - 1);
698         else if (event->key() == Qt::Key_Right)
699                 pts.SetXY(ptHighlight, pts.GetX(ptHighlight) + 1, pts.GetY(ptHighlight));
700         else if (event->key() == Qt::Key_Left)
701                 pts.SetXY(ptHighlight, pts.GetX(ptHighlight) - 1, pts.GetY(ptHighlight));
702         else
703                 return;
704
705         event->accept();
706         update();
707         ((TTEdit *)qApp)->charWnd->MakePathFromPoints(&pts);
708         ((TTEdit *)qApp)->charWnd->update();
709 }
710
711
712 void EditWindow::keyReleaseEvent(QKeyEvent * /*event*/)
713 {
714 }
715