]> Shamusworld >> Repos - ttedit/blob - src/editwindow.cpp
ef3d585b2ddd4fd333c2c74759c3a43e3ccf9376
[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 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 (glyph.GetNumPoints(poly) < 3)
230                         continue;
231
232                 // Initial move: If our start point is on curve, then go to it. Otherwise,
233                 // check previous point. If it's on curve, go to it otherwise go the
234                 // midpoint between start point and previous (since it's between two curve
235                 // control points).
236                 IPoint pt = (glyph.GetOnCurve(poly, 0)
237                         ? glyph.GetPoint(poly, 0) : (glyph.GetPrevOnCurve(poly, 0)
238                                 ? glyph.GetPrevPoint(poly, 0) : glyph.GetMidpointToPrev(poly, 0)));
239
240 // Need to add separate color handling here for polys that are being manipulated...
241
242                 for(int i=0; i<glyph.GetNumPoints(poly); i++)
243                 {
244                         // If this point and then next are both on curve, we have a line...
245                         if (glyph.GetOnCurve(poly, i) && glyph.GetNextOnCurve(poly, i))
246                         {
247                                 IPoint pt2 = glyph.GetNextPoint(poly, i);
248                                 p.drawLine(pt.x, pt.y, pt2.x, pt2.y);
249                                 pt = pt2;
250                         }
251                         else
252                         {
253                                 // Skip point if it's on curve (start of curve--it's already
254                                 // been plotted so we don't need to handle it...)
255                                 if (glyph.GetOnCurve(poly, i))
256                                         continue;
257
258                                 // We are now guaranteed that we are sitting on a curve control point
259                                 // (off curve). Figure the extent of the curve: If the following is a
260                                 // curve control point, then use the midpoint to it otherwise go to
261                                 // the next point since it's on curve.
262                                 IPoint pt2 = (glyph.GetNextOnCurve(poly, i)
263                                         ? glyph.GetNextPoint(poly, i) : glyph.GetMidpointToNext(poly, i));
264
265                                 Bezier(p, pt, glyph.GetPoint(poly, i), pt2);
266                                 pt = pt2;
267                         }
268                 }
269         }
270 }
271
272
273 void EditWindow::DrawGlyphPoly(QPainter & p, GlyphPoints & glyph, uint16 poly)
274 {
275         // Sanity check
276         if (glyph.GetNumPoints(poly) < 3)
277                 return;
278
279         // Initial move: If our start point is on curve, then go to it. Otherwise,
280         // check previous point. If it's on curve, go to it otherwise go the
281         // midpoint between start point and previous (since it's between two curve
282         // control points).
283         IPoint pt = (glyph.GetOnCurve(poly, 0)
284                 ? glyph.GetPoint(poly, 0) : (glyph.GetPrevOnCurve(poly, 0)
285                         ? glyph.GetPrevPoint(poly, 0) : glyph.GetMidpointToPrev(poly, 0)));
286
287         for(int i=0; i<glyph.GetNumPoints(poly); i++)
288         {
289                 // If this point and then next are both on curve, we have a line...
290                 if (glyph.GetOnCurve(poly, i) && glyph.GetNextOnCurve(poly, i))
291                 {
292                         IPoint pt2 = glyph.GetNextPoint(poly, i);
293                         p.drawLine(pt.x, pt.y, pt2.x, pt2.y);
294                         pt = pt2;
295                 }
296                 else
297                 {
298                         // Skip point if it's on curve (start of curve--it's already
299                         // been plotted so we don't need to handle it...)
300                         if (glyph.GetOnCurve(poly, i))
301                                 continue;
302
303                         // We are now guaranteed that we are sitting on a curve control point
304                         // (off curve). Figure the extent of the curve: If the following is a
305                         // curve control point, then use the midpoint to it otherwise go to
306                         // the next point since it's on curve.
307                         IPoint pt2 = (glyph.GetNextOnCurve(poly, i)
308                                 ? glyph.GetNextPoint(poly, i) : glyph.GetMidpointToNext(poly, i));
309
310                         Bezier(p, pt, glyph.GetPoint(poly, i), pt2);
311                         pt = pt2;
312                 }
313         }
314 }
315
316
317 void EditWindow::mousePressEvent(QMouseEvent * event)
318 {
319         if (event->button() == Qt::RightButton)
320         {
321                 toolPalette->move(event->globalPos());
322                 toolPalette->setVisible(true);
323                 setCursor(cur[TOOLSelect]);
324                 toolPalette->prevTool = TOOLSelect;
325         }
326         else if (event->button() == Qt::MidButton)
327         {
328                 setCursor(cur[2]);                                                      // Scrolling cursor
329         }
330         else if (event->button() == Qt::LeftButton)
331         {
332                 if (tool == TOOLScroll || tool == TOOLZoom)
333 ;//meh                  CaptureMouse();                                         // Make sure we capture the mouse when in scroll/zoom mode
334                 else if (tool == TOOLAddPt)             // "Add Point" tool
335                 {
336                         QPoint pt = GetAdjustedMousePosition(event);
337                         IPoint pointToAdd(pt.x(), pt.y(), ((event->modifiers() == Qt::ShiftModifier || event->modifiers() == Qt::ControlModifier) ? false : true));
338
339                         if (pts.GetNumPoints() < 2)
340                         {
341 //                              pts += IPoint(pt.x(), pt.y(), ((event->modifiers() == Qt::ShiftModifier || event->modifiers() == Qt::ControlModifier) ? false : true));
342                                 pts += pointToAdd;
343                                 ptHighlight = pts.GetNumPoints() - 1;
344                         }
345                         else
346                         {
347 //                              QPoint pt = GetAdjustedMousePosition(event);
348 //                              pts.InsertPoint(pts.GetNext(ptHighlight), pt.x(), pt.y(), ((event->modifiers() == Qt::ShiftModifier || event->modifiers() == Qt::ControlModifier) ? false : true));
349                                 pts.InsertPoint(pts.GetNext(ptHighlight), pointToAdd);
350                                 ptHighlight = ptNextHighlight;
351 //                              update();
352                         }
353
354                         update();
355                 }
356                 else if (tool == TOOLAddPoly)   // "Add Poly" tool
357                 {
358 #ifdef DEBUGFOO
359 WriteLogMsg("Adding point... # polys: %u, # points: %u", pts.GetNumPolys(), pts.GetNumPoints());
360 #endif
361                         if (polyFirstPoint)
362                         {
363                                 polyFirstPoint = false;
364                                 pts.AddNewPolyAtEnd();
365                         }
366
367                         QPoint pt = GetAdjustedMousePosition(event);
368 //printf("GetAdjustedMousePosition = %i, %i\n", pt.x(), pt.y());
369                         // Append a point to the end of the structure
370                         pts += IPoint(pt.x(), pt.y(), ((event->modifiers() == Qt::ShiftModifier || event->modifiers() == Qt::ControlModifier) ? false : true));
371                         ptHighlight = pts.GetNumPoints() - 1;
372                         update();
373 #ifdef DEBUGFOO
374 WriteLogMsg(" --> [# polys: %u, # points: %u]\n", pts.GetNumPolys(), pts.GetNumPoints());
375 #endif
376                 }
377                 else if (tool == TOOLSelect || tool == TOOLPolySelect)
378                 {
379                         if (pts.GetNumPoints() > 0)
380                         {
381                                 pt = GetAdjustedClientPosition(pts.GetX(ptHighlight), pts.GetY(ptHighlight));
382 //printf("GetAdjustedClientPosition = %i, %i\n", pt.x(), pt.y());
383 //                              WarpPointer(pt.x, pt.y);
384                                 QCursor::setPos(mapToGlobal(pt));
385
386                                 if (event->modifiers() == Qt::ShiftModifier || event->modifiers() == Qt::ControlModifier)
387                                 {
388                                         pts.SetOnCurve(ptHighlight, !pts.GetOnCurve(ptHighlight));
389                                         update();
390                                 }
391                         }
392                 }
393                 else if (tool == TOOLDelPt)
394                 {
395                         if (pts.GetNumPoints() > 0)
396 //Or could use:
397 //                      if (ptHighlight != -1)
398                         {
399 //This assumes that WM_MOUSEMOVE happens before this!
400 //The above commented out line should take care of this contingency... !!! FIX !!!
401                                 pts.DeletePoint(ptHighlight);
402                                 update();
403                         }
404                 }
405                 else if (tool == TOOLRotate)
406                 {
407                         // I think what's needed here is to keep the initial mouse click,
408                         // paint the rotation center, then use the 1st mouse move event to establish
409                         // the rotation "zero line", which becomes the line of reference to all
410                         // subsequent mouse moves.
411                         rotationCenter = GetAdjustedMousePosition(event);
412                         showRotationCenter = true;
413                         haveZeroPoint = false;
414                         rotationAngle = 0;
415                         update();
416                 }
417                 else if (tool == TOOLRotatePoly)
418                 {
419                         IPoint centroid = pts.GetPolyCentroid(pts.GetPolyForPointNumber(ptHighlight));
420                         rotationCenter = QPoint(centroid.x, centroid.y);
421                         showRotationCenter = true;
422                         pt = GetAdjustedClientPosition(pts.GetX(ptHighlight), pts.GetY(ptHighlight));
423                         QCursor::setPos(mapToGlobal(pt));
424                         rotationZeroPoint = QPoint(pts.GetX(ptHighlight), pts.GetY(ptHighlight));
425                         haveZeroPoint = true;
426                         rotationAngle = 0;
427                         update();
428                 }
429                 else if (tool == TOOLFlipWinding)
430                 {
431                         pts.InvertPolyDrawSequence(pts.GetPolyForPointNumber(ptHighlight));
432                         pt = GetAdjustedClientPosition(pts.GetX(ptHighlight), pts.GetY(ptHighlight));
433                         QCursor::setPos(mapToGlobal(pt));
434                         update();
435                 }
436         }
437
438         event->accept();
439 }
440
441
442 void EditWindow::mouseMoveEvent(QMouseEvent * event)
443 {
444         if (event->buttons() == Qt::RightButton)
445         {
446                 ToolType newTool = toolPalette->FindSelectedTool();
447
448                 if (newTool != toolPalette->prevTool)
449                 {
450                         toolPalette->prevTool = newTool;
451                         toolPalette->repaint();
452                 }
453         }
454         else if (event->buttons() == Qt::MidButton)
455         {
456                 // Calc offset from previous point
457                 pt = event->pos();
458                 ptOffset = QPoint(pt.x() - ptPrevious.x(), pt.y() - ptPrevious.y());
459
460 // Then multiply it by the scaling factor. Whee!
461                 // This looks wacky because we're using screen coords for the offset...
462                 // Otherwise, we would subtract both offsets!
463                 offsetX -= ptOffset.x(), offsetY += ptOffset.y();
464                 update();
465                 ptPrevious = pt;
466         }
467         else if (event->buttons() == Qt::LeftButton)
468         {
469                 if (tool == TOOLAddPt || tool == TOOLAddPoly || tool == TOOLSelect)
470                 {
471                         // Bail out if we have the select tool and no points yet...
472                         if (tool == TOOLSelect && pts.GetNumPoints() == 0)
473                                 return;
474
475                         QPoint pt2 = GetAdjustedMousePosition(event);
476                         pts.SetXY(ptHighlight, pt2.x(), pt2.y());
477                         update();
478                 }
479                 else if (tool == TOOLPolySelect)
480                 {
481                         if (pts.GetNumPoints() > 0)
482                         {
483                                 QPoint pt2 = GetAdjustedMousePosition(event);
484                                 // Should also set onCurve here as well, depending on keystate
485 //Or should we?
486 //Would be nice, but we'd need to trap the keyPressEvent() as well, otherwise pressing/releasing
487 //the hotkey would show no change until the user moved their mouse.
488                                 pts.OffsetPoly(pts.GetPoly(ptHighlight), pt2.x() - pts.GetX(ptHighlight), pt2.y() - pts.GetY(ptHighlight));
489                                 update();
490                         }
491                 }
492                 else if (tool == TOOLRotate || tool == TOOLRotatePoly)
493                 {
494                         if (pts.GetNumPoints() > 0)
495                         {
496                                 if (!haveZeroPoint)
497                                 {
498                                         rotationZeroPoint = GetAdjustedMousePosition(event);
499                                         haveZeroPoint = true;
500                                 }
501                                 else
502                                 {
503                                         // Figure out the angle between the "zero" vector and the current one,
504                                         // then rotate all points relative to the "zero" vector (done by paint())
505                                         QPoint currentPoint = GetAdjustedMousePosition(event);
506                                         Vector v1(rotationZeroPoint.x(), rotationZeroPoint.y(), 0,
507                                                 rotationCenter.x(), rotationCenter.y(), 0);
508                                         Vector v2(currentPoint.x(), currentPoint.y(), 0,
509                                                 rotationCenter.x(), rotationCenter.y(), 0);
510 //                                      rotationAngle = v1.Angle(v2);
511                                         rotationAngle = v2.Angle(v1);
512
513                                         QString s;
514                                         s.sprintf("%.3f degrees", rotationAngle * 180.0 / 3.14159265358979323);
515                                         ((TTEdit *)qApp)->mainWindow->statusBar()->showMessage(s);
516                                 }
517
518                                 update();
519                         }
520                 }
521         }
522         else if (event->buttons() == Qt::NoButton)
523         {
524                 // Moving, not dragging...
525                 if (tool == TOOLSelect || tool == TOOLDelPt || tool == TOOLAddPt
526                         || tool == TOOLPolySelect || tool == TOOLRotatePoly || tool == TOOLFlipWinding)
527                 {
528                         QPoint pt2 = GetAdjustedMousePosition(event);
529                         double closest = 1.0e+99;
530
531                         for(int i=0; i<pts.GetNumPoints(); i++)
532                         {
533                                 double dist = ((pt2.x() - pts.GetX(i)) * (pt2.x() - pts.GetX(i)))
534                                         + ((pt2.y() - pts.GetY(i)) * (pt2.y() - pts.GetY(i)));
535
536                                 if (dist < closest)
537                                         closest = dist, ptHighlight = i;
538                         }
539
540                         if (ptHighlight != oldPtHighlight)
541                         {
542                                 oldPtHighlight = ptHighlight;
543                                 update();
544                         }
545
546                         // What follows here looks like voodoo, but is really simple. What we do is
547                         // check to see if the mouse point has a perpendicular intersection with any of
548                         // the line segments. If it does, calculate the length of the perpendicular
549                         // and choose the smallest length. If there is no perpendicular, then choose the
550                         // length of line connecting the closer of either the first endpoint or the
551                         // second and choose the smallest of those.
552
553                         // There is one bit of math that looks like voodoo to me ATM--will explain once
554                         // I understand it better (the calculation of the length of the perpendicular).
555
556                         if (pts.GetNumPoints() > 1 && tool == TOOLAddPt)
557                         {
558                                 double smallest = 1.0e+99;
559
560                                 for(int i=0; i<pts.GetNumPoints(); i++)
561                                 {
562                                         int32 p1x = pts.GetX(i), p1y = pts.GetY(i),
563                                                 p2x = pts.GetX(pts.GetNext(i)), p2y = pts.GetY(pts.GetNext(i));
564
565                                         Vector ls(p2x, p2y, 0, p1x, p1y, 0), v1(pt2.x(), pt2.y(), 0, p1x, p1y, 0),
566                                                 v2(pt2.x(), pt2.y(), 0, p2x, p2y, 0);
567                                         double pp = ls.Dot(v1) / ls.Magnitude(), dist;
568 // Geometric interpretation:
569 // pp is the paremeterized point on the vector ls where the perpendicular intersects ls.
570 // If pp < 0, then the perpendicular lies beyond the 1st endpoint. If pp > length of ls,
571 // then the perpendicular lies beyond the 2nd endpoint.
572
573                                         if (pp < 0.0)
574                                                 dist = v1.Magnitude();
575                                         else if (pp > ls.Magnitude())
576                                                 dist = v2.Magnitude();
577                                         else                                    // distance = ?Det?(ls, v1) / |ls|
578                                                 dist = fabs((ls.x * v1.y - v1.x * ls.y) / ls.Magnitude());
579
580 //The answer to the above looks like it might be found here:
581 //
582 //If the segment endpoints are s and e, and the point is p, then the test for the perpendicular
583 //intercepting the segment is equivalent to insisting that the two dot products {s-e}.{s-p} and
584 //{e-s}.{e-p} are both non-negative.  Perpendicular distance from the point to the segment is
585 //computed by first computing the area of the triangle the three points form, then dividing by the
586 //length of the segment.  Distances are done just by the Pythagorean theorem.  Twice the area of the
587 //triangle formed by three points is the determinant of the following matrix:
588 //
589 //sx sy 1
590 //ex ey 1
591 //px py 1
592 //
593 //By translating the start point to the origin, this can be rewritten as:
594 //By subtracting row 1 from all rows, you get the following:
595 //[because sx = sy = 0. you could leave out the -sx/y terms below. because we subtracted
596 // row 1 from all rows (including row 1) row 1 turns out to be zero. duh!]
597 //
598 //0         0         0
599 //(ex - sx) (ey - sy) 0
600 //(px - sx) (py - sy) 0
601 //
602 //which greatly simplifies the calculation of the determinant.
603
604                                         if (dist < smallest)
605                                                 smallest = dist, ptNextHighlight = pts.GetNext(i), ptHighlight = i;
606                                 }
607
608                                 if (ptNextHighlight != oldPtNextHighlight)
609                                 {
610                                         oldPtNextHighlight = ptNextHighlight;
611                                         update();
612                                 }
613                         }
614                 }
615
616                 ptPrevious = event->pos();
617         }
618
619         event->accept();
620 }
621
622
623 void EditWindow::mouseReleaseEvent(QMouseEvent * event)
624 {
625         if (event->button() == Qt::RightButton)
626         {
627                 ToolType newTool = toolPalette->FindSelectedTool();
628
629                 // We only change the tool if a new one was actually selected. Otherwise, we do nothing.
630                 if (newTool != TOOLNone)
631                 {
632                         tool = newTool;
633
634                         if (tool == TOOLScroll || tool == TOOLZoom || tool == TOOLAddPoly
635                                 || tool == TOOLDelPoly)
636                                 ptHighlight = -1;
637
638                         if (tool == TOOLAddPoly)
639                                 polyFirstPoint = true;
640                 }
641
642                 toolPalette->setVisible(false);
643                 setCursor(cur[tool]);
644                 // Just in case we changed highlighting style with the new tool...
645                 update();
646         }
647         else if (event->button() == Qt::MidButton)
648         {
649                 setCursor(cur[tool]);                                           // Restore previous cursor
650         }
651         else if (event->button() == Qt::LeftButton)
652         {
653                 if (showRotationCenter)
654                 {
655                         showRotationCenter = false;
656                         haveZeroPoint = false;
657
658                         if (tool == TOOLRotate)
659                                 pts.RotatePoints(rotationAngle, IPoint(rotationCenter.x(), rotationCenter.y()));
660                         else
661                         {
662                                 uint16 poly = pts.GetPolyForPointNumber(ptHighlight);
663                                 pts.RotatePolyAroundCentroid(poly, rotationAngle);
664                         }
665
666                         update();
667                         ((TTEdit *)qApp)->mainWindow->statusBar()->showMessage("");
668                 }
669
670 //              if (tool == TOOLScroll || tool == TOOLZoom)
671 //                      ReleaseMouse();
672 //this is prolly too much
673                 ((TTEdit *)qApp)->charWnd->MakePathFromPoints(&pts);
674                 ((TTEdit *)qApp)->charWnd->update();
675
676         }
677
678         event->accept();
679 }