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