]> Shamusworld >> Repos - ttedit/blob - src/editwindow.cpp
Converted project from wxWidgets to Qt. This will be the LAST time this
[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 <QtGui>
38 #include "graphicprimitives.h"
39 #include "debug.h"
40 #include "vector.h"
41
42 EditWindow::EditWindow(QWidget * parent/*=NULL*/): QWidget(parent),
43         scale(1.0), offsetX(-10), offsetY(-10), tool(TOOLSelect),
44         ptHighlight(-1), oldPtHighlight(-1), ptNextHighlight(-1), oldPtNextHighlight(-1),
45         polyFirstPoint(true)
46 {
47         setBackgroundRole(QPalette::Base);
48         setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
49
50         toolPalette = new ToolWindow();
51         CreateCursors();
52         setCursor(cur[TOOLSelect]);
53         setMouseTracking(true);
54 }
55
56 QSize EditWindow::minimumSizeHint() const
57 {
58         return QSize(50, 50);
59 }
60
61 QSize EditWindow::sizeHint() const
62 {
63         return QSize(400, 400);
64 }
65
66 void EditWindow::CreateCursors(void)
67 {
68         int hotx[8] = {  1,  1, 11, 15,  1,  1,  1,  1 };
69         int hoty[8] = {  1,  1, 11, 13,  1,  1,  1,  1 };
70
71         for(int i=0; i<8; i++)
72         {
73                 QString s;
74                 s.sprintf(":/res/cursor%u.png", i+1);
75                 QPixmap pmTmp(s);
76                 cur[i] = QCursor(pmTmp, hotx[i], hoty[i]);
77         }
78 }
79
80 QPoint EditWindow::GetAdjustedMousePosition(QMouseEvent * event)
81 {
82         QSize winSize = size();
83         // This is undoing the transform, e.g. going from client coords to local coords.
84         // In essence, the height - y is height + (y * -1), the (y * -1) term doing the conversion
85         // of the y-axis from increasing bottom to top.
86         return QPoint(offsetX + event->x(), offsetY + (winSize.height() - event->y()));
87 }
88
89 QPoint EditWindow::GetAdjustedClientPosition(int x, int y)
90 {
91         QSize winSize = size();
92
93         // VOODOO ALERT (ON Y COMPONENT!!!!)
94         return QPoint(-offsetX + x, (winSize.height() - (-offsetY + y)) * +1.0);
95 }
96
97 void EditWindow::paintEvent(QPaintEvent * /*event*/)
98 {
99         QPainter p(this);
100 //hm, causes lockup
101 //      p.setRenderHint(QPainter::Antialiasing);
102 //Doesn't do crap!
103 //dc.SetBackground(*wxWHITE_BRUSH);
104
105 // Due to the screwiness of wxWidgets coord system, the origin is ALWAYS
106 // the upper left corner--regardless of axis orientation, etc...
107 //      int width, height;
108 //      dc.GetSize(&width, &height);
109         QSize winSize = size();
110
111 //      dc.SetDeviceOrigin(-offsetX, height - (-offsetY));
112 //      dc.SetAxisOrientation(true, true);
113         p.translate(QPoint(-offsetX, winSize.height() - (-offsetY)));
114         p.scale(1.0, -1.0);
115
116 // Scrolling can be done by using OffsetViewportOrgEx
117 // Scaling can be done by adjusting SetWindowExtEx (it's denominator of txform)
118 // you'd use: % = ViewportExt / WindowExt
119 // But it makes the window look like crap: fuggetuboutit.
120 // Instead, we have to scale EVERYTHING by hand. Crap!
121 // It's not *that* bad, but not as convenient either...
122
123 //      dc.SetPen(*(wxThePenList->FindOrCreatePen(wxColour(0x00, 0x00, 0xFF), 1, wxDOT)));
124 ////    dc.DrawLine(0, 0, 10, 10);
125         p.setPen(QPen(Qt::blue, 1.0, Qt::DotLine));
126
127     // Draw coordinate axes
128
129 //      dc.CrossHair(0, 0);
130         p.drawLine(0, -16384, 0, 16384);
131         p.drawLine(-16384, 0, 16384, 0);
132
133     // Draw points
134
135         for(int i=0; i<pts.GetNumPoints(); i++)
136         {
137                 if (i == ptHighlight)
138                 {
139 //                      dc.SetPen(*(wxThePenList->FindOrCreatePen(wxColour(0xFF, 0x00, 0x00), 1, wxSOLID)));
140 ////                    SelectObject(hdc, hRedPen1);
141                         p.setPen(QPen(Qt::red, 1.0, Qt::SolidLine));
142
143                         if (pts.GetOnCurve(i))
144                         {
145                                 DrawSquareDotN(p, pts.GetX(i), pts.GetY(i), 7);
146                                 DrawSquareDotN(p, pts.GetX(i), pts.GetY(i), 9);
147                         }
148                         else
149                         {
150                                 DrawRoundDotN(p, pts.GetX(i), pts.GetY(i), 7);
151                                 DrawRoundDotN(p, pts.GetX(i), pts.GetY(i), 9);
152                         }
153                 }
154                 else if ((i == ptHighlight || i == ptNextHighlight) && tool == TOOLAddPt)
155                 {
156 //                      dc.SetPen(*(wxThePenList->FindOrCreatePen(wxColour(0x00, 0xAF, 0x00), 1, wxSOLID)));
157 ////                    SelectObject(hdc, hGreenPen1);
158                         p.setPen(QPen(Qt::green, 1.0, Qt::SolidLine));
159
160                         if (pts.GetOnCurve(i))
161                         {
162                                 DrawSquareDotN(p, pts.GetX(i), pts.GetY(i), 7);
163                                 DrawSquareDotN(p, pts.GetX(i), pts.GetY(i), 9);
164                         }
165                         else
166                         {
167                                 DrawRoundDotN(p, pts.GetX(i), pts.GetY(i), 7);
168                                 DrawRoundDotN(p, pts.GetX(i), pts.GetY(i), 9);
169                         }
170                 }
171                 else
172                 {
173 //                      dc.SetPen(*(wxThePenList->FindOrCreatePen(wxColour(0x00, 0x00, 0x00), 1, wxSOLID)));
174 ////                    SelectObject(hdc, hBlackPen1);
175                         p.setPen(QPen(Qt::black, 1.0, Qt::SolidLine));
176
177                         if (pts.GetOnCurve(i))
178                                 DrawSquareDot(p, pts.GetX(i), pts.GetY(i));
179                         else
180                                 DrawRoundDot(p, pts.GetX(i), pts.GetY(i));
181                 }
182
183                 if (tool == TOOLDelPt && i == ptHighlight)
184                 {
185 #if 0
186                         dc.SetPen(*(wxThePenList->FindOrCreatePen(wxColour(0xFF, 0x00, 0x00), 1, wxSOLID)));
187 //                      SelectObject(hdc, hRedPen1);
188 //                      MoveToEx(hdc, pts.GetX(i) - 5, pts.GetY(i) - 5, NULL);
189 //                      LineTo(hdc, pts.GetX(i) + 5, pts.GetY(i) + 5);
190 //                      LineTo(hdc, pts.GetX(i) - 5, pts.GetY(i) - 5);//Lameness!
191 //                      MoveToEx(hdc, pts.GetX(i) - 5, pts.GetY(i) + 5, NULL);
192 //                      LineTo(hdc, pts.GetX(i) + 5, pts.GetY(i) - 5);
193 //                      LineTo(hdc, pts.GetX(i) - 5, pts.GetY(i) + 5);//More lameness!!
194 #endif
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 ////            SelectObject(hdc, hBlackPen1);
202 //      dc.SetPen(*(wxThePenList->FindOrCreatePen(wxColour(0x00, 0x00, 0x00), 1, wxSOLID)));
203         p.setPen(QPen(Qt::black, 1.0, Qt::SolidLine));
204
205         // Draw curve formed by points
206
207         for(int poly=0; poly<pts.GetNumPolys(); poly++)
208         {
209                 if (pts.GetNumPoints(poly) > 2)
210                 {
211                         // Initial move...
212                         // If it's not on curve, then move to it, otherwise move to last point...
213
214                         int x, y;
215
216                         if (pts.GetOnCurve(poly, pts.GetNumPoints(poly) - 1))
217                                 x = (int)pts.GetX(poly, pts.GetNumPoints(poly) - 1), y = (int)pts.GetY(poly, pts.GetNumPoints(poly) - 1);
218                         else
219                                 x = (int)pts.GetX(poly, 0), y = (int)pts.GetY(poly, 0);
220
221                         for(int i=0; i<pts.GetNumPoints(poly); i++)
222                         {
223                                 if (pts.GetOnCurve(poly, i))
224 //                                      LineTo(hdc, pts.GetX(poly, i), pts.GetY(poly, i));
225                                 {
226                                         p.drawLine(x, y, pts.GetX(poly, i), pts.GetY(poly, i));
227                                         x = (int)pts.GetX(poly, i), y = (int)pts.GetY(poly, i);
228                                 }
229                                 else
230                                 {
231                                         uint32 prev = pts.GetPrev(poly, i), next = pts.GetNext(poly, i);
232                                         float px = pts.GetX(poly, prev), py = pts.GetY(poly, prev),
233                                                 nx = pts.GetX(poly, next), ny = pts.GetY(poly, next);
234
235                                         if (!pts.GetOnCurve(poly, prev))
236                                                 px = (px + pts.GetX(poly, i)) / 2.0f,
237                                                 py = (py + pts.GetY(poly, i)) / 2.0f;
238
239                                         if (!pts.GetOnCurve(poly, next))
240                                                 nx = (nx + pts.GetX(poly, i)) / 2.0f,
241                                                 ny = (ny + pts.GetY(poly, i)) / 2.0f;
242
243                                         Bezier(p, point(px, py), point(pts.GetX(poly, i), pts.GetY(poly, i)), point(nx, ny));
244                                         x = (int)nx, y = (int)ny;
245
246                                         if (pts.GetOnCurve(poly, next))
247                                                 i++;                                    // Following point is on curve, so move past it
248                                 }
249                         }
250                 }
251         }
252 }
253
254 void EditWindow::mousePressEvent(QMouseEvent * event)
255 {
256         if (event->button() == Qt::RightButton)
257         {
258                 toolPalette->move(event->globalPos());
259                 toolPalette->setVisible(true);
260                 setCursor(cur[TOOLSelect]);
261                 toolPalette->prevTool = TOOLSelect;
262         }
263         else if (event->button() == Qt::MidButton)
264         {
265                 setCursor(cur[2]);                                                      // Scrolling cursor
266         }
267         else if (event->button() == Qt::LeftButton)
268         {
269                 if (tool == TOOLScroll || tool == TOOLZoom)
270 ;//meh                  CaptureMouse();                                         // Make sure we capture the mouse when in scroll/zoom mode
271                 else if (tool == TOOLAddPt)             // "Add Point" tool
272                 {
273                         if (pts.GetNumPoints() > 0)
274                         {
275                                 QPoint pt = GetAdjustedMousePosition(event);
276                                 pts.InsertPoint(pts.GetNext(ptHighlight), pt.x(), pt.y(), ((event->modifiers() == Qt::ShiftModifier || event->modifiers() == Qt::ControlModifier) ? false : true));
277                                 ptHighlight = ptNextHighlight;
278                                 update();
279                         }
280                 }
281                 else if (tool == TOOLAddPoly)   // "Add Poly" tool
282                 {
283 #ifdef DEBUGFOO
284 WriteLogMsg("Adding point... # polys: %u, # points: %u", pts.GetNumPolys(), pts.GetNumPoints());
285 #endif
286                         if (polyFirstPoint)
287                         {
288                                 polyFirstPoint = false;
289                                 pts.AddNewPolyAtEnd();
290                         }
291
292                         QPoint pt = GetAdjustedMousePosition(event);
293 //printf("GetAdjustedMousePosition = %i, %i\n", pt.x(), pt.y());
294                         // Append a point to the end of the structure
295                         pts += IPoint(pt.x(), pt.y(), ((event->modifiers() == Qt::ShiftModifier || event->modifiers() == Qt::ControlModifier) ? false : true));
296                         ptHighlight = pts.GetNumPoints() - 1;
297                         update();
298 #ifdef DEBUGFOO
299 WriteLogMsg(" --> [# polys: %u, # points: %u]\n", pts.GetNumPolys(), pts.GetNumPoints());
300 #endif
301                 }
302                 else if (tool == TOOLSelect || tool == TOOLPolySelect)
303                 {
304                         if (pts.GetNumPoints() > 0)
305                         {
306                                 pt = GetAdjustedClientPosition(pts.GetX(ptHighlight), pts.GetY(ptHighlight));
307 //printf("GetAdjustedClientPosition = %i, %i\n", pt.x(), pt.y());
308 //                              WarpPointer(pt.x, pt.y);
309                                 QCursor::setPos(mapToGlobal(pt));
310
311                                 if (event->modifiers() == Qt::ShiftModifier || event->modifiers() == Qt::ControlModifier)
312                                 {
313                                         pts.SetOnCurve(ptHighlight, !pts.GetOnCurve(ptHighlight));
314                                         update();
315                                 }
316                         }
317                 }
318                 else if (tool == TOOLDelPt)
319                 {
320                         if (pts.GetNumPoints() > 0)
321 //Or could use:
322 //                      if (ptHighlight != -1)
323                         {
324 //This assumes that WM_MOUSEMOVE happens before this!
325 //The above commented out line should take care of this contingency... !!! FIX !!!
326                                 pts.DeletePoint(ptHighlight);
327                                 update();
328                         }
329                 }
330         }
331
332         event->accept();
333 }
334
335 void EditWindow::mouseMoveEvent(QMouseEvent * event)
336 {
337         if (event->buttons() == Qt::RightButton)
338         {
339                 ToolType newTool = toolPalette->FindSelectedTool();
340
341                 if (newTool != toolPalette->prevTool)
342                 {
343                         toolPalette->prevTool = newTool;
344                         toolPalette->repaint();
345                 }
346         }
347         else if (event->buttons() == Qt::MidButton)
348         {
349                 // Calc offset from previous point
350                 pt = event->pos();
351                 ptOffset = QPoint(pt.x() - ptPrevious.x(), pt.y() - ptPrevious.y());
352
353 // Then multiply it by the scaling factor. Whee!
354                 // This looks wacky because we're using screen coords for the offset...
355                 // Otherwise, we would subtract both offsets!
356                 offsetX -= ptOffset.x(), offsetY += ptOffset.y();
357                 update();
358                 ptPrevious = pt;
359         }
360         else if (event->buttons() == Qt::LeftButton)
361         {
362 #if 0
363                         if (tool == TOOLScroll)
364                         {
365                             // Extract current point from lParam/calc offset from previous point
366
367                                 pt = e.GetPosition();
368                                 ptOffset.x = pt.x - ptPrevious.x,
369                                 ptOffset.y = pt.y - ptPrevious.y;
370
371                                 // NOTE: OffsetViewportOrg operates in DEVICE UNITS...
372
373 //Seems there's no equivalent for this in wxWidgets...!
374 //!!! FIX !!!
375 //                              hdc = GetDC(hWnd);
376 //                              OffsetViewportOrgEx(hdc, ptOffset.x, ptOffset.y, NULL);
377 //                              ReleaseDC(hWnd, hdc);
378
379 // this shows that it works, so the logic above must be faulty...
380 // And it is. It should convert the coords first, then do the subtraction to figure the offset...
381 // Above: DONE
382 // Then multiply it by the scaling factor. Whee!
383                                 // This looks wacky because we're using screen coords for the offset...
384                                 // Otherwise, we would subtract both offsets!
385                                 offsetX -= ptOffset.x, offsetY += ptOffset.y;
386                                 Refresh();
387                         }
388                         else
389 #endif
390                         if (tool == TOOLAddPt || tool == TOOLAddPoly || tool == TOOLSelect)
391                         {
392                                 if (tool != TOOLAddPt || pts.GetNumPoints() > 0)//yecch.
393                                 {
394 //temporary, for testing. BTW, Select drag bug is here...!
395 #if 1
396                                         QPoint pt2 = GetAdjustedMousePosition(event);
397                                         pts.SetXY(ptHighlight, pt2.x(), pt2.y());
398                                         update();
399 #endif
400                                 }
401                         }
402                         else if (tool == TOOLPolySelect)
403                         {
404                                 if (pts.GetNumPoints() > 0)
405                                 {
406                                         QPoint pt2 = GetAdjustedMousePosition(event);
407                                         // Should also set onCurve here as well, depending on keystate
408 //Or should we?
409                                         pts.OffsetPoly(pts.GetPoly(ptHighlight), pt2.x() - pts.GetX(ptHighlight), pt2.y() - pts.GetY(ptHighlight));
410                                         update();
411                                 }
412                         }
413         }
414         else if (event->buttons() == Qt::NoButton)
415         {
416                 // Moving, not dragging...
417                 if (tool == TOOLSelect || tool == TOOLDelPt || tool == TOOLAddPt
418                         || tool == TOOLPolySelect)// || tool == TOOLAddPoly)
419                 {
420                         QPoint pt2 = GetAdjustedMousePosition(event);
421                         double closest = 1.0e+99;
422
423                         for(int i=0; i<pts.GetNumPoints(); i++)
424                         {
425                                 double dist = ((pt2.x() - pts.GetX(i)) * (pt2.x() - pts.GetX(i)))
426                                         + ((pt2.y() - pts.GetY(i)) * (pt2.y() - pts.GetY(i)));
427
428                                 if (dist < closest)
429                                         closest = dist, ptHighlight = i;
430                         }
431
432                         if (ptHighlight != oldPtHighlight)
433                         {
434                                 oldPtHighlight = ptHighlight;
435                                 update();
436                         }
437
438                         // What follows here looks like voodoo, but is really simple. What we do is
439                         // check to see if the mouse point has a perpendicular intersection with any of
440                         // the line segments. If it does, calculate the length of the perpendicular
441                         // and choose the smallest length. If there is no perpendicular, then choose the
442                         // length of line connecting the closer of either the first endpoint or the
443                         // second and choose the smallest of those.
444
445                         // There is one bit of math that looks like voodoo to me ATM--will explain once
446                         // I understand it better (the calculation of the length of the perpendicular).
447
448                         if (pts.GetNumPoints() > 1 && tool == TOOLAddPt)
449                         {
450                                 double smallest = 1.0e+99;
451
452                                 for(int i=0; i<pts.GetNumPoints(); i++)
453                                 {
454                                         int32 p1x = pts.GetX(i), p1y = pts.GetY(i),
455                                                 p2x = pts.GetX(pts.GetNext(i)), p2y = pts.GetY(pts.GetNext(i));
456
457                                         vector ls(p2x, p2y, 0, p1x, p1y, 0), v1(pt2.x(), pt2.y(), 0, p1x, p1y, 0),
458                                                 v2(pt2.x(), pt2.y(), 0, p2x, p2y, 0);
459                                         double pp = ls.dot(v1) / ls.length(), dist;
460 // Geometric interpretation:
461 // pp is the paremeterized point on the vector ls where the perpendicular intersects ls.
462 // If pp < 0, then the perpendicular lies beyond the 1st endpoint. If pp > length of ls,
463 // then the perpendicular lies beyond the 2nd endpoint.
464
465                                         if (pp < 0.0)
466                                                 dist = v1.length();
467                                         else if (pp > ls.length())
468                                                 dist = v2.length();
469                                         else                                    // distance = ?Det?(ls, v1) / |ls|
470                                                 dist = fabs((ls.x * v1.y - v1.x * ls.y) / ls.length());
471
472 //The answer to the above looks like it might be found here:
473 //
474 //If the segment endpoints are s and e, and the point is p, then the test for the perpendicular
475 //intercepting the segment is equivalent to insisting that the two dot products {s-e}.{s-p} and
476 //{e-s}.{e-p} are both non-negative.  Perpendicular distance from the point to the segment is
477 //computed by first computing the area of the triangle the three points form, then dividing by the
478 //length of the segment.  Distances are done just by the Pythagorean theorem.  Twice the area of the
479 //triangle formed by three points is the determinant of the following matrix:
480 //
481 //sx sy 1
482 //ex ey 1
483 //px py 1
484 //
485 //(???) By translating the start point to the origin, this can be rewritten as:
486 //By subtracting row 1 from all rows, you get the following:
487 //
488 //0         0         0
489 //(ex - sx) (ey - sy) 0
490 //(px - sx) (py - sy) 0
491 //
492 //which greatly simplifies the calculation of the determinant.
493
494                                         if (dist < smallest)
495                                                 smallest = dist, ptNextHighlight = pts.GetNext(i), ptHighlight = i;
496                                 }
497
498                                 if (ptNextHighlight != oldPtNextHighlight)
499                                 {
500                                         oldPtNextHighlight = ptNextHighlight;
501                                         update();
502                                 }
503                         }
504                 }
505
506                 ptPrevious = event->pos();
507         }
508
509         event->accept();
510 }
511
512 void EditWindow::mouseReleaseEvent(QMouseEvent * event)
513 {
514         if (event->button() == Qt::RightButton)
515         {
516                 ToolType newTool = toolPalette->FindSelectedTool();
517
518                 // We only change the tool if a new one was actually selected. Otherwise, we do nothing.
519                 if (newTool != TOOLNone)
520                 {
521                         tool = newTool;
522
523                         if (tool == TOOLScroll || tool == TOOLZoom || tool == TOOLAddPoly
524                                 || tool == TOOLDelPoly)
525                                 ptHighlight = -1;
526
527                         if (tool == TOOLAddPoly)
528                                 polyFirstPoint = true;
529                 }
530
531                 toolPalette->setVisible(false);
532                 setCursor(cur[tool]);
533                 // Just in case we changed highlighting style with the new tool...
534                 update();
535         }
536         else if (event->button() == Qt::MidButton)
537         {
538                 setCursor(cur[tool]);                                           // Restore previous cursor
539         }
540         else if (event->button() == Qt::LeftButton)
541         {
542 //              if (tool == TOOLScroll || tool == TOOLZoom)
543 //                      ReleaseMouse();
544         }
545
546         event->accept();
547 }
548
549
550
551 #if 0
552 #include "editwindow.h"
553 #include "graphicprimitives.h"
554 #include "toolwindow.h"
555 #include "debug.h"
556 #include "vector.h"
557
558
559 BEGIN_EVENT_TABLE(TTEditWindow, wxWindow)
560         EVT_PAINT(TTEditWindow::OnPaint)
561         EVT_MOUSE_EVENTS(TTEditWindow::OnMouseEvent)
562 END_EVENT_TABLE()
563
564 TTEditWindow::TTEditWindow(wxFrame * parent, const wxPoint &pos, const wxSize &size, long style):
565         wxWindow(parent, -1, pos, size, style | wxFULL_REPAINT_ON_RESIZE),
566         app(wxGetApp()), scale(1.0), offsetX(-10), offsetY(-10), tool(TOOLSelect),
567         ptHighlight(-1), oldPtHighlight(-1), ptNextHighlight(-1), oldPtNextHighlight(-1),
568         polyFirstPoint(true), bmp(NULL)
569 {
570         SetCursor(*(app.cur[tool]));
571         SetBackgroundColour(wxColour(0xFF, 0xFF, 0xFF));
572
573         wxString s;
574         s.Printf(_("Zoom: %.2f%%"), scale * 100.0);
575         parent->SetStatusText(s, 1);
576 }
577
578 TTEditWindow::~TTEditWindow(void)
579 {
580         if (bmp)
581                 delete bmp;
582 }
583
584 void TTEditWindow::OnPaint(wxPaintEvent &e)
585 {
586         wxPaintDC dc(this);
587 //Doesn't do crap!
588 //dc.SetBackground(*wxWHITE_BRUSH);
589
590 // Due to the screwiness of wxWidgets coord system, the origin is ALWAYS
591 // the upper left corner--regardless of axis orientation, etc...
592         wxCoord width, height;
593         dc.GetSize(&width, &height);
594
595         dc.SetDeviceOrigin(-offsetX, height - (-offsetY));
596         dc.SetAxisOrientation(true, true);
597
598 // Scrolling can be done by using OffsetViewportOrgEx
599 // Scaling can be done by adjusting SetWindowExtEx (it's denominator of txform)
600 // you'd use: % = ViewportExt / WindowExt
601 // But it makes the window look like crap: fuggetuboutit.
602 // Instead, we have to scale EVERYTHING by hand. Crap!
603 // It's not *that* bad, but not as convenient either...
604
605         dc.SetPen(*(wxThePenList->FindOrCreatePen(wxColour(0x00, 0x00, 0xFF), 1, wxDOT)));
606 //      dc.DrawLine(0, 0, 10, 10);
607
608     // Draw coordinate axes
609
610         dc.CrossHair(0, 0);
611
612     // Draw points
613
614         for(int i=0; i<pts.GetNumPoints(); i++)
615         {
616                 if (i == ptHighlight)
617                 {
618                         dc.SetPen(*(wxThePenList->FindOrCreatePen(wxColour(0xFF, 0x00, 0x00), 1, wxSOLID)));
619 //                      SelectObject(hdc, hRedPen1);
620
621                         if (pts.GetOnCurve(i))
622                         {
623                                 DrawSquareDotN(dc, pts.GetX(i), pts.GetY(i), 7);
624                                 DrawSquareDotN(dc, pts.GetX(i), pts.GetY(i), 9);
625                         }
626                         else
627                         {
628                                 DrawRoundDotN(dc, pts.GetX(i), pts.GetY(i), 7);
629                                 DrawRoundDotN(dc, pts.GetX(i), pts.GetY(i), 9);
630                         }
631                 }
632                 else if ((i == ptHighlight || i == ptNextHighlight) && tool == TOOLAddPt)
633                 {
634                         dc.SetPen(*(wxThePenList->FindOrCreatePen(wxColour(0x00, 0xAF, 0x00), 1, wxSOLID)));
635 //                      SelectObject(hdc, hGreenPen1);
636
637                         if (pts.GetOnCurve(i))
638                         {
639                                 DrawSquareDotN(dc, pts.GetX(i), pts.GetY(i), 7);
640                                 DrawSquareDotN(dc, pts.GetX(i), pts.GetY(i), 9);
641                         }
642                         else
643                         {
644                                 DrawRoundDotN(dc, pts.GetX(i), pts.GetY(i), 7);
645                                 DrawRoundDotN(dc, pts.GetX(i), pts.GetY(i), 9);
646                         }
647                 }
648                 else
649                 {
650                         dc.SetPen(*(wxThePenList->FindOrCreatePen(wxColour(0x00, 0x00, 0x00), 1, wxSOLID)));
651 //                      SelectObject(hdc, hBlackPen1);
652
653                         if (pts.GetOnCurve(i))
654                                 DrawSquareDot(dc, pts.GetX(i), pts.GetY(i));
655                         else
656                                 DrawRoundDot(dc, pts.GetX(i), pts.GetY(i));
657                 }
658
659                 if (tool == TOOLDelPt && i == ptHighlight)
660                 {
661                         dc.SetPen(*(wxThePenList->FindOrCreatePen(wxColour(0xFF, 0x00, 0x00), 1, wxSOLID)));
662 //                      SelectObject(hdc, hRedPen1);
663 //                      MoveToEx(hdc, pts.GetX(i) - 5, pts.GetY(i) - 5, NULL);
664 //                      LineTo(hdc, pts.GetX(i) + 5, pts.GetY(i) + 5);
665 //                      LineTo(hdc, pts.GetX(i) - 5, pts.GetY(i) - 5);//Lameness!
666 //                      MoveToEx(hdc, pts.GetX(i) - 5, pts.GetY(i) + 5, NULL);
667 //                      LineTo(hdc, pts.GetX(i) + 5, pts.GetY(i) - 5);
668 //                      LineTo(hdc, pts.GetX(i) - 5, pts.GetY(i) + 5);//More lameness!!
669                         dc.DrawLine(pts.GetX(i) - 5, pts.GetY(i) - 5, pts.GetX(i) + 5, pts.GetY(i) + 5);
670                         dc.DrawLine(pts.GetX(i) + 5, pts.GetY(i) - 5, pts.GetX(i) - 5, pts.GetY(i) + 5);
671                 }
672         }
673
674 //              SelectObject(hdc, hBlackPen1);
675         dc.SetPen(*(wxThePenList->FindOrCreatePen(wxColour(0x00, 0x00, 0x00), 1, wxSOLID)));
676
677         // Draw curve formed by points
678
679         for(int poly=0; poly<pts.GetNumPolys(); poly++)
680         {
681                 if (pts.GetNumPoints(poly) > 2)
682                 {
683                         // Initial move...
684                         // If it's not on curve, then move to it, otherwise move to last point...
685
686                         wxCoord x, y;
687
688                         if (pts.GetOnCurve(poly, pts.GetNumPoints(poly) - 1))
689                                 x = (wxCoord)pts.GetX(poly, pts.GetNumPoints(poly) - 1), y = (wxCoord)pts.GetY(poly, pts.GetNumPoints(poly) - 1);
690                         else
691                                 x = (wxCoord)pts.GetX(poly, 0), y = (wxCoord)pts.GetY(poly, 0);
692
693                         for(int i=0; i<pts.GetNumPoints(poly); i++)
694                         {
695                                 if (pts.GetOnCurve(poly, i))
696 //                                      LineTo(hdc, pts.GetX(poly, i), pts.GetY(poly, i));
697                                 {
698                                         dc.DrawLine(x, y, pts.GetX(poly, i), pts.GetY(poly, i));
699                                         x = (wxCoord)pts.GetX(poly, i), y = (wxCoord)pts.GetY(poly, i);
700                                 }
701                                 else
702                                 {
703                                         uint32 prev = pts.GetPrev(poly, i), next = pts.GetNext(poly, i);
704                                         float px = pts.GetX(poly, prev), py = pts.GetY(poly, prev),
705                                                 nx = pts.GetX(poly, next), ny = pts.GetY(poly, next);
706
707                                         if (!pts.GetOnCurve(poly, prev))
708                                                 px = (px + pts.GetX(poly, i)) / 2.0f,
709                                                 py = (py + pts.GetY(poly, i)) / 2.0f;
710
711                                         if (!pts.GetOnCurve(poly, next))
712                                                 nx = (nx + pts.GetX(poly, i)) / 2.0f,
713                                                 ny = (ny + pts.GetY(poly, i)) / 2.0f;
714
715                                         Bezier(dc, point(px, py), point(pts.GetX(poly, i), pts.GetY(poly, i)), point(nx, ny));
716                                         x = (wxCoord)nx, y = (wxCoord)ny;
717
718                                         if (pts.GetOnCurve(poly, next))
719                                                 i++;                                    // Following point is on curve, so move past it
720                                 }
721                         }
722                 }
723         }
724
725 //              SelectObject(hdc, oldPen);                              // Restore the stuff we disrupted...
726         dc.SetPen(wxNullPen);
727 //              SelectObject(hdc, oldBrush);
728 //              EndPaint(hWnd, &ps);
729 }
730
731 void TTEditWindow::OnMouseEvent(wxMouseEvent &e)
732 {
733 #ifdef DEBUGTP
734 //printf("!!! This window %s focus...!\n", (HasCapture() ? "has" : "doesn't have"));
735 #endif
736         if (e.RightDown())
737         {
738                 // Handle tool palette (NOTE: tool palette deals with RightUp event.)
739
740                 wxPoint pt = ClientToScreen(e.GetPosition());
741                 app.toolPalette->Move(pt);
742                 app.toolPalette->Show(true);
743                 SetCursor(*wxSTANDARD_CURSOR);
744                 app.toolPalette->SetCursor(*wxSTANDARD_CURSOR);
745                 app.toolPalette->prevTool = TOOLSelect;
746                 app.toolPalette->Refresh(false);
747                 CaptureMouse();
748         }
749         else if (e.RightUp())
750         {
751                 ToolType newTool = app.toolPalette->FindSelectedTool();//, oldTool = tool;
752
753                 // We only change the tool if a new one was actually selected. Otherwise, we do nothing.
754                 if (newTool != TOOLNone)
755                 {
756                         tool = newTool;
757
758                         if (tool == TOOLScroll || tool == TOOLZoom || tool == TOOLAddPoly
759                                 || tool == TOOLDelPoly)
760                                 ptHighlight = -1;
761
762                         if (tool == TOOLAddPoly)
763                                 polyFirstPoint = true;
764                 }
765
766                 ReleaseMouse();
767                 app.toolPalette->Show(false);
768                 SetCursor(*(app.cur[tool]));
769         }
770         else if (e.LeftDown())
771         {
772                 if (tool == TOOLScroll || tool == TOOLZoom)
773                         CaptureMouse();                                         // Make sure we capture the mouse when in scroll/zoom mode
774                 else if (tool == TOOLAddPt)             // "Add Point" tool
775                 {
776                         if (pts.GetNumPoints() > 0)
777                         {
778                                 wxPoint pt = GetAdjustedMousePosition(e);
779                                 pts.InsertPoint(pts.GetNext(ptHighlight), pt.x, pt.y, (e.ShiftDown() | e.ControlDown() ? false : true));
780                                 ptHighlight = ptNextHighlight;
781                                 Refresh();
782                         }
783                 }
784                 else if (tool == TOOLAddPoly)   // "Add Poly" tool
785                 {
786 #ifdef DEBUGFOO
787 WriteLogMsg("Adding point... # polys: %u, # points: %u", pts.GetNumPolys(), pts.GetNumPoints());
788 #endif
789                         if (polyFirstPoint)
790                         {
791                                 polyFirstPoint = false;
792                                 pts.AddNewPolyAtEnd();
793                         }
794
795                         wxPoint pt = GetAdjustedMousePosition(e);
796                         // Append a point to the end of the structure
797                         pts += IPoint(pt.x, pt.y, (e.ShiftDown() | e.ControlDown() ? false : true));
798                         ptHighlight = pts.GetNumPoints() - 1;
799                         Refresh();
800 #ifdef DEBUGFOO
801 WriteLogMsg(" --> [# polys: %u, # points: %u]\n", pts.GetNumPolys(), pts.GetNumPoints());
802 #endif
803                 }
804                 else if (tool == TOOLSelect || tool == TOOLPolySelect)
805                 {
806                         if (pts.GetNumPoints() > 0)
807                         {
808                                 pt = GetAdjustedClientPosition(pts.GetX(ptHighlight), pts.GetY(ptHighlight));
809                                 WarpPointer(pt.x, pt.y);
810
811                                 if (e.ShiftDown() | e.ControlDown())
812                                         pts.SetOnCurve(ptHighlight, !pts.GetOnCurve(ptHighlight));
813                         }
814                 }
815                 else if (tool == TOOLDelPt)
816                 {
817                         if (pts.GetNumPoints() > 0)
818 //Or could use:
819 //                      if (ptHighlight != -1)
820                         {
821 //This assumes that WM_MOUSEMOVE happens before this!
822 //The above commented out line should take care of this contingency... !!! FIX !!!
823                                 pts.DeletePoint(ptHighlight);
824                                 Refresh();
825                         }
826                 }
827         }
828         else if (e.LeftUp())
829         {
830                 if (tool == TOOLScroll || tool == TOOLZoom)
831                         ReleaseMouse();
832         }
833         else if (e.MiddleDown())
834         {
835                 SetCursor(*(app.cur[2]));                               // Scrolling cursor
836         }
837         else if (e.MiddleUp())
838         {
839                 SetCursor(*(app.cur[tool]));                    // Restore previous cursor
840         }
841         else if (e.Dragging())
842         {
843                 if (e.RightIsDown())
844                 {
845                         ToolType newTool = app.toolPalette->FindSelectedTool();
846
847                         if (newTool != app.toolPalette->prevTool)
848                         {
849                                 app.toolPalette->prevTool = newTool;
850                                 app.toolPalette->Refresh(false);
851                         }
852
853 //                      return;
854                 }
855                 else if (e.MiddleIsDown())
856                 {
857                     // Calc offset from previous point
858                         pt = e.GetPosition();
859                         ptOffset.x = pt.x - ptPrevious.x,
860                         ptOffset.y = pt.y - ptPrevious.y;
861
862 // Then multiply it by the scaling factor. Whee!
863                         // This looks wacky because we're using screen coords for the offset...
864                         // Otherwise, we would subtract both offsets!
865                         offsetX -= ptOffset.x, offsetY += ptOffset.y;
866                         Refresh();
867 //                      ptPrevious = pt;
868
869 //                      return;
870                 }
871
872                 else if (e.LeftIsDown())
873                 {
874 #if 0
875                         if (tool == TOOLScroll)
876                         {
877                             // Extract current point from lParam/calc offset from previous point
878
879                                 pt = e.GetPosition();
880                                 ptOffset.x = pt.x - ptPrevious.x,
881                                 ptOffset.y = pt.y - ptPrevious.y;
882
883                                 // NOTE: OffsetViewportOrg operates in DEVICE UNITS...
884
885 //Seems there's no equivalent for this in wxWidgets...!
886 //!!! FIX !!!
887 //                              hdc = GetDC(hWnd);
888 //                              OffsetViewportOrgEx(hdc, ptOffset.x, ptOffset.y, NULL);
889 //                              ReleaseDC(hWnd, hdc);
890
891 // this shows that it works, so the logic above must be faulty...
892 // And it is. It should convert the coords first, then do the subtraction to figure the offset...
893 // Above: DONE
894 // Then multiply it by the scaling factor. Whee!
895                                 // This looks wacky because we're using screen coords for the offset...
896                                 // Otherwise, we would subtract both offsets!
897                                 offsetX -= ptOffset.x, offsetY += ptOffset.y;
898                                 Refresh();
899                         }
900                         else
901 #endif
902                         if (tool == TOOLAddPt || tool == TOOLAddPoly || tool == TOOLSelect)
903                         {
904                                 if (tool != TOOLAddPt || pts.GetNumPoints() > 0)//yecch.
905                                 {
906 //temporary, for testing. BTW, Select drag bug is here...!
907 #if 1
908                                         wxPoint pt2 = GetAdjustedMousePosition(e);
909                                         pts.SetXY(ptHighlight, pt2.x, pt2.y);
910                                         Refresh();
911 #endif
912                                 }
913                         }
914                         else if (tool == TOOLPolySelect)
915                         {
916                                 if (pts.GetNumPoints() > 0)
917                                 {
918                                         wxPoint pt2 = GetAdjustedMousePosition(e);
919                                         // Should also set onCurve here as well, depending on keystate
920 //Or should we?
921                                         pts.OffsetPoly(pts.GetPoly(ptHighlight), pt2.x - pts.GetX(ptHighlight), pt2.y - pts.GetY(ptHighlight));
922                                         Refresh();
923                                 }
924                         }
925                 }
926
927                 ptPrevious = pt;
928         }
929         else if (e.Moving())
930         {
931 //              else    // Moving, not dragging...
932 //              {
933                         if (tool == TOOLSelect || tool == TOOLDelPt || tool == TOOLAddPt
934                                 || tool == TOOLPolySelect)// || tool == TOOLAddPoly)
935                         {
936                                 wxPoint pt2 = GetAdjustedMousePosition(e);
937                                 double closest = 1.0e+99;
938
939                                 for(int i=0; i<pts.GetNumPoints(); i++)
940                                 {
941                                         double dist = ((pt2.x - pts.GetX(i)) * (pt2.x - pts.GetX(i)))
942                                                 + ((pt2.y - pts.GetY(i)) * (pt2.y - pts.GetY(i)));
943
944                                         if (dist < closest)
945                                                 closest = dist, ptHighlight = i;
946                                 }
947
948                                 if (ptHighlight != oldPtHighlight)
949                                 {
950                                         oldPtHighlight = ptHighlight;
951                                         Refresh();
952                                 }
953
954                                 // What follows here looks like voodoo, but is really simple. What we do is
955                                 // check to see if the mouse point has a perpendicular intersection with any of
956                                 // the line segments. If it does, calculate the length of the perpendicular
957                                 // and choose the smallest length. If there is no perpendicular, then choose the
958                                 // length of line connecting the closer of either the first endpoint or the
959                                 // second and choose the smallest of those.
960
961                                 // There is one bit of math that looks like voodoo to me ATM--will explain once
962                                 // I understand it better (the calculation of the length of the perpendicular).
963
964                                 if (pts.GetNumPoints() > 1 && tool == TOOLAddPt)
965                                 {
966                                         double smallest = 1.0e+99;
967
968                                         for(int i=0; i<pts.GetNumPoints(); i++)
969                                         {
970                                                 int32 p1x = pts.GetX(i), p1y = pts.GetY(i),
971                                                         p2x = pts.GetX(pts.GetNext(i)), p2y = pts.GetY(pts.GetNext(i));
972
973                                                 vector ls(p2x, p2y, 0, p1x, p1y, 0), v1(pt2.x, pt2.y, 0, p1x, p1y, 0),
974                                                         v2(pt2.x, pt2.y, 0, p2x, p2y, 0);
975                                                 double pp = ls.dot(v1) / ls.length(), dist;
976 // Geometric interpretation:
977 // pp is the paremeterized point on the vector ls where the perpendicular intersects ls.
978 // If pp < 0, then the perpendicular lies beyond the 1st endpoint. If pp > length of ls,
979 // then the perpendicular lies beyond the 2nd endpoint.
980
981                                                 if (pp < 0.0)
982                                                         dist = v1.length();
983                                                 else if (pp > ls.length())
984                                                         dist = v2.length();
985                                                 else                                    // distance = ?Det?(ls, v1) / |ls|
986                                                         dist = fabs((ls.x * v1.y - v1.x * ls.y) / ls.length());
987
988 //The answer to the above looks like it might be found here:
989 //
990 //If the segment endpoints are s and e, and the point is p, then the test for the perpendicular
991 //intercepting the segment is equivalent to insisting that the two dot products {s-e}.{s-p} and
992 //{e-s}.{e-p} are both non-negative.  Perpendicular distance from the point to the segment is
993 //computed by first computing the area of the triangle the three points form, then dividing by the
994 //length of the segment.  Distances are done just by the Pythagorean theorem.  Twice the area of the
995 //triangle formed by three points is the determinant of the following matrix:
996 //
997 //sx sy 1
998 //ex ey 1
999 //px py 1
1000 //
1001 //(???) By translating the start point to the origin, this can be rewritten as:
1002 //By subtracting row 1 from all rows, you get the following:
1003 //
1004 //0         0         0
1005 //(ex - sx) (ey - sy) 0
1006 //(px - sx) (py - sy) 0
1007 //
1008 //which greatly simplifies the calculation of the determinant.
1009
1010                                                 if (dist < smallest)
1011                                                         smallest = dist, ptNextHighlight = pts.GetNext(i), ptHighlight = i;
1012                                         }
1013
1014                                         if (ptNextHighlight != oldPtNextHighlight)
1015                                         {
1016                                                 oldPtNextHighlight = ptNextHighlight;
1017                                                 Refresh();
1018                                         }
1019                                 }
1020                         }
1021 //              }
1022
1023                 ptPrevious = e.GetPosition();
1024         }
1025 }
1026
1027 wxPoint TTEditWindow::GetAdjustedMousePosition(wxMouseEvent &e)
1028 {
1029         wxCoord width, height;
1030         wxClientDC dc(this);
1031
1032         dc.GetSize(&width, &height);
1033         dc.SetDeviceOrigin(-offsetX, height - (-offsetY));
1034         dc.SetAxisOrientation(true, true);
1035
1036 #if 0
1037 wxStatusBar * sb = ((wxFrame *)GetParent())->GetStatusBar();
1038 wxString s;
1039 s.Printf("Logical mouse pos: %d, %d (%d, %d)", pt.x, pt.y, width, height);
1040 sb->SetStatusText(s);
1041 #endif
1042
1043         return e.GetLogicalPosition(dc);
1044 }
1045
1046 wxPoint TTEditWindow::GetAdjustedClientPosition(wxCoord x, wxCoord y)
1047 {
1048         wxCoord width, height;
1049         wxClientDC dc(this);
1050
1051         dc.GetSize(&width, &height);
1052         dc.SetDeviceOrigin(-offsetX, height - (-offsetY));
1053         dc.SetAxisOrientation(true, true);
1054
1055         return wxPoint(dc.LogicalToDeviceX(x), dc.LogicalToDeviceY(y));
1056 }
1057
1058
1059 #if 0
1060
1061 !!! OLD STUFF !!!
1062
1063
1064 LRESULT CALLBACK WndProc(HWND hWnd, UINT msgID, WPARAM wParam, LPARAM lParam)
1065 {
1066         RECT rc1, rc2;
1067         HDC hdc;
1068         POINT pt, ptOffset;
1069         SIZE sz;
1070         PAINTSTRUCT ps;
1071
1072         switch (msgID)
1073         {
1074         case WM_CREATE:
1075
1076                 MiscCenterWnd(hWnd, GetDesktopWindow());
1077                 InitCommonControls();
1078                 hStatusBar = CreateStatusWindow(WS_CHILD | WS_VISIBLE, statusBarTxt, hWnd, ID_STATUSBAR);
1079
1080                 if (!hStatusBar)
1081                         return -1;
1082
1083 // clean this crap up!
1084 // well, the only crappy thing here is using a POINT as an int array, but otherwise, this is OK
1085                 wsprintf(strBuf, zoom, 1000);
1086                 hdc = GetDC(hWnd);
1087                 GetTextExtentPoint32(hdc, strBuf, lstrlen(strBuf), &sz);
1088                 ReleaseDC(hWnd, hdc);
1089                 zoomWndWidth = sz.cx;
1090                 wsprintf(strBuf, zoom, 100);
1091
1092                 GetClientRect(hWnd, &rc1);
1093                 pt.x = rc1.right - zoomWndWidth, pt.y = -1;
1094                 SendMessage(hStatusBar, SB_SETPARTS, 2, (LPARAM)&pt);
1095                 SendMessage(hStatusBar, SB_SETTEXT, (0 | SBT_NOBORDERS), (LPARAM)statusBarTxt);
1096                 SendMessage(hStatusBar, SB_SETTEXT, 1, (LPARAM)strBuf);
1097
1098                 hToolBar = CreateToolbarEx(hWnd, WS_CHILD | WS_BORDER | WS_VISIBLE | TBSTYLE_TOOLTIPS,
1099                         IDR_TOOLBAR1, 3, hInst, IDB_TOOLBAR1, tbButtons, 4, 16, 16, 16, 16, sizeof(TBBUTTON));
1100
1101                 if (!hToolBar)
1102                         return -1;
1103
1104                 CreateNewDoc();
1105
1106 // The following can only be done because we use a private DC (using "CS_OWNDC")
1107 // (Is that true???)
1108 // Set the mapping to draw the character so it fits in the viewport...
1109                 hdc = GetDC(hWnd);
1110                 GetClientRect(hWnd, &rc1);
1111                 GetClientRect(hStatusBar, &rc2);
1112                 rc1.bottom -= rc2.bottom;
1113                 SetMapMode(hdc, MM_ISOTROPIC);
1114                 SetWindowExtEx(hdc, rc1.right, rc1.bottom, NULL);
1115                 SetViewportExtEx(hdc, rc1.right, -rc1.bottom, NULL);
1116                 SetViewportOrgEx(hdc, 0, rc1.bottom, NULL);
1117                 ReleaseDC(hWnd, hdc);
1118                 break;
1119
1120         case WM_CLOSE:
1121
1122                 if (SaveChanges())
1123                 {
1124                         wpM.length = wpC.length = sizeof(WINDOWPLACEMENT);
1125                         GetWindowPlacement(hMainWnd, &wpM);
1126                         GetWindowPlacement(hCharWnd, &wpC);
1127
1128                         if (!IsWindowVisible(hCharWnd))         // Needed because Windows lies about visibility
1129                                 wpC.showCmd = SW_HIDE;
1130
1131                         hdc = GetDC(hWnd);
1132                         GetViewportOrgEx(hdc, &ptVPM);
1133                         ReleaseDC(hWnd, hdc);
1134
1135                         DestroyWindow(hWnd);
1136                 }
1137
1138                 break;
1139
1140         case WM_DESTROY:
1141
1142                 PostQuitMessage(0);
1143                 break;
1144
1145         case WM_NCLBUTTONDOWN:
1146
1147                 if (wParam == HTCAPTION)
1148                 {
1149                         NCMouseDown = true;
1150                         GetWindowRect(hMainWnd, &rc1);
1151                         GetWindowRect(hCharWnd, &rc2);
1152                         ptWinOffset.x = rc2.left - rc1.left;
1153                         ptWinOffset.y = rc2.top - rc1.top;
1154                 }
1155
1156                 // Let Windows do its thing with this msg, or weird things will happen...
1157
1158                 DefWindowProc(hWnd, msgID, wParam, lParam);
1159                 NCMouseDown = false;
1160                 break;
1161
1162         case WM_WINDOWPOSCHANGING:
1163
1164                 if (NCMouseDown)
1165                 {
1166                         WINDOWPOS * wp = (WINDOWPOS *)lParam;
1167
1168                         if (wp->hwnd == hMainWnd && !(wp->flags & SWP_NOMOVE))
1169                                 SetWindowPos(hCharWnd, 0, wp->x + ptWinOffset.x, wp->y + ptWinOffset.y,
1170                                                 0, 0, SWP_NOZORDER | SWP_NOSIZE | SWP_NOACTIVATE);
1171                 }
1172
1173                 return DefWindowProc(hWnd, msgID, wParam, lParam);      // Seems this is needed... Bleah!
1174
1175         case WM_PAINT:
1176         {
1177                 hdc = BeginPaint(hWnd, &ps);
1178
1179 // Scrolling can be done by using OffsetViewportOrgEx
1180 // Scaling can be done by adjusting SetWindowExtEx (it's denominator of txform)
1181 // you'd use: % = ViewportExt / WindowExt
1182 // But it makes the window look like crap: fuggetuboutit.
1183 // Instead, we have to scale EVERYTHING by hand. Crap!
1184
1185                 // Apparently, you *must* save the individual object types (pen, brush, etc.)
1186
1187                 HGDIOBJ oldPen = SelectObject(hdc, hBluePen1),
1188                         oldBrush = SelectObject(hdc, hNullBrush);
1189
1190             // Draw coordinate axes
1191
1192                 MoveToEx(hdc, 0, -32000, NULL);
1193                 LineTo(hdc, 0, 32000);
1194                 MoveToEx(hdc, -32000, 0, NULL);
1195                 LineTo(hdc, 32000, 0);
1196
1197             // Draw points
1198
1199                 for(int i=0; i<pts.GetNumPoints(); i++)
1200                 {
1201                         if (i == ptHighlight)
1202                         {
1203                                 SelectObject(hdc, hRedPen1);
1204
1205                                 if (pts.GetOnCurve(i))
1206                                 {
1207                                         DrawSquareDotN(hdc, pts.GetX(i), pts.GetY(i), 7);
1208                                         DrawSquareDotN(hdc, pts.GetX(i), pts.GetY(i), 9);
1209                                 }
1210                                 else
1211                                 {
1212                                         DrawRoundDotN(hdc, pts.GetX(i), pts.GetY(i), 7);
1213                                         DrawRoundDotN(hdc, pts.GetX(i), pts.GetY(i), 9);
1214                                 }
1215                         }
1216                         else if ((i == ptHighlight || i == ptNextHighlight) && currentTool == TOOLAddPt)
1217                         {
1218                                 SelectObject(hdc, hGreenPen1);
1219
1220                                 if (pts.GetOnCurve(i))
1221                                 {
1222                                         DrawSquareDotN(hdc, pts.GetX(i), pts.GetY(i), 7);
1223                                         DrawSquareDotN(hdc, pts.GetX(i), pts.GetY(i), 9);
1224                                 }
1225                                 else
1226                                 {
1227                                         DrawRoundDotN(hdc, pts.GetX(i), pts.GetY(i), 7);
1228                                         DrawRoundDotN(hdc, pts.GetX(i), pts.GetY(i), 9);
1229                                 }
1230                         }
1231                         else
1232                         {
1233                                 SelectObject(hdc, hBlackPen1);
1234
1235                                 if (pts.GetOnCurve(i))
1236                                         DrawSquareDot(hdc, pts.GetX(i), pts.GetY(i));
1237                                 else
1238                                         DrawRoundDot(hdc, pts.GetX(i), pts.GetY(i));
1239                         }
1240
1241                         if (currentTool == TOOLDelPt && i == ptHighlight)
1242                         {
1243                                 SelectObject(hdc, hRedPen1);
1244                                 MoveToEx(hdc, pts.GetX(i) - 5, pts.GetY(i) - 5, NULL);
1245                                 LineTo(hdc, pts.GetX(i) + 5, pts.GetY(i) + 5);
1246                                 LineTo(hdc, pts.GetX(i) - 5, pts.GetY(i) - 5);//Lameness!
1247                                 MoveToEx(hdc, pts.GetX(i) - 5, pts.GetY(i) + 5, NULL);
1248                                 LineTo(hdc, pts.GetX(i) + 5, pts.GetY(i) - 5);
1249                                 LineTo(hdc, pts.GetX(i) - 5, pts.GetY(i) + 5);//More lameness!!
1250                         }
1251                 }
1252
1253                 SelectObject(hdc, hBlackPen1);
1254
1255                 // Draw curve formed by points
1256
1257                 for(int poly=0; poly<pts.GetNumPolys(); poly++)
1258                 {
1259                         if (pts.GetNumPoints(poly) > 2)
1260                         {
1261                                 // Initial move...
1262                                 // If it's not on curve, then move to it, otherwise move to last point...
1263
1264                                 if (pts.GetOnCurve(poly, pts.GetNumPoints(poly) - 1))
1265                                         MoveToEx(hdc, pts.GetX(poly, pts.GetNumPoints(poly) - 1), pts.GetY(poly, pts.GetNumPoints(poly) - 1), NULL);
1266                                 else
1267                                         MoveToEx(hdc, pts.GetX(poly, 0), pts.GetY(poly, 0), NULL);
1268
1269                                 for(int i=0; i<pts.GetNumPoints(poly); i++)
1270                                 {
1271                                         if (pts.GetOnCurve(poly, i))
1272                                                 LineTo(hdc, pts.GetX(poly, i), pts.GetY(poly, i));
1273                                         else
1274                                         {
1275                                                 uint32 prev = pts.GetPrev(poly, i), next = pts.GetNext(poly, i);
1276                                                 float px = pts.GetX(poly, prev), py = pts.GetY(poly, prev),
1277                                                         nx = pts.GetX(poly, next), ny = pts.GetY(poly, next);
1278
1279                                                 if (!pts.GetOnCurve(poly, prev))
1280                                                         px = (px + pts.GetX(poly, i)) / 2.0f,
1281                                                         py = (py + pts.GetY(poly, i)) / 2.0f;
1282
1283                                                 if (!pts.GetOnCurve(poly, next))
1284                                                         nx = (nx + pts.GetX(poly, i)) / 2.0f,
1285                                                         ny = (ny + pts.GetY(poly, i)) / 2.0f;
1286
1287                                                 Bezier(hdc, point(px, py), point(pts.GetX(poly, i), pts.GetY(poly, i)), point(nx, ny));
1288
1289                                                 if (pts.GetOnCurve(poly, next))
1290                                                         i++;                                    // Following point is on curve, so move past it
1291                                         }
1292                                 }
1293                         }
1294                 }
1295
1296                 SelectObject(hdc, oldPen);                              // Restore the stuff we disrupted...
1297                 SelectObject(hdc, oldBrush);
1298                 EndPaint(hWnd, &ps);
1299                 break;
1300         }
1301         case WM_SIZE:
1302
1303                 // Apparently this is needed since these windows don't update themselves.
1304                 SendMessage(hStatusBar, msgID, wParam, lParam);
1305                 SendMessage(hToolBar, msgID, wParam, lParam);
1306
1307                 // This is needed to make the 2nd status pane visible
1308                 GetClientRect(hWnd, &rc1);
1309                 pt.x = rc1.right - zoomWndWidth, pt.y = -1;
1310                 SendMessage(hStatusBar, SB_SETPARTS, 2, (LPARAM)&pt);
1311                 break;
1312
1313         case WM_RBUTTONDOWN:
1314
1315                 GetCursorPos(&pt);
1316                 SetWindowPos(hToolPalWnd, 0, pt.x, pt.y, 0, 0, SWP_NOZORDER | SWP_NOSIZE | SWP_SHOWWINDOW);
1317                 SetFocus(hToolPalWnd);
1318                 SetCapture(hToolPalWnd);                                // Ensure tool palette gets RButtonUp
1319                 SetCursor(LoadCursor(NULL, MAKEINTRESOURCE(IDC_ARROW)));        // Tool pallete has "regular" cursor
1320                 break;
1321
1322         case WM_LBUTTONDOWN:
1323
1324                 mouseDown = true;
1325
1326                 if (currentTool == TOOLScroll || currentTool == TOOLZoom)
1327                         SetCapture(hWnd);                                       // Make sure we capture the mouse when in scroll/zoom mode
1328                 else if (currentTool == TOOLAddPt)              // "Add Point" tool
1329                 {
1330                         if (pts.GetNumPoints() > 0)
1331                         {
1332 //Do we really need to put a cap on this???
1333 //Maybe...
1334 //                              if (pts.GetNumPoints() < 16)
1335 //                              {
1336                                 pt.x = lParam & 0xFFFF, pt.y = lParam >> 16;
1337                                 hdc = GetDC(hWnd);
1338                                 DPtoLP(hdc, &pt, 1);
1339                                 pts.InsertPoint(pts.GetNext(ptHighlight), pt.x, pt.y, (wParam & (MK_SHIFT | MK_CONTROL) ? false : true));
1340                                 ptHighlight = ptNextHighlight;
1341                                 ReleaseDC(hWnd, hdc);
1342                                 InvalidateRect(hWnd, NULL, TRUE);
1343 //                              }
1344                         }
1345                 }
1346                 else if (currentTool == TOOLAddPoly)    // "Add Poly" tool
1347                 {
1348 #ifdef DEBUGFOO
1349 wsprintf(strBuf, "Adding point... # polys: %u, # points: %u", pts.GetNumPolys(), pts.GetNumPoints());
1350 WriteLogMsg(strBuf);
1351 #endif
1352                         if (polyFirstPoint)
1353                         {
1354                                 polyFirstPoint = false;
1355                                 pts.AddNewPolyAtEnd();
1356                         }
1357
1358 //Do we really need to put a cap on this???
1359 //Maybe...
1360 //                      if (pts.GetNumPoints() < 16)
1361 //                      {
1362                         pt.x = lParam & 0xFFFF, pt.y = lParam >> 16;
1363                         hdc = GetDC(hWnd);
1364                         DPtoLP(hdc, &pt, 1);
1365                         ReleaseDC(hWnd, hdc);
1366                         // Append a point to the end of the structure
1367                         pts += IPoint(pt.x, pt.y, (wParam & (MK_SHIFT | MK_CONTROL) ? false : true));
1368 ptHighlight = pts.GetNumPoints() - 1;
1369                         InvalidateRect(hWnd, NULL, TRUE);
1370 //                      }
1371 #ifdef DEBUGFOO
1372 wsprintf(strBuf, " --> [# polys: %u, # points: %u]\xD\xA", pts.GetNumPolys(), pts.GetNumPoints());
1373 WriteLogMsg(strBuf);
1374 #endif
1375                 }
1376                 else if (currentTool == TOOLSelect || currentTool == TOOLPolySelect)
1377                 {
1378                         if (pts.GetNumPoints() > 0)
1379                         {
1380                                 pt.x = pts.GetX(ptHighlight), pt.y = pts.GetY(ptHighlight);
1381                                 hdc = GetDC(hWnd);
1382                                 LPtoDP(hdc, &pt, 1);
1383                                 ClientToScreen(hWnd, &pt);
1384                                 SetCursorPos(pt.x, pt.y);
1385                                 ReleaseDC(hWnd, hdc);
1386
1387                                 if (wParam & (MK_SHIFT | MK_CONTROL))
1388                                         pts.SetOnCurve(ptHighlight, !pts.GetOnCurve(ptHighlight));
1389                         }
1390                 }
1391                 else if (currentTool == TOOLDelPt)
1392                 {
1393                         if (pts.GetNumPoints() > 0)
1394 //Or could use:
1395 //                      if (ptHighlight != -1)
1396                         {
1397 //This assumes that WM_MOUSEMOVE happens before this!
1398 //The above commented out line should take care of this contingency... !!! FIX !!!
1399                                 pts.DeletePoint(ptHighlight);
1400                                 InvalidateRect(hWnd, NULL, TRUE);
1401                         }
1402                 }
1403
1404                 break;
1405
1406         case WM_LBUTTONUP:
1407
1408                 mouseDown = false;
1409
1410                 if (currentTool == TOOLScroll || currentTool == TOOLZoom)
1411                         ReleaseCapture();
1412
1413                 break;
1414
1415         case WM_MOUSEMOVE:
1416
1417                 SetCursor(hCur[currentTool]);
1418
1419             // Extract current point from lParam/calc offset from previous point
1420
1421                 pt.x = lParam & 0xFFFF, pt.y = lParam >> 16;
1422                 ptOffset.x = pt.x - ptPrevious.x,
1423                 ptOffset.y = pt.y - ptPrevious.y;
1424
1425                 if (mouseDown)
1426                 {
1427                         if (currentTool == TOOLScroll)
1428                         {
1429                                 // NOTE: OffsetViewportOrg operates in DEVICE UNITS...
1430
1431                                 hdc = GetDC(hWnd);
1432                                 OffsetViewportOrgEx(hdc, ptOffset.x, ptOffset.y, NULL);
1433                                 ReleaseDC(hWnd, hdc);
1434
1435 // this shows that it works, so the logic above must be faulty...
1436 // And it is. It should convert the coords first, then do the subtraction to figure the offset...
1437 // Above: DONE
1438 // Then multiply it by the scaling factor. Whee!
1439
1440                                 InvalidateRect(hWnd, NULL, TRUE);
1441 //                              SendMessage(hWnd, WM_PAINT, NULL, NULL);
1442                         }
1443                         else if (currentTool == TOOLAddPt || currentTool == TOOLAddPoly || currentTool == TOOLSelect)
1444                         {
1445                                 if (currentTool != TOOLAddPt || pts.GetNumPoints() > 0)//yecch.
1446                                 {
1447                                         POINT pt2;
1448                                         pt2.x = pt.x, pt2.y = pt.y;
1449                                         // Should also set onCurve here as well, depending on keystate
1450 //Or should we?
1451                                         hdc = GetDC(hWnd);
1452                                         DPtoLP(hdc, &pt2, 1);
1453                                         pts.SetXY(ptHighlight, pt2.x, pt2.y);
1454                                         ReleaseDC(hWnd, hdc);
1455                                         InvalidateRect(hWnd, NULL, TRUE);
1456                                 }
1457                         }
1458                         else if (currentTool == TOOLPolySelect)
1459                         {
1460                                 if (pts.GetNumPoints() > 0)
1461                                 {
1462                                         POINT pt2;
1463                                         pt2.x = pt.x, pt2.y = pt.y;
1464                                         // Should also set onCurve here as well, depending on keystate
1465 //Or should we?
1466                                         hdc = GetDC(hWnd);
1467                                         DPtoLP(hdc, &pt2, 1);
1468                                         pts.OffsetPoly(pts.GetPoly(ptHighlight), pt2.x - pts.GetX(ptHighlight), pt2.y - pts.GetY(ptHighlight));
1469                                         ReleaseDC(hWnd, hdc);
1470                                         InvalidateRect(hWnd, NULL, TRUE);
1471                                 }
1472                         }
1473                 }
1474                 else
1475                 {
1476                         if (currentTool == TOOLSelect || currentTool == TOOLDelPt || currentTool == TOOLAddPt
1477                                 || currentTool == TOOLPolySelect)// || currentTool == TOOLAddPoly)
1478                         {
1479                                 POINT pt2;
1480                                 pt2.x = pt.x, pt2.y = pt.y;
1481                                 hdc = GetDC(hWnd);
1482                                 DPtoLP(hdc, &pt2, 1);
1483                                 ReleaseDC(hWnd, hdc);
1484
1485                                 double closest = 1.0e+99;
1486
1487                                 for(int i=0; i<pts.GetNumPoints(); i++)
1488                                 {
1489                                         double dist = ((pt2.x - pts.GetX(i)) * (pt2.x - pts.GetX(i)))
1490                                                 + ((pt2.y - pts.GetY(i)) * (pt2.y - pts.GetY(i)));
1491
1492                                         if (dist < closest)
1493                                                 closest = dist, ptHighlight = i;
1494                                 }
1495
1496                                 if (ptHighlight != oldPtHighlight)
1497                                 {
1498                                         oldPtHighlight = ptHighlight;
1499                                         InvalidateRect(hWnd, NULL, TRUE);
1500                                 }
1501
1502                                 // What follows here looks like voodoo, but is really simple. What we do is
1503                                 // check to see if the mouse point has a perpendicular intersection with any of
1504                                 // the line segments. If it does, calculate the length of the perpendicular
1505                                 // and choose the smallest length. If there is no perpendicular, then choose the
1506                                 // length of line connecting the closer of either the first endpoint or the
1507                                 // second and choose the smallest of those.
1508
1509                                 // There is one bit of math that looks like voodoo to me ATM--will explain once
1510                                 // I understand it better (the calculation of the length of the perpendicular).
1511
1512                                 if (pts.GetNumPoints() > 1 && currentTool == TOOLAddPt)
1513                                 {
1514                                         double smallest = 1.0e+99;
1515
1516                                         for(int i=0; i<pts.GetNumPoints(); i++)
1517                                         {
1518                                                 int32 p1x = pts.GetX(i), p1y = pts.GetY(i),
1519                                                         p2x = pts.GetX(pts.GetNext(i)), p2y = pts.GetY(pts.GetNext(i));
1520
1521                                                 vector ls(p2x, p2y, 0, p1x, p1y, 0), v1(pt2.x, pt2.y, 0, p1x, p1y, 0),
1522                                                         v2(pt2.x, pt2.y, 0, p2x, p2y, 0);
1523                                                 double pp = ls.dot(v1) / ls.length(), dist;
1524 // Geometric interpretation:
1525 // pp is the paremeterized point on the vector ls where the perpendicular intersects ls.
1526 // If pp < 0, then the perpendicular lies beyond the 1st endpoint. If pp > length of ls,
1527 // then the perpendicular lies beyond the 2nd endpoint.
1528
1529                                                 if (pp < 0.0)
1530                                                         dist = v1.length();
1531                                                 else if (pp > ls.length())
1532                                                         dist = v2.length();
1533                                                 else                                    // distance = ?Det?(ls, v1) / |ls|
1534                                                         dist = abs((ls.x * v1.y - v1.x * ls.y) / ls.length());
1535
1536 //The answer to the above looks like it might be found here:
1537 //
1538 //If the segment endpoints are s and e, and the point is p, then the test for the perpendicular
1539 //intercepting the segment is equivalent to insisting that the two dot products {s-e}.{s-p} and
1540 //{e-s}.{e-p} are both non-negative.  Perpendicular distance from the point to the segment is
1541 //computed by first computing the area of the triangle the three points form, then dividing by the
1542 //length of the segment.  Distances are done just by the Pythagorean theorem.  Twice the area of the
1543 //triangle formed by three points is the determinant of the following matrix:
1544 //
1545 //sx sy 1
1546 //ex ey 1
1547 //px py 1
1548 //
1549 //(???) By translating the start point to the origin, this can be rewritten as:
1550 //By subtracting row 1 from all rows, you get the following:
1551 //
1552 //0         0         0
1553 //(ex - sx) (ey - sy) 0
1554 //(px - sx) (py - sy) 0
1555 //
1556 //which greatly simplifies the calculation of the determinant.
1557
1558                                                 if (dist < smallest)
1559                                                         smallest = dist, ptNextHighlight = pts.GetNext(i), ptHighlight = i;
1560                                         }
1561
1562                                         if (ptNextHighlight != oldPtNextHighlight)
1563                                         {
1564                                                 oldPtNextHighlight = ptNextHighlight;
1565                                                 InvalidateRect(hWnd, NULL, TRUE);
1566                                         }
1567                                 }
1568                         }
1569                 }
1570
1571                 ptPrevious.x = pt.x, ptPrevious.y = pt.y;
1572
1573                 break;
1574
1575         case WM_NOTIFY:
1576
1577                 if (((NMHDR *)lParam)->code == TTN_NEEDTEXT)
1578                 {
1579                         LoadString(hInst, ((TOOLTIPTEXT *)lParam)->hdr.idFrom + 0x80, toolTipTxt, 16);
1580                         ((TOOLTIPTEXT *)lParam)->lpszText = toolTipTxt;
1581                 }
1582
1583                 break;
1584
1585         case WM_MENUSELECT:
1586         {
1587                 statusBarTxt[0] = 0;                                    // Clear status bar text
1588                 uint16 flags = wParam >> 16;                    // Extract flags
1589
1590                 if (!(flags & MFT_SEPARATOR))
1591                 {
1592                         uint16 id = wParam & 0xFFFF;
1593
1594                         if (flags & MF_POPUP)
1595                         {
1596                                 if (flags & MF_SYSMENU)
1597                                         id = IDS_SYSMENU;
1598                                 else
1599                                         id = IDM_FILEMENU + wParam;
1600                         }
1601
1602                         LoadString(hInst, id, statusBarTxt, 64);
1603                 }
1604
1605                 SendMessage(hStatusBar, SB_SETTEXT, 0 + SBT_NOBORDERS, (LPARAM)statusBarTxt);
1606                 break;
1607         }
1608         case WM_COMMAND:
1609         {
1610                 uint16 cmd = wParam & 0xFFFF;
1611
1612                 if (cmd == IDM_NEW)
1613                 {
1614 //                    call   CmdIDM_NEW
1615                 }
1616                 else if (cmd == IDM_OPEN)
1617                 {
1618 //                    call   SaveChanges
1619 //                    .IF (eax)
1620 //                      movmov ofn.hwndOwner, eax, hMainWnd
1621 //                      mov    ofn.Flags, OFN_PATHMUSTEXIST + OFN_FILEMUSTEXIST
1622 //                      invoke GetOpenFileName, ADDR ofn
1623 //                      .IF (eax)
1624 ////////
1625 //jmp @F
1626 //szDMsg1a      BYTE    "Could not open the file (GetOpenFileName)...", 0
1627 //szDMsg1b      BYTE    "Open error!", 0
1628 //szDMsg1c      BYTE    "About to attempt to open file...", 0
1629 //@@:
1630 ////invoke MessageBox, hWnd, ADDR szDMsg1a, ADDR szDMsg1b, MB_ICONERROR or MB_OK
1631 //invoke MessageBox, hMainWnd, ADDR szDMsg1c, ADDR szFile, MB_ICONERROR or MB_OK
1632 //                        invoke LoadTTF, ADDR szFile
1633 //
1634 //////
1635 //                        // <<< FILE OPEN CODE HERE >>>
1636 //                        or     fFileStatus, NAMEDbit
1637 //                        and    fFileStatus, NOT CHANGEDbit
1638 //                        call   NewWindowName
1639 //                        mov    eax, TRUE      // return TRUE
1640 //                        jmp    Return
1641 //                        //��������������������������������������
1642 //OpenError:              invoke GetLastError
1643 //                        // <<< FILE OPEN ERROR CODE HERE >>>
1644 //                      .ENDIF
1645 //                      zero   eax      // return FALSE
1646 //                    .ENDIF
1647                 }
1648                 else if (cmd == IDM_SAVEAS)
1649                 {
1650 //                    and    fFileStatus, NOT NAMEDbit
1651 //                    call   CmdIDM_SAVE
1652                 }
1653                 else if (cmd == IDM_SAVE)
1654                 {
1655 //                    call   CmdIDM_SAVE
1656                 }
1657                 else if (cmd == IDM_ABOUT)
1658                         DialogBoxParam(hInst, MAKEINTRESOURCE(IDD_ABOUT), hMainWnd, AboutProc, NULL);
1659                 else if (cmd == IDM_EXIT)
1660                         SendMessage(hWnd, WM_CLOSE, 0, 0);
1661                 else if (cmd == ID_TBCHARWIN)
1662                 {
1663                         ShowWindow(hCharWnd, (IsWindowVisible(hCharWnd) ? SW_HIDE : SW_SHOWNOACTIVATE));
1664
1665 #ifdef DEBUGFOO
1666 wpC.length = sizeof(WINDOWPLACEMENT);
1667 GetWindowPlacement(hCharWnd, &wpC);
1668 wsprintf(strBuf, "Char window showCmd = %08X...\n", wpC.showCmd);
1669 WriteLogMsg(strBuf);
1670 #endif
1671                 }
1672                 else
1673                         return DefWindowProc(hWnd, msgID, wParam, lParam);
1674
1675                 break;
1676         }
1677         default:
1678                 return DefWindowProc(hWnd, msgID, wParam, lParam);
1679         }
1680
1681         return 0;
1682 }
1683
1684 //
1685 // Initialize TTF data
1686 //
1687 void CreateNewDoc(void)
1688 {
1689 }
1690
1691 //
1692 // Save changes to document before quitting
1693 //
1694 bool SaveChanges(void)
1695 {
1696         return true;
1697 }
1698
1699
1700
1701 //
1702 // ABOUT Dialog WndProc
1703 //
1704 BOOL CALLBACK AboutProc(HWND hDlg, UINT msgID, WPARAM wParam, LPARAM lParam)
1705 {
1706         switch (msgID)
1707         {
1708         case WM_INITDIALOG:
1709
1710                 MiscCenterWnd(hDlg, hMainWnd);
1711                 break;
1712
1713         case WM_COMMAND:
1714
1715                 if (wParam == IDOK || wParam == IDCANCEL)
1716                         EndDialog(hDlg, TRUE);
1717
1718                 break;
1719
1720         default:
1721                 return FALSE;
1722         }
1723
1724         return TRUE;
1725 }
1726
1727 //
1728 // Character Window WndProc
1729 //
1730 WndProcCW       PROC  STDCALL, hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM
1731
1732                 mov    eax, uMsg                // pickup our message
1733                 .IF (eax == WM_PAINT)
1734                   mov    eax, 0                 // Non-sense... (placeholder!)
1735 // Scan conversion etc. goes here...
1736                 .ELSEIF (eax == WM_LBUTTONDOWN)
1737                   invoke SetCapture, hCharWnd
1738                 .ELSEIF (eax == WM_LBUTTONUP)
1739                   invoke ReleaseCapture
1740                   invoke SetFocus, hMainWnd     // Make sure the main wnd keeps focus!
1741                 .ELSEIF (eax == WM_NCLBUTTONDOWN)
1742                   invoke DefWindowProc, hWnd, uMsg, wParam, lParam // Let it do its thing
1743                   invoke SetFocus, hMainWnd     // Make sure the main wnd keeps focus!
1744                 .ELSE
1745 DefProc:          invoke DefWindowProc, hWnd, uMsg, wParam, lParam
1746                 .ENDIF
1747                 ret
1748
1749 WndProcCW       ENDP
1750
1751 //
1752 // Character Window WndProc
1753 //
1754 LRESULT CALLBACK WndProcCW(HWND hWnd, UINT msgID, WPARAM wParam, LPARAM lParam)
1755 {
1756         switch (msgID)
1757         {
1758         default:
1759                 return DefWindowProc(hWnd, msgID, wParam, lParam);
1760         }
1761
1762         return 0;
1763 }
1764
1765
1766 // Function prototypes
1767
1768 int32 FindSelectedTool(void);
1769
1770 //
1771 // Tool Palette WndProc
1772 //
1773 LRESULT CALLBACK WndProcTP(HWND hWnd, UINT msgID, WPARAM wParam, LPARAM lParam)
1774 {
1775         PAINTSTRUCT ps;
1776         HDC hdc;
1777         POINT pt;
1778         static uint32 prevTool = -1;
1779
1780         switch (msgID)
1781         {
1782         case WM_PAINT:
1783         {
1784                 hdc = BeginPaint(hWnd, &ps);
1785                 HDC newDC = CreateCompatibleDC(NULL);
1786                 SelectObject(newDC, hBMToolPal1);
1787                 BitBlt(hdc, 0, 0, sizeTPBM.x, sizeTPBM.y, newDC, 0, 0, SRCCOPY);
1788                 DeleteDC(newDC);
1789
1790 // This is crappy. Find some way to tighten this up!
1791                 int32 tool = FindSelectedTool();
1792
1793                 if (tool != -1)
1794                 {
1795                         newDC = CreateCompatibleDC(NULL);
1796                         SelectObject(newDC, hBMToolPal1);
1797                     //need ul corner of bitmap, ul corner of dest, width/height
1798                         pt.x = sizeStamp.x * (tool & 0x03), pt.y = sizeStamp.y * (tool >> 2);
1799                         BitBlt(hdc, pt.x, pt.y, sizeStamp.x, sizeStamp.y, newDC, pt.x, pt.y, NOTSRCCOPY);
1800                         DeleteDC(newDC);
1801                 }
1802
1803                 EndPaint(hWnd, &ps);
1804                 break;
1805         }
1806         case WM_MOUSEMOVE:
1807         {
1808                 int32 tool = FindSelectedTool();
1809
1810                 if (tool != prevTool)
1811                 {
1812                         prevTool = tool;
1813                         InvalidateRect(hWnd, NULL, FALSE);
1814                 }
1815
1816                 break;
1817         }
1818         case WM_RBUTTONUP:
1819         {
1820                 int32 tool = FindSelectedTool(), oldTool = currentTool;
1821
1822                 if (tool != -1)
1823                         currentTool = tool;
1824
1825                 if (currentTool != TOOLSelect && currentTool != TOOLDelPt && currentTool != TOOLAddPt
1826                         && currentTool != TOOLPolySelect)
1827                         ptHighlight = -1;
1828
1829                 if (currentTool != oldTool)
1830                         InvalidateRect(hMainWnd, NULL, TRUE);
1831
1832                 if (currentTool == TOOLAddPoly)
1833 #ifdef DEBUGFOO
1834 {
1835 #endif
1836                         polyFirstPoint = true;
1837 #ifdef DEBUGFOO
1838 wsprintf(strBuf, "--> Selected poly tool, polyFirstPoint is %s\n", polyFirstPoint ? "true" : "false");
1839 WriteLogMsg(strBuf);
1840 }
1841 #endif
1842
1843                 ReleaseCapture();
1844                 ShowWindow(hToolPalWnd, SW_HIDE);
1845                 SetFocus(hMainWnd);                                             // Make sure the main wnd keeps focus!
1846
1847                 break;
1848         }
1849         default:
1850                 return DefWindowProc(hWnd, msgID, wParam, lParam);
1851         }
1852
1853         return 0;
1854 }
1855
1856 //
1857 // Find which tool we're pointing at
1858 // Use: xcoord = mouse.x / (bmsize.x/4), ycoord = mouse.y / (bmsize.y/2)
1859 //
1860 int32 FindSelectedTool(void)
1861 {
1862         POINT pt;
1863
1864         GetCursorPos(&pt);
1865         ScreenToClient(hToolPalWnd, &pt);
1866
1867         uint32 x = (uint32)pt.x / sizeStamp.x, y = (uint32)pt.y / sizeStamp.y, tool = -1;
1868
1869         if (x < 4 && y < 2)
1870 //      {
1871                 tool = (y * 4) + x;
1872
1873 //              if (tool == 7)
1874 //                      tool = -1;                                                      // 7 has no tool...
1875 //      }
1876
1877         return tool;
1878 }
1879
1880
1881 //
1882 // Misc center window
1883 //
1884 void MiscCenterWnd(HWND hChild, HWND hParent)
1885 {
1886         RECT parent, child;
1887
1888         if (!GetWindowRect(hParent, &parent) || !GetWindowRect(hChild, &child))
1889                 return;
1890
1891         int32 x = parent.left + (((parent.right - parent.left) - (child.right - child.left)) / 2),
1892                 y = parent.top + (((parent.bottom - parent.top) - (child.bottom - child.top)) / 2);
1893
1894         if (x < 0)
1895                 x = 0;
1896         else if (x > GetSystemMetrics(SM_CXFULLSCREEN) - (child.right - child.left))
1897                 x = GetSystemMetrics(SM_CXFULLSCREEN) - (child.right - child.left);
1898
1899         if (y < 0)
1900                 y = 0;
1901         else if (y > GetSystemMetrics(SM_CYFULLSCREEN) - (child.bottom - child.top))
1902                 y = GetSystemMetrics(SM_CYFULLSCREEN) - (child.bottom - child.top);
1903
1904         SetWindowPos(hChild, NULL, x, y, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
1905 }
1906
1907 //
1908 // Allow only one instance
1909 //
1910 bool OnlyOneInstance(void)
1911 {
1912         HWND window = FindWindow(className, NULL);
1913
1914         if (window == NULL)
1915                 return true;
1916
1917         ShowWindow(window, SW_SHOWNORMAL);
1918         SetWindowPos(window, NULL, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE);
1919
1920         return false;
1921 }
1922
1923 //
1924 // Load/Allocate all resources
1925 //
1926 bool LoadResources(void)
1927 {
1928         hCur[0] = LoadCursor(hInst, MAKEINTRESOURCE(IDC_CURSOR1));
1929         hCur[1] = LoadCursor(hInst, MAKEINTRESOURCE(IDC_CURSOR2));
1930         hCur[2] = LoadCursor(hInst, MAKEINTRESOURCE(IDC_CURSOR3));
1931         hCur[3] = LoadCursor(hInst, MAKEINTRESOURCE(IDC_CURSOR4));
1932         hCur[4] = LoadCursor(hInst, MAKEINTRESOURCE(IDC_CURSOR5));
1933         hCur[5] = LoadCursor(hInst, MAKEINTRESOURCE(IDC_CURSOR6));
1934         hCur[6] = LoadCursor(hInst, MAKEINTRESOURCE(IDC_CURSOR7));
1935         hCur[7] = LoadCursor(hInst, MAKEINTRESOURCE(IDC_CURSOR8));
1936
1937         BITMAP bm;
1938
1939         hBMToolPal1 = LoadBitmap(hInst, MAKEINTRESOURCE(IDB_TOOLPAL1));
1940         GetObject(hBMToolPal1, sizeof(bm), &bm);
1941
1942         // Set up sizes
1943
1944         sizeTPBM.x = bm.bmWidth, sizeTPBM.y = bm.bmHeight;
1945         sizeStamp.x = bm.bmWidth / 4, sizeStamp.y = bm.bmHeight / 2;
1946
1947         hBluePen1 = CreatePen(PS_DOT, 1, 0x00FF0000);
1948         hRedPen1 = CreatePen(PS_SOLID, 1, 0x000000FF);
1949         hGreenPen1 = CreatePen(PS_SOLID, 1, 0x0000AF00);
1950         hBlackPen1 = CreatePen(PS_SOLID, 1, 0x00000000);
1951
1952         LOGBRUSH lb = { BS_NULL, 0, 0 };
1953
1954         hNullBrush = CreateBrushIndirect(&lb);
1955
1956         return true;
1957 }
1958
1959 //
1960 // Deallocate all resources
1961 //
1962 void DeallocateResources(void)
1963 {
1964         DeleteObject(hBMToolPal1);
1965         DeleteObject(hBluePen1);
1966         DeleteObject(hRedPen1);
1967         DeleteObject(hGreenPen1);
1968         DeleteObject(hBlackPen1);
1969         DeleteObject(hNullBrush);
1970 }
1971
1972 //
1973 // Save all application specific data, so we can pick up where we last left off...
1974 //
1975 void SaveAppState(void)
1976 {
1977         SetINIInt("Main", "flags", wpM.flags);
1978         SetINIInt("Main", "showCmd", wpM.showCmd);
1979         SetINIInt("Main", "x1", wpM.rcNormalPosition.left);
1980         SetINIInt("Main", "y1", wpM.rcNormalPosition.top);
1981         SetINIInt("Main", "x2", wpM.rcNormalPosition.right);
1982         SetINIInt("Main", "y2", wpM.rcNormalPosition.bottom);
1983
1984         SetINIInt("Main", "vpx", ptVPM.x);
1985         SetINIInt("Main", "vpy", ptVPM.y);
1986
1987         SetINIInt("Char", "flags", wpC.flags);
1988         SetINIInt("Char", "showCmd", wpC.showCmd);
1989         SetINIInt("Char", "x1", wpC.rcNormalPosition.left);
1990         SetINIInt("Char", "y1", wpC.rcNormalPosition.top);
1991         SetINIInt("Char", "x2", wpC.rcNormalPosition.right);
1992         SetINIInt("Char", "y2", wpC.rcNormalPosition.bottom);
1993
1994         // Need to write out currently opened font, character looking at, other misc. crap
1995 //      SetINIString("Main", "currentFile", pDoc->GetPathName());
1996 //      SetINIInt("Main", "currentChar", pDoc->character_num);
1997 }
1998
1999 //
2000 // Restore all application specific data previously saved
2001 //
2002 bool RestoreAppState(void)
2003 {
2004         InitINIFile();
2005
2006         WINDOWPLACEMENT wp;
2007         wp.length = sizeof(WINDOWPLACEMENT);
2008         GetWindowPlacement(hMainWnd, &wp);
2009
2010         wp.flags = GetINIInt("Main", "flags", wp.flags);
2011         wp.showCmd = GetINIInt("Main", "showCmd", wp.showCmd);
2012         wp.rcNormalPosition.left = GetINIInt("Main", "x1", wp.rcNormalPosition.left);
2013         wp.rcNormalPosition.top = GetINIInt("Main", "y1", wp.rcNormalPosition.top);
2014         wp.rcNormalPosition.right = GetINIInt("Main", "x2", wp.rcNormalPosition.right);
2015         wp.rcNormalPosition.bottom = GetINIInt("Main", "y2", wp.rcNormalPosition.bottom);
2016
2017         SetWindowPlacement(hMainWnd, &wp);
2018
2019         HDC hdc;
2020         POINT pt;
2021         hdc = GetDC(hMainWnd);
2022         GetViewportOrgEx(hdc, &pt);
2023
2024         pt.x = GetINIInt("Main", "vpx", pt.x);
2025         pt.y = GetINIInt("Main", "vpy", pt.y);
2026
2027         SetViewportOrgEx(hdc, pt.x, pt.y, NULL);
2028         ReleaseDC(hMainWnd, hdc);
2029
2030         GetWindowPlacement(hCharWnd, &wp);
2031
2032         wp.flags = GetINIInt("Char", "flags", wp.flags);
2033         wp.showCmd = GetINIInt("Char", "showCmd", wp.showCmd);
2034         wp.rcNormalPosition.left = GetINIInt("Char", "x1", wp.rcNormalPosition.left);
2035         wp.rcNormalPosition.top = GetINIInt("Char", "y1", wp.rcNormalPosition.top);
2036         wp.rcNormalPosition.right = GetINIInt("Char", "x2", wp.rcNormalPosition.right);
2037         wp.rcNormalPosition.bottom = GetINIInt("Char", "y2", wp.rcNormalPosition.bottom);
2038
2039         SetWindowPlacement(hCharWnd, &wp);
2040
2041         if (wp.showCmd == SW_HIDE)
2042                 SendMessage(hToolBar, TB_SETSTATE, ID_TBCHARWIN, MAKELONG(TBSTATE_ENABLED, 0));
2043
2044 //  CString lastFile = theApplicationObject.GetProfileString(version, "currentFile", "");
2045 //  int lastChar = theApplicationObject.GetProfileInt(version, "currentChar", 0);
2046 //  if (lastFile.GetLength())
2047 //  {
2048 //    // Attempt to restore the last session by open the last file used, etc...
2049 //    if (!pDoc->m_myFont.Load(lastFile))
2050 //    {
2051 //      // Err, make sure you can support any allegations you make below, buddy!
2052 //      AfxMessageBox("The last file opened with TTF Edit\n\rseems to have been moved or deleted.");
2053 //    }
2054 //    else
2055 //    {
2056 //      pDoc->m_myFont.SetGlyph(lastChar);  // Set TTF object to last used char
2057 //      pDoc->character_num = lastChar;
2058 //      pDoc->SetPathName(lastFile);
2059 //
2060 //      BYTE name[512];
2061 //      pDoc->m_myFont.GetCharName(lastChar, name);
2062 //      m_wndOwned.SetWindowText((char *)name);
2063 //    }
2064 //  }
2065
2066         return true;
2067 }
2068
2069 //
2070 // Initialization
2071 //
2072 bool Initialization(void)
2073 {
2074         WNDCLASSEX wcex;
2075
2076         if (!LoadResources())
2077                 return false;
2078
2079         RtlFillMemory(&wcex, sizeof(WNDCLASSEX), 0);
2080         wcex.cbSize = sizeof(WNDCLASSEX);
2081         wcex.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
2082         wcex.lpfnWndProc = WndProc;
2083         wcex.hInstance = hInst;
2084         wcex.hIcon = LoadIcon(hInst, MAKEINTRESOURCE(IDI_ICON));
2085         wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
2086         wcex.lpszMenuName = MAKEINTRESOURCE(IDM_MENU);
2087         wcex.lpszClassName = className;
2088         wcex.hIconSm = (HICON)LoadImage(hInst, MAKEINTRESOURCE(IDI_ICON), IMAGE_ICON, 16, 16, NULL);
2089
2090         if (!RegisterClassEx(&wcex))
2091                 return false;
2092
2093         hMainWnd = CreateWindowEx(NULL, className, className, WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN,
2094                 0, 0, 0x1A0, 0x180, NULL, NULL, hInst, NULL);
2095
2096         if (!hMainWnd)
2097                 return false;
2098
2099         ShowWindow(hMainWnd, nCmdShow);
2100         UpdateWindow(hMainWnd);
2101
2102         // Character window creation
2103
2104         wcex.lpfnWndProc = WndProcCW;
2105         wcex.lpszMenuName = NULL;
2106         wcex.lpszClassName = CNCharWin;
2107         wcex.hCursor = LoadCursor(NULL, IDC_ARROW);     // Owned windows have "regular" cursors
2108
2109         if (!RegisterClassEx(&wcex))
2110                 return false;
2111
2112         hCharWnd = CreateWindowEx(WS_EX_WINDOWEDGE | WS_EX_TOOLWINDOW, CNCharWin,
2113                 curCharName, WS_POPUP | WS_CAPTION | WS_VISIBLE | WS_THICKFRAME,
2114                 100, 100, 120, 120, hMainWnd, NULL, hInst, NULL);
2115
2116         if (!hCharWnd)
2117                 return false;
2118
2119         ShowWindow(hCharWnd, SW_SHOWNORMAL);
2120         UpdateWindow(hCharWnd);
2121         SetFocus(hMainWnd);                                                     // Make sure main wnd has focus!
2122
2123         // Tool palette window creation
2124
2125         wcex.lpfnWndProc = WndProcTP;
2126         wcex.lpszClassName = CNToolPal;
2127
2128         if (!RegisterClassEx(&wcex))
2129                 return false;
2130
2131         hToolPalWnd = CreateWindowEx(WS_EX_WINDOWEDGE, CNToolPal, NULL, WS_POPUP,
2132                 0, 0, sizeTPBM.x, sizeTPBM.y, hMainWnd, NULL, hInst, NULL);
2133
2134         if (!hToolPalWnd)
2135                 return false;
2136
2137 // Note: A better way to handle this would be to have a sub that registers ALL
2138 //       classes beforehand and passes a TRUE/FALSE back depending on whether or not
2139 //       all the classes were able to be registered or not, THEN create the windows
2140 //       and controls...
2141
2142         RestoreAppState();                                                      // Restore app related stuff
2143
2144         return true;
2145 }
2146
2147 #endif
2148 #endif