]> Shamusworld >> Repos - ttedit/blob - src/editwindow.cpp
34b1a06c59c5e15a7065e6d7b6d788d079a06de4
[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 //
12
13 // FIXED:
14 //
15 // STILL TO BE DONE:
16 //
17 // - Fix bug in Glyphpoints when dragging on an empty canvas or loading a font
18 // - Fix scrolling, zooming, settings (ini)
19 //
20
21 // Uncomment this for debugging...
22 #define DEBUG
23 #define DEBUGFOO            // Various tool debugging...
24 #define DEBUGTP                         // Toolpalette debugging...
25
26 #include "editwindow.h"
27 #include "graphicprimitives.h"
28 #include "toolwindow.h"
29 #include "debug.h"
30 #include "vector.h"
31
32
33 BEGIN_EVENT_TABLE(TTEditWindow, wxWindow)
34         EVT_PAINT(TTEditWindow::OnPaint)
35         EVT_MOUSE_EVENTS(TTEditWindow::OnMouseEvent)
36 END_EVENT_TABLE()
37
38 TTEditWindow::TTEditWindow(wxFrame * parent, const wxPoint &pos, const wxSize &size, long style):
39         wxWindow(parent, -1, pos, size, style | wxFULL_REPAINT_ON_RESIZE),
40         app(wxGetApp()), scale(1.0), offsetX(-10), offsetY(-10), tool(TOOLSelect),
41         ptHighlight(-1), oldPtHighlight(-1), ptNextHighlight(-1), oldPtNextHighlight(-1),
42         polyFirstPoint(true), bmp(NULL)
43
44         SetCursor(*(app.cur[tool]));
45         SetBackgroundColour(wxColour(0xFF, 0xFF, 0xFF));
46
47         wxString s;
48         s.Printf(_("Zoom: %.2f%%"), scale * 100.0);
49         parent->SetStatusText(s, 1);
50 }
51
52 TTEditWindow::~TTEditWindow(void)
53 {
54         if (bmp)
55                 delete bmp;
56 }
57
58 void TTEditWindow::OnPaint(wxPaintEvent &e)
59 {
60         wxPaintDC dc(this);
61 //Doesn't do crap!
62 //dc.SetBackground(*wxWHITE_BRUSH);
63
64 // Due to the screwiness of wxWidgets coord system, the origin is ALWAYS
65 // the upper left corner--regardless of axis orientation, etc...
66         wxCoord width, height;
67         dc.GetSize(&width, &height);
68
69         dc.SetDeviceOrigin(-offsetX, height - (-offsetY));
70         dc.SetAxisOrientation(true, true);
71
72 // Scrolling can be done by using OffsetViewportOrgEx
73 // Scaling can be done by adjusting SetWindowExtEx (it's denominator of txform)
74 // you'd use: % = ViewportExt / WindowExt
75 // But it makes the window look like crap: fuggetuboutit.
76 // Instead, we have to scale EVERYTHING by hand. Crap!
77 // It's not *that* bad, but not as convenient either...
78
79         dc.SetPen(*(wxThePenList->FindOrCreatePen(wxColour(0x00, 0x00, 0xFF), 1, wxDOT)));
80 //      dc.DrawLine(0, 0, 10, 10);
81
82     // Draw coordinate axes
83
84         dc.CrossHair(0, 0);
85
86     // Draw points
87
88         for(int i=0; i<pts.GetNumPoints(); i++)
89         {
90                 if (i == ptHighlight)
91                 {
92                         dc.SetPen(*(wxThePenList->FindOrCreatePen(wxColour(0xFF, 0x00, 0x00), 1, wxSOLID)));
93 //                      SelectObject(hdc, hRedPen1);
94
95                         if (pts.GetOnCurve(i))
96                         {
97                                 DrawSquareDotN(dc, pts.GetX(i), pts.GetY(i), 7);
98                                 DrawSquareDotN(dc, pts.GetX(i), pts.GetY(i), 9);
99                         }
100                         else
101                         {
102                                 DrawRoundDotN(dc, pts.GetX(i), pts.GetY(i), 7);
103                                 DrawRoundDotN(dc, pts.GetX(i), pts.GetY(i), 9);
104                         }
105                 }
106                 else if ((i == ptHighlight || i == ptNextHighlight) && tool == TOOLAddPt)
107                 {
108                         dc.SetPen(*(wxThePenList->FindOrCreatePen(wxColour(0x00, 0xAF, 0x00), 1, wxSOLID)));
109 //                      SelectObject(hdc, hGreenPen1);
110
111                         if (pts.GetOnCurve(i))
112                         {
113                                 DrawSquareDotN(dc, pts.GetX(i), pts.GetY(i), 7);
114                                 DrawSquareDotN(dc, pts.GetX(i), pts.GetY(i), 9);
115                         }
116                         else
117                         {
118                                 DrawRoundDotN(dc, pts.GetX(i), pts.GetY(i), 7);
119                                 DrawRoundDotN(dc, pts.GetX(i), pts.GetY(i), 9);
120                         }
121                 }
122                 else
123                 {
124                         dc.SetPen(*(wxThePenList->FindOrCreatePen(wxColour(0x00, 0x00, 0x00), 1, wxSOLID)));
125 //                      SelectObject(hdc, hBlackPen1);
126
127                         if (pts.GetOnCurve(i))
128                                 DrawSquareDot(dc, pts.GetX(i), pts.GetY(i));
129                         else
130                                 DrawRoundDot(dc, pts.GetX(i), pts.GetY(i));
131                 }
132
133                 if (tool == TOOLDelPt && i == ptHighlight)
134                 {
135                         dc.SetPen(*(wxThePenList->FindOrCreatePen(wxColour(0xFF, 0x00, 0x00), 1, wxSOLID)));
136 //                      SelectObject(hdc, hRedPen1);
137 //                      MoveToEx(hdc, pts.GetX(i) - 5, pts.GetY(i) - 5, NULL);
138 //                      LineTo(hdc, pts.GetX(i) + 5, pts.GetY(i) + 5);
139 //                      LineTo(hdc, pts.GetX(i) - 5, pts.GetY(i) - 5);//Lameness!
140 //                      MoveToEx(hdc, pts.GetX(i) - 5, pts.GetY(i) + 5, NULL);
141 //                      LineTo(hdc, pts.GetX(i) + 5, pts.GetY(i) - 5);
142 //                      LineTo(hdc, pts.GetX(i) - 5, pts.GetY(i) + 5);//More lameness!!
143                         dc.DrawLine(pts.GetX(i) - 5, pts.GetY(i) - 5, pts.GetX(i) + 5, pts.GetY(i) + 5);
144                         dc.DrawLine(pts.GetX(i) + 5, pts.GetY(i) - 5, pts.GetX(i) - 5, pts.GetY(i) + 5);
145                 }
146         }
147
148 //              SelectObject(hdc, hBlackPen1);
149         dc.SetPen(*(wxThePenList->FindOrCreatePen(wxColour(0x00, 0x00, 0x00), 1, wxSOLID)));
150
151         // Draw curve formed by points
152
153         for(int poly=0; poly<pts.GetNumPolys(); poly++)
154         {
155                 if (pts.GetNumPoints(poly) > 2)
156                 {
157                         // Initial move...
158                         // If it's not on curve, then move to it, otherwise move to last point...
159
160                         wxCoord x, y;
161         
162                         if (pts.GetOnCurve(poly, pts.GetNumPoints(poly) - 1))
163                                 x = (wxCoord)pts.GetX(poly, pts.GetNumPoints(poly) - 1), y = (wxCoord)pts.GetY(poly, pts.GetNumPoints(poly) - 1);
164                         else
165                                 x = (wxCoord)pts.GetX(poly, 0), y = (wxCoord)pts.GetY(poly, 0);
166         
167                         for(int i=0; i<pts.GetNumPoints(poly); i++)
168                         {
169                                 if (pts.GetOnCurve(poly, i))
170 //                                      LineTo(hdc, pts.GetX(poly, i), pts.GetY(poly, i));
171                                 {
172                                         dc.DrawLine(x, y, pts.GetX(poly, i), pts.GetY(poly, i));
173                                         x = (wxCoord)pts.GetX(poly, i), y = (wxCoord)pts.GetY(poly, i);
174                                 }
175                                 else
176                                 {
177                                         uint32 prev = pts.GetPrev(poly, i), next = pts.GetNext(poly, i);
178                                         float px = pts.GetX(poly, prev), py = pts.GetY(poly, prev),
179                                                 nx = pts.GetX(poly, next), ny = pts.GetY(poly, next);
180         
181                                         if (!pts.GetOnCurve(poly, prev))
182                                                 px = (px + pts.GetX(poly, i)) / 2.0f,
183                                                 py = (py + pts.GetY(poly, i)) / 2.0f;
184         
185                                         if (!pts.GetOnCurve(poly, next))
186                                                 nx = (nx + pts.GetX(poly, i)) / 2.0f,
187                                                 ny = (ny + pts.GetY(poly, i)) / 2.0f;
188         
189                                         Bezier(dc, point(px, py), point(pts.GetX(poly, i), pts.GetY(poly, i)), point(nx, ny));
190                                         x = (wxCoord)nx, y = (wxCoord)ny;
191         
192                                         if (pts.GetOnCurve(poly, next))
193                                                 i++;                                    // Following point is on curve, so move past it
194                                 }
195                         }
196                 }
197         }
198
199 //              SelectObject(hdc, oldPen);                              // Restore the stuff we disrupted...
200         dc.SetPen(wxNullPen);
201 //              SelectObject(hdc, oldBrush);
202 //              EndPaint(hWnd, &ps);
203 }
204
205 void TTEditWindow::OnMouseEvent(wxMouseEvent &e)
206 {
207 #ifdef DEBUGTP
208 //printf("!!! This window %s focus...!\n", (HasCapture() ? "has" : "doesn't have"));
209 #endif
210         if (e.RightDown())
211         {
212                 // Handle tool palette (NOTE: tool palette deals with RightUp event.)
213
214                 wxPoint pt = ClientToScreen(e.GetPosition());
215                 app.toolPalette->Move(pt);
216                 app.toolPalette->Show(true);
217                 SetCursor(*wxSTANDARD_CURSOR);
218                 app.toolPalette->SetCursor(*wxSTANDARD_CURSOR);
219                 app.toolPalette->prevTool = TOOLSelect;
220                 app.toolPalette->Refresh(false);
221                 CaptureMouse();
222         }
223         else if (e.RightUp())
224         {
225                 ToolType newTool = app.toolPalette->FindSelectedTool();//, oldTool = tool;
226
227                 // We only change the tool if a new one was actually selected. Otherwise, we do nothing.
228                 if (newTool != TOOLNone)
229                 {
230                         tool = newTool;
231
232                         if (tool == TOOLScroll || tool == TOOLZoom || tool == TOOLAddPoly
233                                 || tool == TOOLDelPoly)
234                                 ptHighlight = -1;
235
236                         if (tool == TOOLAddPoly)
237                                 polyFirstPoint = true;
238                 }
239
240                 ReleaseMouse();
241                 app.toolPalette->Show(false);
242                 SetCursor(*(app.cur[tool]));
243         }
244         else if (e.LeftDown())
245         {
246                 if (tool == TOOLScroll || tool == TOOLZoom)
247                         CaptureMouse();                                         // Make sure we capture the mouse when in scroll/zoom mode
248                 else if (tool == TOOLAddPt)             // "Add Point" tool
249                 {
250                         if (pts.GetNumPoints() > 0)
251                         {
252                                 wxPoint pt = GetAdjustedMousePosition(e);
253                                 pts.InsertPoint(pts.GetNext(ptHighlight), pt.x, pt.y, (e.ShiftDown() | e.ControlDown() ? false : true));
254                                 ptHighlight = ptNextHighlight;
255                                 Refresh();
256                         }
257                 }
258                 else if (tool == TOOLAddPoly)   // "Add Poly" tool
259                 {
260 #ifdef DEBUGFOO
261 WriteLogMsg("Adding point... # polys: %u, # points: %u", pts.GetNumPolys(), pts.GetNumPoints());
262 #endif
263                         if (polyFirstPoint)
264                         {
265                                 polyFirstPoint = false;
266                                 pts.AddNewPolyAtEnd();
267                         }
268
269                         wxPoint pt = GetAdjustedMousePosition(e);
270                         // Append a point to the end of the structure
271                         pts += IPoint(pt.x, pt.y, (e.ShiftDown() | e.ControlDown() ? false : true));
272                         ptHighlight = pts.GetNumPoints() - 1;
273                         Refresh();
274 #ifdef DEBUGFOO
275 WriteLogMsg(" --> [# polys: %u, # points: %u]\n", pts.GetNumPolys(), pts.GetNumPoints());
276 #endif
277                 }
278                 else if (tool == TOOLSelect || tool == TOOLPolySelect)
279                 {
280                         if (pts.GetNumPoints() > 0)
281                         {
282                                 pt = GetAdjustedClientPosition(pts.GetX(ptHighlight), pts.GetY(ptHighlight));
283                                 WarpPointer(pt.x, pt.y);
284
285                                 if (e.ShiftDown() | e.ControlDown())
286                                         pts.SetOnCurve(ptHighlight, !pts.GetOnCurve(ptHighlight));
287                         }
288                 }
289                 else if (tool == TOOLDelPt)
290                 {
291                         if (pts.GetNumPoints() > 0)
292 //Or could use:
293 //                      if (ptHighlight != -1)
294                         {
295 //This assumes that WM_MOUSEMOVE happens before this!
296 //The above commented out line should take care of this contingency... !!! FIX !!!
297                                 pts.DeletePoint(ptHighlight);
298                                 Refresh();
299                         }
300                 }
301         }
302         else if (e.LeftUp())
303         {
304 //              mouseDown = false;
305
306                 if (tool == TOOLScroll || tool == TOOLZoom)
307                         ReleaseMouse();
308         }
309         else if (e.Dragging())
310         {
311                 if (e.RightIsDown())
312                 {
313                         ToolType newTool = app.toolPalette->FindSelectedTool();
314
315                         if (newTool != app.toolPalette->prevTool)
316                         {
317                                 app.toolPalette->prevTool = newTool;
318                                 app.toolPalette->Refresh(false);
319                         }
320
321                         return;
322                 }
323
324                 if (e.MiddleIsDown())
325                 {
326                     // Calc offset from previous point
327                         pt = e.GetPosition();
328                         ptOffset.x = pt.x - ptPrevious.x,
329                         ptOffset.y = pt.y - ptPrevious.y;
330
331 // Then multiply it by the scaling factor. Whee!
332                         // This looks wacky because we're using screen coords for the offset...
333                         // Otherwise, we would subtract both offsets!
334                         offsetX -= ptOffset.x, offsetY += ptOffset.y;
335                         Refresh();
336
337                         return;
338                 }
339
340 //              if (e.LeftIsDown())
341 //              {
342 #if 0
343                         if (tool == TOOLScroll)
344                         {
345                             // Extract current point from lParam/calc offset from previous point
346
347                                 pt = e.GetPosition();
348                                 ptOffset.x = pt.x - ptPrevious.x,
349                                 ptOffset.y = pt.y - ptPrevious.y;
350
351                                 // NOTE: OffsetViewportOrg operates in DEVICE UNITS...
352
353 //Seems there's no equivalent for this in wxWidgets...!
354 //!!! FIX !!!
355 //                              hdc = GetDC(hWnd);
356 //                              OffsetViewportOrgEx(hdc, ptOffset.x, ptOffset.y, NULL);
357 //                              ReleaseDC(hWnd, hdc);
358
359 // this shows that it works, so the logic above must be faulty...
360 // And it is. It should convert the coords first, then do the subtraction to figure the offset...
361 // Above: DONE
362 // Then multiply it by the scaling factor. Whee!
363                                 // This looks wacky because we're using screen coords for the offset...
364                                 // Otherwise, we would subtract both offsets!
365                                 offsetX -= ptOffset.x, offsetY += ptOffset.y;
366                                 Refresh();
367                         }
368                         else
369 #endif
370  if (tool == TOOLAddPt || tool == TOOLAddPoly || tool == TOOLSelect)
371                         {
372                                 if (tool != TOOLAddPt || pts.GetNumPoints() > 0)//yecch.
373                                 {
374 //temporary, for testing. BTW, Select drag bug is here...!
375 #if 0
376                                         wxPoint pt2 = GetAdjustedMousePosition(e);
377                                         pts.SetXY(ptHighlight, pt2.x, pt2.y);
378                                         Refresh();
379 #endif
380                                 }
381                         }
382                         else if (tool == TOOLPolySelect)
383                         {
384                                 if (pts.GetNumPoints() > 0)
385                                 {
386                                         wxPoint pt2 = GetAdjustedMousePosition(e);
387                                         // Should also set onCurve here as well, depending on keystate
388 //Or should we?
389                                         pts.OffsetPoly(pts.GetPoly(ptHighlight), pt2.x - pts.GetX(ptHighlight), pt2.y - pts.GetY(ptHighlight));
390                                         Refresh();
391                                 }
392                         }
393 //              }
394
395                 ptPrevious = pt;
396         }
397         else if (e.Moving())
398         {
399 //              else    // Moving, not dragging...
400 //              {
401                         if (tool == TOOLSelect || tool == TOOLDelPt || tool == TOOLAddPt
402                                 || tool == TOOLPolySelect)// || tool == TOOLAddPoly)
403                         {
404                                 wxPoint pt2 = GetAdjustedMousePosition(e);
405                                 double closest = 1.0e+99;
406
407                                 for(int i=0; i<pts.GetNumPoints(); i++)
408                                 {
409                                         double dist = ((pt2.x - pts.GetX(i)) * (pt2.x - pts.GetX(i)))
410                                                 + ((pt2.y - pts.GetY(i)) * (pt2.y - pts.GetY(i)));
411
412                                         if (dist < closest)
413                                                 closest = dist, ptHighlight = i;
414                                 }
415
416                                 if (ptHighlight != oldPtHighlight)
417                                 {
418                                         oldPtHighlight = ptHighlight;
419                                         Refresh();
420                                 }
421
422                                 // What follows here looks like voodoo, but is really simple. What we do is
423                                 // check to see if the mouse point has a perpendicular intersection with any of
424                                 // the line segments. If it does, calculate the length of the perpendicular
425                                 // and choose the smallest length. If there is no perpendicular, then choose the
426                                 // length of line connecting the closer of either the first endpoint or the
427                                 // second and choose the smallest of those.
428
429                                 // There is one bit of math that looks like voodoo to me ATM--will explain once
430                                 // I understand it better (the calculation of the length of the perpendicular).
431
432                                 if (pts.GetNumPoints() > 1 && tool == TOOLAddPt)
433                                 {
434                                         double smallest = 1.0e+99;
435
436                                         for(int i=0; i<pts.GetNumPoints(); i++)
437                                         {
438                                                 int32 p1x = pts.GetX(i), p1y = pts.GetY(i),
439                                                         p2x = pts.GetX(pts.GetNext(i)), p2y = pts.GetY(pts.GetNext(i));
440
441                                                 vector ls(p2x, p2y, 0, p1x, p1y, 0), v1(pt2.x, pt2.y, 0, p1x, p1y, 0),
442                                                         v2(pt2.x, pt2.y, 0, p2x, p2y, 0);
443                                                 double pp = ls.dot(v1) / ls.length(), dist;
444 // Geometric interpretation:
445 // pp is the paremeterized point on the vector ls where the perpendicular intersects ls.
446 // If pp < 0, then the perpendicular lies beyond the 1st endpoint. If pp > length of ls,
447 // then the perpendicular lies beyond the 2nd endpoint.
448
449                                                 if (pp < 0.0)
450                                                         dist = v1.length();
451                                                 else if (pp > ls.length())
452                                                         dist = v2.length();
453                                                 else                                    // distance = ?Det?(ls, v1) / |ls|
454                                                         dist = fabs((ls.x * v1.y - v1.x * ls.y) / ls.length());
455
456 //The answer to the above looks like it might be found here:
457 //
458 //If the segment endpoints are s and e, and the point is p, then the test for the perpendicular
459 //intercepting the segment is equivalent to insisting that the two dot products {s-e}.{s-p} and
460 //{e-s}.{e-p} are both non-negative.  Perpendicular distance from the point to the segment is
461 //computed by first computing the area of the triangle the three points form, then dividing by the
462 //length of the segment.  Distances are done just by the Pythagorean theorem.  Twice the area of the
463 //triangle formed by three points is the determinant of the following matrix:
464 //
465 //sx sy 1
466 //ex ey 1
467 //px py 1
468 //
469 //(???) By translating the start point to the origin, this can be rewritten as:
470 //By subtracting row 1 from all rows, you get the following:
471 //
472 //0         0         0
473 //(ex - sx) (ey - sy) 0
474 //(px - sx) (py - sy) 0
475 //
476 //which greatly simplifies the calculation of the determinant.
477
478                                                 if (dist < smallest)
479                                                         smallest = dist, ptNextHighlight = pts.GetNext(i), ptHighlight = i;
480                                         }
481
482                                         if (ptNextHighlight != oldPtNextHighlight)
483                                         {
484                                                 oldPtNextHighlight = ptNextHighlight;
485                                                 Refresh();
486                                         }
487                                 }
488                         }
489 //              }
490
491                 ptPrevious = e.GetPosition();
492         }
493 }
494
495 wxPoint TTEditWindow::GetAdjustedMousePosition(wxMouseEvent &e)
496 {
497         wxCoord width, height;
498         wxClientDC dc(this);
499
500         dc.GetSize(&width, &height);
501         dc.SetDeviceOrigin(-offsetX, height - (-offsetY));
502         dc.SetAxisOrientation(true, true);
503
504 #if 0
505 wxStatusBar * sb = ((wxFrame *)GetParent())->GetStatusBar();
506 wxString s;
507 s.Printf("Logical mouse pos: %d, %d (%d, %d)", pt.x, pt.y, width, height);
508 sb->SetStatusText(s);
509 #endif
510
511         return e.GetLogicalPosition(dc);
512 }
513
514 wxPoint TTEditWindow::GetAdjustedClientPosition(wxCoord x, wxCoord y)
515 {
516         wxCoord width, height;
517         wxClientDC dc(this);
518
519         dc.GetSize(&width, &height);
520         dc.SetDeviceOrigin(-offsetX, height - (-offsetY));
521         dc.SetAxisOrientation(true, true);
522
523         return wxPoint(dc.LogicalToDeviceX(x), dc.LogicalToDeviceY(y));
524 }
525
526
527 #if 0
528
529 !!! OLD STUFF !!!
530
531
532 LRESULT CALLBACK WndProc(HWND hWnd, UINT msgID, WPARAM wParam, LPARAM lParam)
533 {
534         RECT rc1, rc2;
535         HDC hdc;
536         POINT pt, ptOffset;
537         SIZE sz;
538         PAINTSTRUCT ps;
539
540         switch (msgID)
541         {
542         case WM_CREATE:
543
544                 MiscCenterWnd(hWnd, GetDesktopWindow());
545                 InitCommonControls();
546                 hStatusBar = CreateStatusWindow(WS_CHILD | WS_VISIBLE, statusBarTxt, hWnd, ID_STATUSBAR);
547
548                 if (!hStatusBar)
549                         return -1;
550
551 // clean this crap up!
552 // well, the only crappy thing here is using a POINT as an int array, but otherwise, this is OK
553                 wsprintf(strBuf, zoom, 1000);
554                 hdc = GetDC(hWnd);
555                 GetTextExtentPoint32(hdc, strBuf, lstrlen(strBuf), &sz);
556                 ReleaseDC(hWnd, hdc);
557                 zoomWndWidth = sz.cx;
558                 wsprintf(strBuf, zoom, 100);
559
560                 GetClientRect(hWnd, &rc1);
561                 pt.x = rc1.right - zoomWndWidth, pt.y = -1;
562                 SendMessage(hStatusBar, SB_SETPARTS, 2, (LPARAM)&pt);
563                 SendMessage(hStatusBar, SB_SETTEXT, (0 | SBT_NOBORDERS), (LPARAM)statusBarTxt);
564                 SendMessage(hStatusBar, SB_SETTEXT, 1, (LPARAM)strBuf);
565
566                 hToolBar = CreateToolbarEx(hWnd, WS_CHILD | WS_BORDER | WS_VISIBLE | TBSTYLE_TOOLTIPS,
567                         IDR_TOOLBAR1, 3, hInst, IDB_TOOLBAR1, tbButtons, 4, 16, 16, 16, 16, sizeof(TBBUTTON));
568
569                 if (!hToolBar)
570                         return -1;
571
572                 CreateNewDoc();
573
574 // The following can only be done because we use a private DC (using "CS_OWNDC")
575 // (Is that true???)
576 // Set the mapping to draw the character so it fits in the viewport...
577                 hdc = GetDC(hWnd);
578                 GetClientRect(hWnd, &rc1);
579                 GetClientRect(hStatusBar, &rc2);
580                 rc1.bottom -= rc2.bottom;
581                 SetMapMode(hdc, MM_ISOTROPIC);
582                 SetWindowExtEx(hdc, rc1.right, rc1.bottom, NULL);
583                 SetViewportExtEx(hdc, rc1.right, -rc1.bottom, NULL);
584                 SetViewportOrgEx(hdc, 0, rc1.bottom, NULL);
585                 ReleaseDC(hWnd, hdc);
586                 break;
587
588         case WM_CLOSE:
589
590                 if (SaveChanges())
591                 {
592                         wpM.length = wpC.length = sizeof(WINDOWPLACEMENT);
593                         GetWindowPlacement(hMainWnd, &wpM);
594                         GetWindowPlacement(hCharWnd, &wpC);
595
596                         if (!IsWindowVisible(hCharWnd))         // Needed because Windows lies about visibility
597                                 wpC.showCmd = SW_HIDE;
598
599                         hdc = GetDC(hWnd);
600                         GetViewportOrgEx(hdc, &ptVPM);
601                         ReleaseDC(hWnd, hdc);
602
603                         DestroyWindow(hWnd);
604                 }
605
606                 break;
607
608         case WM_DESTROY:
609
610                 PostQuitMessage(0);
611                 break;
612
613         case WM_NCLBUTTONDOWN:
614
615                 if (wParam == HTCAPTION)
616                 {
617                         NCMouseDown = true;
618                         GetWindowRect(hMainWnd, &rc1);
619                         GetWindowRect(hCharWnd, &rc2);
620                         ptWinOffset.x = rc2.left - rc1.left;
621                         ptWinOffset.y = rc2.top - rc1.top;
622                 }
623                     
624                 // Let Windows do its thing with this msg, or weird things will happen...
625
626                 DefWindowProc(hWnd, msgID, wParam, lParam);
627                 NCMouseDown = false;
628                 break;
629
630         case WM_WINDOWPOSCHANGING:
631
632                 if (NCMouseDown)
633                 {
634                         WINDOWPOS * wp = (WINDOWPOS *)lParam;
635
636                         if (wp->hwnd == hMainWnd && !(wp->flags & SWP_NOMOVE))
637                                 SetWindowPos(hCharWnd, 0, wp->x + ptWinOffset.x, wp->y + ptWinOffset.y,
638                                                 0, 0, SWP_NOZORDER | SWP_NOSIZE | SWP_NOACTIVATE);
639                 }
640
641                 return DefWindowProc(hWnd, msgID, wParam, lParam);      // Seems this is needed... Bleah!
642
643         case WM_PAINT:
644         {
645                 hdc = BeginPaint(hWnd, &ps);
646
647 // Scrolling can be done by using OffsetViewportOrgEx
648 // Scaling can be done by adjusting SetWindowExtEx (it's denominator of txform)
649 // you'd use: % = ViewportExt / WindowExt
650 // But it makes the window look like crap: fuggetuboutit.
651 // Instead, we have to scale EVERYTHING by hand. Crap!
652
653                 // Apparently, you *must* save the individual object types (pen, brush, etc.)
654
655                 HGDIOBJ oldPen = SelectObject(hdc, hBluePen1),
656                         oldBrush = SelectObject(hdc, hNullBrush);
657
658             // Draw coordinate axes
659
660                 MoveToEx(hdc, 0, -32000, NULL);
661                 LineTo(hdc, 0, 32000);
662                 MoveToEx(hdc, -32000, 0, NULL);
663                 LineTo(hdc, 32000, 0);
664
665             // Draw points
666
667                 for(int i=0; i<pts.GetNumPoints(); i++)
668                 {
669                         if (i == ptHighlight)
670                         {
671                                 SelectObject(hdc, hRedPen1);
672
673                                 if (pts.GetOnCurve(i))
674                                 {
675                                         DrawSquareDotN(hdc, pts.GetX(i), pts.GetY(i), 7);
676                                         DrawSquareDotN(hdc, pts.GetX(i), pts.GetY(i), 9);
677                                 }
678                                 else
679                                 {
680                                         DrawRoundDotN(hdc, pts.GetX(i), pts.GetY(i), 7);
681                                         DrawRoundDotN(hdc, pts.GetX(i), pts.GetY(i), 9);
682                                 }
683                         }
684                         else if ((i == ptHighlight || i == ptNextHighlight) && currentTool == TOOLAddPt)
685                         {
686                                 SelectObject(hdc, hGreenPen1);
687
688                                 if (pts.GetOnCurve(i))
689                                 {
690                                         DrawSquareDotN(hdc, pts.GetX(i), pts.GetY(i), 7);
691                                         DrawSquareDotN(hdc, pts.GetX(i), pts.GetY(i), 9);
692                                 }
693                                 else
694                                 {
695                                         DrawRoundDotN(hdc, pts.GetX(i), pts.GetY(i), 7);
696                                         DrawRoundDotN(hdc, pts.GetX(i), pts.GetY(i), 9);
697                                 }
698                         }
699                         else
700                         {
701                                 SelectObject(hdc, hBlackPen1);
702
703                                 if (pts.GetOnCurve(i))
704                                         DrawSquareDot(hdc, pts.GetX(i), pts.GetY(i));
705                                 else
706                                         DrawRoundDot(hdc, pts.GetX(i), pts.GetY(i));
707                         }
708
709                         if (currentTool == TOOLDelPt && i == ptHighlight)
710                         {
711                                 SelectObject(hdc, hRedPen1);
712                                 MoveToEx(hdc, pts.GetX(i) - 5, pts.GetY(i) - 5, NULL);
713                                 LineTo(hdc, pts.GetX(i) + 5, pts.GetY(i) + 5);
714                                 LineTo(hdc, pts.GetX(i) - 5, pts.GetY(i) - 5);//Lameness!
715                                 MoveToEx(hdc, pts.GetX(i) - 5, pts.GetY(i) + 5, NULL);
716                                 LineTo(hdc, pts.GetX(i) + 5, pts.GetY(i) - 5);
717                                 LineTo(hdc, pts.GetX(i) - 5, pts.GetY(i) + 5);//More lameness!!
718                         }
719                 }
720
721                 SelectObject(hdc, hBlackPen1);
722
723                 // Draw curve formed by points
724
725                 for(int poly=0; poly<pts.GetNumPolys(); poly++)
726                 {
727                         if (pts.GetNumPoints(poly) > 2)
728                         {
729                                 // Initial move...
730                                 // If it's not on curve, then move to it, otherwise move to last point...
731         
732                                 if (pts.GetOnCurve(poly, pts.GetNumPoints(poly) - 1))
733                                         MoveToEx(hdc, pts.GetX(poly, pts.GetNumPoints(poly) - 1), pts.GetY(poly, pts.GetNumPoints(poly) - 1), NULL);
734                                 else
735                                         MoveToEx(hdc, pts.GetX(poly, 0), pts.GetY(poly, 0), NULL);
736         
737                                 for(int i=0; i<pts.GetNumPoints(poly); i++)
738                                 {
739                                         if (pts.GetOnCurve(poly, i))
740                                                 LineTo(hdc, pts.GetX(poly, i), pts.GetY(poly, i));
741                                         else
742                                         {
743                                                 uint32 prev = pts.GetPrev(poly, i), next = pts.GetNext(poly, i);
744                                                 float px = pts.GetX(poly, prev), py = pts.GetY(poly, prev),
745                                                         nx = pts.GetX(poly, next), ny = pts.GetY(poly, next);
746         
747                                                 if (!pts.GetOnCurve(poly, prev))
748                                                         px = (px + pts.GetX(poly, i)) / 2.0f,
749                                                         py = (py + pts.GetY(poly, i)) / 2.0f;
750         
751                                                 if (!pts.GetOnCurve(poly, next))
752                                                         nx = (nx + pts.GetX(poly, i)) / 2.0f,
753                                                         ny = (ny + pts.GetY(poly, i)) / 2.0f;
754         
755                                                 Bezier(hdc, point(px, py), point(pts.GetX(poly, i), pts.GetY(poly, i)), point(nx, ny));
756         
757                                                 if (pts.GetOnCurve(poly, next))
758                                                         i++;                                    // Following point is on curve, so move past it
759                                         }
760                                 }
761                         }
762                 }
763
764                 SelectObject(hdc, oldPen);                              // Restore the stuff we disrupted...
765                 SelectObject(hdc, oldBrush);
766                 EndPaint(hWnd, &ps);
767                 break;
768         }
769         case WM_SIZE:
770
771                 // Apparently this is needed since these windows don't update themselves.
772                 SendMessage(hStatusBar, msgID, wParam, lParam);
773                 SendMessage(hToolBar, msgID, wParam, lParam);
774
775                 // This is needed to make the 2nd status pane visible
776                 GetClientRect(hWnd, &rc1);
777                 pt.x = rc1.right - zoomWndWidth, pt.y = -1;
778                 SendMessage(hStatusBar, SB_SETPARTS, 2, (LPARAM)&pt);
779                 break;
780
781         case WM_RBUTTONDOWN:
782
783                 GetCursorPos(&pt);
784                 SetWindowPos(hToolPalWnd, 0, pt.x, pt.y, 0, 0, SWP_NOZORDER | SWP_NOSIZE | SWP_SHOWWINDOW);
785                 SetFocus(hToolPalWnd);
786                 SetCapture(hToolPalWnd);                                // Ensure tool palette gets RButtonUp
787                 SetCursor(LoadCursor(NULL, MAKEINTRESOURCE(IDC_ARROW)));        // Tool pallete has "regular" cursor
788                 break;
789
790         case WM_LBUTTONDOWN:
791
792                 mouseDown = true;
793
794                 if (currentTool == TOOLScroll || currentTool == TOOLZoom)
795                         SetCapture(hWnd);                                       // Make sure we capture the mouse when in scroll/zoom mode
796                 else if (currentTool == TOOLAddPt)              // "Add Point" tool
797                 {
798                         if (pts.GetNumPoints() > 0)
799                         {
800 //Do we really need to put a cap on this???
801 //Maybe...
802 //                              if (pts.GetNumPoints() < 16)
803 //                              {
804                                 pt.x = lParam & 0xFFFF, pt.y = lParam >> 16;
805                                 hdc = GetDC(hWnd);
806                                 DPtoLP(hdc, &pt, 1);
807                                 pts.InsertPoint(pts.GetNext(ptHighlight), pt.x, pt.y, (wParam & (MK_SHIFT | MK_CONTROL) ? false : true));
808                                 ptHighlight = ptNextHighlight;
809                                 ReleaseDC(hWnd, hdc);
810                                 InvalidateRect(hWnd, NULL, TRUE);
811 //                              }
812                         }
813                 }
814                 else if (currentTool == TOOLAddPoly)    // "Add Poly" tool
815                 {
816 #ifdef DEBUGFOO
817 wsprintf(strBuf, "Adding point... # polys: %u, # points: %u", pts.GetNumPolys(), pts.GetNumPoints());
818 WriteLogMsg(strBuf);
819 #endif
820                         if (polyFirstPoint)
821                         {
822                                 polyFirstPoint = false;
823                                 pts.AddNewPolyAtEnd();
824                         }
825
826 //Do we really need to put a cap on this???
827 //Maybe...
828 //                      if (pts.GetNumPoints() < 16)
829 //                      {
830                         pt.x = lParam & 0xFFFF, pt.y = lParam >> 16;
831                         hdc = GetDC(hWnd);
832                         DPtoLP(hdc, &pt, 1);
833                         ReleaseDC(hWnd, hdc);
834                         // Append a point to the end of the structure
835                         pts += IPoint(pt.x, pt.y, (wParam & (MK_SHIFT | MK_CONTROL) ? false : true));
836 ptHighlight = pts.GetNumPoints() - 1;
837                         InvalidateRect(hWnd, NULL, TRUE);
838 //                      }
839 #ifdef DEBUGFOO
840 wsprintf(strBuf, " --> [# polys: %u, # points: %u]\xD\xA", pts.GetNumPolys(), pts.GetNumPoints());
841 WriteLogMsg(strBuf);
842 #endif
843                 }
844                 else if (currentTool == TOOLSelect || currentTool == TOOLPolySelect)
845                 {
846                         if (pts.GetNumPoints() > 0)
847                         {
848                                 pt.x = pts.GetX(ptHighlight), pt.y = pts.GetY(ptHighlight);
849                                 hdc = GetDC(hWnd);
850                                 LPtoDP(hdc, &pt, 1);
851                                 ClientToScreen(hWnd, &pt);
852                                 SetCursorPos(pt.x, pt.y);
853                                 ReleaseDC(hWnd, hdc);
854
855                                 if (wParam & (MK_SHIFT | MK_CONTROL))
856                                         pts.SetOnCurve(ptHighlight, !pts.GetOnCurve(ptHighlight));
857                         }
858                 }
859                 else if (currentTool == TOOLDelPt)
860                 {
861                         if (pts.GetNumPoints() > 0)
862 //Or could use:
863 //                      if (ptHighlight != -1)
864                         {
865 //This assumes that WM_MOUSEMOVE happens before this!
866 //The above commented out line should take care of this contingency... !!! FIX !!!
867                                 pts.DeletePoint(ptHighlight);
868                                 InvalidateRect(hWnd, NULL, TRUE);
869                         }
870                 }
871
872                 break;
873
874         case WM_LBUTTONUP:
875
876                 mouseDown = false;
877
878                 if (currentTool == TOOLScroll || currentTool == TOOLZoom)
879                         ReleaseCapture();
880
881                 break;
882
883         case WM_MOUSEMOVE:
884
885                 SetCursor(hCur[currentTool]);
886
887             // Extract current point from lParam/calc offset from previous point
888
889                 pt.x = lParam & 0xFFFF, pt.y = lParam >> 16;
890                 ptOffset.x = pt.x - ptPrevious.x,
891                 ptOffset.y = pt.y - ptPrevious.y;
892
893                 if (mouseDown)
894                 {
895                         if (currentTool == TOOLScroll)
896                         {
897                                 // NOTE: OffsetViewportOrg operates in DEVICE UNITS...
898
899                                 hdc = GetDC(hWnd);
900                                 OffsetViewportOrgEx(hdc, ptOffset.x, ptOffset.y, NULL);
901                                 ReleaseDC(hWnd, hdc);
902
903 // this shows that it works, so the logic above must be faulty...
904 // And it is. It should convert the coords first, then do the subtraction to figure the offset...
905 // Above: DONE
906 // Then multiply it by the scaling factor. Whee!
907
908                                 InvalidateRect(hWnd, NULL, TRUE);
909 //                              SendMessage(hWnd, WM_PAINT, NULL, NULL);
910                         }
911                         else if (currentTool == TOOLAddPt || currentTool == TOOLAddPoly || currentTool == TOOLSelect)
912                         {
913                                 if (currentTool != TOOLAddPt || pts.GetNumPoints() > 0)//yecch.
914                                 {
915                                         POINT pt2;
916                                         pt2.x = pt.x, pt2.y = pt.y;
917                                         // Should also set onCurve here as well, depending on keystate
918 //Or should we?
919                                         hdc = GetDC(hWnd);
920                                         DPtoLP(hdc, &pt2, 1);
921                                         pts.SetXY(ptHighlight, pt2.x, pt2.y);
922                                         ReleaseDC(hWnd, hdc);
923                                         InvalidateRect(hWnd, NULL, TRUE);
924                                 }
925                         }
926                         else if (currentTool == TOOLPolySelect)
927                         {
928                                 if (pts.GetNumPoints() > 0)
929                                 {
930                                         POINT pt2;
931                                         pt2.x = pt.x, pt2.y = pt.y;
932                                         // Should also set onCurve here as well, depending on keystate
933 //Or should we?
934                                         hdc = GetDC(hWnd);
935                                         DPtoLP(hdc, &pt2, 1);
936                                         pts.OffsetPoly(pts.GetPoly(ptHighlight), pt2.x - pts.GetX(ptHighlight), pt2.y - pts.GetY(ptHighlight));
937                                         ReleaseDC(hWnd, hdc);
938                                         InvalidateRect(hWnd, NULL, TRUE);
939                                 }
940                         }
941                 }
942                 else
943                 {
944                         if (currentTool == TOOLSelect || currentTool == TOOLDelPt || currentTool == TOOLAddPt
945                                 || currentTool == TOOLPolySelect)// || currentTool == TOOLAddPoly)
946                         {
947                                 POINT pt2;
948                                 pt2.x = pt.x, pt2.y = pt.y;
949                                 hdc = GetDC(hWnd);
950                                 DPtoLP(hdc, &pt2, 1);
951                                 ReleaseDC(hWnd, hdc);
952
953                                 double closest = 1.0e+99;
954
955                                 for(int i=0; i<pts.GetNumPoints(); i++)
956                                 {
957                                         double dist = ((pt2.x - pts.GetX(i)) * (pt2.x - pts.GetX(i)))
958                                                 + ((pt2.y - pts.GetY(i)) * (pt2.y - pts.GetY(i)));
959
960                                         if (dist < closest)
961                                                 closest = dist, ptHighlight = i;
962                                 }
963
964                                 if (ptHighlight != oldPtHighlight)
965                                 {
966                                         oldPtHighlight = ptHighlight;
967                                         InvalidateRect(hWnd, NULL, TRUE);
968                                 }
969
970                                 // What follows here looks like voodoo, but is really simple. What we do is
971                                 // check to see if the mouse point has a perpendicular intersection with any of
972                                 // the line segments. If it does, calculate the length of the perpendicular
973                                 // and choose the smallest length. If there is no perpendicular, then choose the
974                                 // length of line connecting the closer of either the first endpoint or the
975                                 // second and choose the smallest of those.
976
977                                 // There is one bit of math that looks like voodoo to me ATM--will explain once
978                                 // I understand it better (the calculation of the length of the perpendicular).
979
980                                 if (pts.GetNumPoints() > 1 && currentTool == TOOLAddPt)
981                                 {
982                                         double smallest = 1.0e+99;
983
984                                         for(int i=0; i<pts.GetNumPoints(); i++)
985                                         {
986                                                 int32 p1x = pts.GetX(i), p1y = pts.GetY(i),
987                                                         p2x = pts.GetX(pts.GetNext(i)), p2y = pts.GetY(pts.GetNext(i));
988
989                                                 vector ls(p2x, p2y, 0, p1x, p1y, 0), v1(pt2.x, pt2.y, 0, p1x, p1y, 0),
990                                                         v2(pt2.x, pt2.y, 0, p2x, p2y, 0);
991                                                 double pp = ls.dot(v1) / ls.length(), dist;
992 // Geometric interpretation:
993 // pp is the paremeterized point on the vector ls where the perpendicular intersects ls.
994 // If pp < 0, then the perpendicular lies beyond the 1st endpoint. If pp > length of ls,
995 // then the perpendicular lies beyond the 2nd endpoint.
996
997                                                 if (pp < 0.0)
998                                                         dist = v1.length();
999                                                 else if (pp > ls.length())
1000                                                         dist = v2.length();
1001                                                 else                                    // distance = ?Det?(ls, v1) / |ls|
1002                                                         dist = abs((ls.x * v1.y - v1.x * ls.y) / ls.length());
1003
1004 //The answer to the above looks like it might be found here:
1005 //
1006 //If the segment endpoints are s and e, and the point is p, then the test for the perpendicular
1007 //intercepting the segment is equivalent to insisting that the two dot products {s-e}.{s-p} and
1008 //{e-s}.{e-p} are both non-negative.  Perpendicular distance from the point to the segment is
1009 //computed by first computing the area of the triangle the three points form, then dividing by the
1010 //length of the segment.  Distances are done just by the Pythagorean theorem.  Twice the area of the
1011 //triangle formed by three points is the determinant of the following matrix:
1012 //
1013 //sx sy 1
1014 //ex ey 1
1015 //px py 1
1016 //
1017 //(???) By translating the start point to the origin, this can be rewritten as:
1018 //By subtracting row 1 from all rows, you get the following:
1019 //
1020 //0         0         0
1021 //(ex - sx) (ey - sy) 0
1022 //(px - sx) (py - sy) 0
1023 //
1024 //which greatly simplifies the calculation of the determinant.
1025
1026                                                 if (dist < smallest)
1027                                                         smallest = dist, ptNextHighlight = pts.GetNext(i), ptHighlight = i;
1028                                         }
1029
1030                                         if (ptNextHighlight != oldPtNextHighlight)
1031                                         {
1032                                                 oldPtNextHighlight = ptNextHighlight;
1033                                                 InvalidateRect(hWnd, NULL, TRUE);
1034                                         }
1035                                 }
1036                         }
1037                 }
1038
1039                 ptPrevious.x = pt.x, ptPrevious.y = pt.y;
1040
1041                 break;
1042
1043         case WM_NOTIFY:
1044
1045                 if (((NMHDR *)lParam)->code == TTN_NEEDTEXT)
1046                 {
1047                         LoadString(hInst, ((TOOLTIPTEXT *)lParam)->hdr.idFrom + 0x80, toolTipTxt, 16);
1048                         ((TOOLTIPTEXT *)lParam)->lpszText = toolTipTxt;
1049                 }
1050
1051                 break;
1052
1053         case WM_MENUSELECT:
1054         {
1055                 statusBarTxt[0] = 0;                                    // Clear status bar text
1056                 uint16 flags = wParam >> 16;                    // Extract flags
1057
1058                 if (!(flags & MFT_SEPARATOR))
1059                 {
1060                         uint16 id = wParam & 0xFFFF;
1061
1062                         if (flags & MF_POPUP)
1063                         {
1064                                 if (flags & MF_SYSMENU)
1065                                         id = IDS_SYSMENU;
1066                                 else
1067                                         id = IDM_FILEMENU + wParam;
1068                         }
1069
1070                         LoadString(hInst, id, statusBarTxt, 64);
1071                 }
1072
1073                 SendMessage(hStatusBar, SB_SETTEXT, 0 + SBT_NOBORDERS, (LPARAM)statusBarTxt);
1074                 break;
1075         }
1076         case WM_COMMAND:
1077         {
1078                 uint16 cmd = wParam & 0xFFFF;
1079
1080                 if (cmd == IDM_NEW)
1081                 {
1082 //                    call   CmdIDM_NEW
1083                 }
1084                 else if (cmd == IDM_OPEN)
1085                 {
1086 //                    call   SaveChanges
1087 //                    .IF (eax)
1088 //                      movmov ofn.hwndOwner, eax, hMainWnd
1089 //                      mov    ofn.Flags, OFN_PATHMUSTEXIST + OFN_FILEMUSTEXIST
1090 //                      invoke GetOpenFileName, ADDR ofn
1091 //                      .IF (eax)
1092 ////////
1093 //jmp @F
1094 //szDMsg1a      BYTE    "Could not open the file (GetOpenFileName)...", 0
1095 //szDMsg1b      BYTE    "Open error!", 0
1096 //szDMsg1c      BYTE    "About to attempt to open file...", 0
1097 //@@:
1098 ////invoke MessageBox, hWnd, ADDR szDMsg1a, ADDR szDMsg1b, MB_ICONERROR or MB_OK
1099 //invoke MessageBox, hMainWnd, ADDR szDMsg1c, ADDR szFile, MB_ICONERROR or MB_OK
1100 //                        invoke LoadTTF, ADDR szFile
1101 //
1102 //////
1103 //                        // <<< FILE OPEN CODE HERE >>>
1104 //                        or     fFileStatus, NAMEDbit
1105 //                        and    fFileStatus, NOT CHANGEDbit
1106 //                        call   NewWindowName
1107 //                        mov    eax, TRUE      // return TRUE
1108 //                        jmp    Return
1109 //                        //��������������������������������������
1110 //OpenError:              invoke GetLastError
1111 //                        // <<< FILE OPEN ERROR CODE HERE >>>
1112 //                      .ENDIF
1113 //                      zero   eax      // return FALSE
1114 //                    .ENDIF
1115                 }
1116                 else if (cmd == IDM_SAVEAS)
1117                 {
1118 //                    and    fFileStatus, NOT NAMEDbit
1119 //                    call   CmdIDM_SAVE
1120                 }
1121                 else if (cmd == IDM_SAVE)
1122                 {
1123 //                    call   CmdIDM_SAVE
1124                 }
1125                 else if (cmd == IDM_ABOUT)
1126                         DialogBoxParam(hInst, MAKEINTRESOURCE(IDD_ABOUT), hMainWnd, AboutProc, NULL);
1127                 else if (cmd == IDM_EXIT)
1128                         SendMessage(hWnd, WM_CLOSE, 0, 0);
1129                 else if (cmd == ID_TBCHARWIN)
1130                 {
1131                         ShowWindow(hCharWnd, (IsWindowVisible(hCharWnd) ? SW_HIDE : SW_SHOWNOACTIVATE));
1132
1133 #ifdef DEBUGFOO
1134 wpC.length = sizeof(WINDOWPLACEMENT);
1135 GetWindowPlacement(hCharWnd, &wpC);
1136 wsprintf(strBuf, "Char window showCmd = %08X...\n", wpC.showCmd);
1137 WriteLogMsg(strBuf);
1138 #endif
1139                 }
1140                 else
1141                         return DefWindowProc(hWnd, msgID, wParam, lParam);
1142
1143                 break;
1144         }
1145         default:
1146                 return DefWindowProc(hWnd, msgID, wParam, lParam);
1147         }
1148
1149         return 0;
1150 }
1151
1152 //
1153 // Initialize TTF data
1154 //
1155 void CreateNewDoc(void)
1156 {
1157 }
1158
1159 //
1160 // Save changes to document before quitting
1161 //
1162 bool SaveChanges(void)
1163 {
1164         return true;
1165 }
1166
1167
1168
1169 //
1170 // ABOUT Dialog WndProc
1171 //
1172 BOOL CALLBACK AboutProc(HWND hDlg, UINT msgID, WPARAM wParam, LPARAM lParam)
1173 {
1174         switch (msgID)
1175         {
1176         case WM_INITDIALOG:
1177
1178                 MiscCenterWnd(hDlg, hMainWnd);
1179                 break;
1180
1181         case WM_COMMAND:
1182
1183                 if (wParam == IDOK || wParam == IDCANCEL)
1184                         EndDialog(hDlg, TRUE);
1185
1186                 break;
1187
1188         default:
1189                 return FALSE;
1190         }
1191
1192         return TRUE;
1193 }
1194
1195 //
1196 // Character Window WndProc
1197 //
1198 WndProcCW       PROC  STDCALL, hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM
1199
1200                 mov    eax, uMsg                // pickup our message
1201                 .IF (eax == WM_PAINT)
1202                   mov    eax, 0                 // Non-sense... (placeholder!)
1203 // Scan conversion etc. goes here...
1204                 .ELSEIF (eax == WM_LBUTTONDOWN)
1205                   invoke SetCapture, hCharWnd
1206                 .ELSEIF (eax == WM_LBUTTONUP)
1207                   invoke ReleaseCapture
1208                   invoke SetFocus, hMainWnd     // Make sure the main wnd keeps focus!
1209                 .ELSEIF (eax == WM_NCLBUTTONDOWN)
1210                   invoke DefWindowProc, hWnd, uMsg, wParam, lParam // Let it do its thing
1211                   invoke SetFocus, hMainWnd     // Make sure the main wnd keeps focus!
1212                 .ELSE
1213 DefProc:          invoke DefWindowProc, hWnd, uMsg, wParam, lParam
1214                 .ENDIF
1215                 ret
1216
1217 WndProcCW       ENDP
1218
1219 //
1220 // Character Window WndProc
1221 //
1222 LRESULT CALLBACK WndProcCW(HWND hWnd, UINT msgID, WPARAM wParam, LPARAM lParam)
1223 {
1224         switch (msgID)
1225         {
1226         default:
1227                 return DefWindowProc(hWnd, msgID, wParam, lParam);
1228         }
1229
1230         return 0;
1231 }
1232
1233
1234 // Function prototypes
1235
1236 int32 FindSelectedTool(void);
1237
1238 //
1239 // Tool Palette WndProc
1240 //
1241 LRESULT CALLBACK WndProcTP(HWND hWnd, UINT msgID, WPARAM wParam, LPARAM lParam)
1242 {
1243         PAINTSTRUCT ps;
1244         HDC hdc;
1245         POINT pt;
1246         static uint32 prevTool = -1;
1247
1248         switch (msgID)
1249         {
1250         case WM_PAINT:
1251         {
1252                 hdc = BeginPaint(hWnd, &ps);
1253                 HDC newDC = CreateCompatibleDC(NULL);
1254                 SelectObject(newDC, hBMToolPal1);
1255                 BitBlt(hdc, 0, 0, sizeTPBM.x, sizeTPBM.y, newDC, 0, 0, SRCCOPY);
1256                 DeleteDC(newDC);
1257
1258 // This is crappy. Find some way to tighten this up!
1259                 int32 tool = FindSelectedTool();
1260
1261                 if (tool != -1)
1262                 {
1263                         newDC = CreateCompatibleDC(NULL);
1264                         SelectObject(newDC, hBMToolPal1);
1265                     //need ul corner of bitmap, ul corner of dest, width/height
1266                         pt.x = sizeStamp.x * (tool & 0x03), pt.y = sizeStamp.y * (tool >> 2);
1267                         BitBlt(hdc, pt.x, pt.y, sizeStamp.x, sizeStamp.y, newDC, pt.x, pt.y, NOTSRCCOPY);
1268                         DeleteDC(newDC);
1269                 }
1270
1271                 EndPaint(hWnd, &ps);
1272                 break;
1273         }
1274         case WM_MOUSEMOVE:
1275         {
1276                 int32 tool = FindSelectedTool();
1277
1278                 if (tool != prevTool)
1279                 {
1280                         prevTool = tool;
1281                         InvalidateRect(hWnd, NULL, FALSE);
1282                 }
1283
1284                 break;
1285         }
1286         case WM_RBUTTONUP:
1287         {
1288                 int32 tool = FindSelectedTool(), oldTool = currentTool;
1289
1290                 if (tool != -1)
1291                         currentTool = tool;
1292
1293                 if (currentTool != TOOLSelect && currentTool != TOOLDelPt && currentTool != TOOLAddPt
1294                         && currentTool != TOOLPolySelect)
1295                         ptHighlight = -1;
1296
1297                 if (currentTool != oldTool)
1298                         InvalidateRect(hMainWnd, NULL, TRUE);
1299
1300                 if (currentTool == TOOLAddPoly)
1301 #ifdef DEBUGFOO
1302 {
1303 #endif
1304                         polyFirstPoint = true;
1305 #ifdef DEBUGFOO
1306 wsprintf(strBuf, "--> Selected poly tool, polyFirstPoint is %s\n", polyFirstPoint ? "true" : "false");
1307 WriteLogMsg(strBuf);
1308 }
1309 #endif
1310
1311                 ReleaseCapture();
1312                 ShowWindow(hToolPalWnd, SW_HIDE);
1313                 SetFocus(hMainWnd);                                             // Make sure the main wnd keeps focus!
1314
1315                 break;
1316         }
1317         default:
1318                 return DefWindowProc(hWnd, msgID, wParam, lParam);
1319         }
1320
1321         return 0;
1322 }
1323
1324 //
1325 // Find which tool we're pointing at
1326 // Use: xcoord = mouse.x / (bmsize.x/4), ycoord = mouse.y / (bmsize.y/2)
1327 //
1328 int32 FindSelectedTool(void)
1329 {
1330         POINT pt;
1331                 
1332         GetCursorPos(&pt);
1333         ScreenToClient(hToolPalWnd, &pt);
1334
1335         uint32 x = (uint32)pt.x / sizeStamp.x, y = (uint32)pt.y / sizeStamp.y, tool = -1;
1336
1337         if (x < 4 && y < 2)
1338 //      {
1339                 tool = (y * 4) + x;
1340
1341 //              if (tool == 7)
1342 //                      tool = -1;                                                      // 7 has no tool...
1343 //      }
1344
1345         return tool;
1346 }
1347
1348
1349 //
1350 // Misc center window
1351 //
1352 void MiscCenterWnd(HWND hChild, HWND hParent)
1353 {
1354         RECT parent, child;
1355
1356         if (!GetWindowRect(hParent, &parent) || !GetWindowRect(hChild, &child))
1357                 return;
1358
1359         int32 x = parent.left + (((parent.right - parent.left) - (child.right - child.left)) / 2),
1360                 y = parent.top + (((parent.bottom - parent.top) - (child.bottom - child.top)) / 2);
1361
1362         if (x < 0)
1363                 x = 0;
1364         else if (x > GetSystemMetrics(SM_CXFULLSCREEN) - (child.right - child.left))
1365                 x = GetSystemMetrics(SM_CXFULLSCREEN) - (child.right - child.left);
1366
1367         if (y < 0)
1368                 y = 0;
1369         else if (y > GetSystemMetrics(SM_CYFULLSCREEN) - (child.bottom - child.top))
1370                 y = GetSystemMetrics(SM_CYFULLSCREEN) - (child.bottom - child.top);
1371
1372         SetWindowPos(hChild, NULL, x, y, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
1373 }
1374
1375 //
1376 // Allow only one instance
1377 //
1378 bool OnlyOneInstance(void)
1379 {
1380         HWND window = FindWindow(className, NULL);
1381
1382         if (window == NULL)
1383                 return true;
1384
1385         ShowWindow(window, SW_SHOWNORMAL);
1386         SetWindowPos(window, NULL, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE);
1387
1388         return false;
1389 }
1390
1391 //
1392 // Load/Allocate all resources
1393 //
1394 bool LoadResources(void)
1395 {
1396         hCur[0] = LoadCursor(hInst, MAKEINTRESOURCE(IDC_CURSOR1));
1397         hCur[1] = LoadCursor(hInst, MAKEINTRESOURCE(IDC_CURSOR2));
1398         hCur[2] = LoadCursor(hInst, MAKEINTRESOURCE(IDC_CURSOR3));
1399         hCur[3] = LoadCursor(hInst, MAKEINTRESOURCE(IDC_CURSOR4));
1400         hCur[4] = LoadCursor(hInst, MAKEINTRESOURCE(IDC_CURSOR5));
1401         hCur[5] = LoadCursor(hInst, MAKEINTRESOURCE(IDC_CURSOR6));
1402         hCur[6] = LoadCursor(hInst, MAKEINTRESOURCE(IDC_CURSOR7));
1403         hCur[7] = LoadCursor(hInst, MAKEINTRESOURCE(IDC_CURSOR8));
1404
1405         BITMAP bm;
1406
1407         hBMToolPal1 = LoadBitmap(hInst, MAKEINTRESOURCE(IDB_TOOLPAL1));
1408         GetObject(hBMToolPal1, sizeof(bm), &bm);
1409
1410         // Set up sizes
1411
1412         sizeTPBM.x = bm.bmWidth, sizeTPBM.y = bm.bmHeight;
1413         sizeStamp.x = bm.bmWidth / 4, sizeStamp.y = bm.bmHeight / 2;
1414
1415         hBluePen1 = CreatePen(PS_DOT, 1, 0x00FF0000);
1416         hRedPen1 = CreatePen(PS_SOLID, 1, 0x000000FF);
1417         hGreenPen1 = CreatePen(PS_SOLID, 1, 0x0000AF00);
1418         hBlackPen1 = CreatePen(PS_SOLID, 1, 0x00000000);
1419
1420         LOGBRUSH lb = { BS_NULL, 0, 0 };
1421
1422         hNullBrush = CreateBrushIndirect(&lb);
1423
1424         return true;
1425 }
1426
1427 //
1428 // Deallocate all resources
1429 //
1430 void DeallocateResources(void)
1431 {
1432         DeleteObject(hBMToolPal1);
1433         DeleteObject(hBluePen1);
1434         DeleteObject(hRedPen1);
1435         DeleteObject(hGreenPen1);
1436         DeleteObject(hBlackPen1);
1437         DeleteObject(hNullBrush);
1438 }
1439
1440 //
1441 // Save all application specific data, so we can pick up where we last left off...
1442 //
1443 void SaveAppState(void)
1444 {
1445         SetINIInt("Main", "flags", wpM.flags);
1446         SetINIInt("Main", "showCmd", wpM.showCmd);
1447         SetINIInt("Main", "x1", wpM.rcNormalPosition.left);
1448         SetINIInt("Main", "y1", wpM.rcNormalPosition.top);
1449         SetINIInt("Main", "x2", wpM.rcNormalPosition.right);
1450         SetINIInt("Main", "y2", wpM.rcNormalPosition.bottom);
1451
1452         SetINIInt("Main", "vpx", ptVPM.x);
1453         SetINIInt("Main", "vpy", ptVPM.y);
1454
1455         SetINIInt("Char", "flags", wpC.flags);
1456         SetINIInt("Char", "showCmd", wpC.showCmd);
1457         SetINIInt("Char", "x1", wpC.rcNormalPosition.left);
1458         SetINIInt("Char", "y1", wpC.rcNormalPosition.top);
1459         SetINIInt("Char", "x2", wpC.rcNormalPosition.right);
1460         SetINIInt("Char", "y2", wpC.rcNormalPosition.bottom);
1461
1462         // Need to write out currently opened font, character looking at, other misc. crap
1463 //      SetINIString("Main", "currentFile", pDoc->GetPathName());
1464 //      SetINIInt("Main", "currentChar", pDoc->character_num);
1465 }
1466
1467 //
1468 // Restore all application specific data previously saved
1469 //
1470 bool RestoreAppState(void)
1471 {
1472         InitINIFile();
1473
1474         WINDOWPLACEMENT wp;
1475         wp.length = sizeof(WINDOWPLACEMENT);
1476         GetWindowPlacement(hMainWnd, &wp);
1477
1478         wp.flags = GetINIInt("Main", "flags", wp.flags);
1479         wp.showCmd = GetINIInt("Main", "showCmd", wp.showCmd);
1480         wp.rcNormalPosition.left = GetINIInt("Main", "x1", wp.rcNormalPosition.left);
1481         wp.rcNormalPosition.top = GetINIInt("Main", "y1", wp.rcNormalPosition.top);
1482         wp.rcNormalPosition.right = GetINIInt("Main", "x2", wp.rcNormalPosition.right);
1483         wp.rcNormalPosition.bottom = GetINIInt("Main", "y2", wp.rcNormalPosition.bottom);
1484
1485         SetWindowPlacement(hMainWnd, &wp);
1486
1487         HDC hdc;
1488         POINT pt;
1489         hdc = GetDC(hMainWnd);
1490         GetViewportOrgEx(hdc, &pt);
1491
1492         pt.x = GetINIInt("Main", "vpx", pt.x);
1493         pt.y = GetINIInt("Main", "vpy", pt.y);
1494
1495         SetViewportOrgEx(hdc, pt.x, pt.y, NULL);
1496         ReleaseDC(hMainWnd, hdc);
1497
1498         GetWindowPlacement(hCharWnd, &wp);
1499
1500         wp.flags = GetINIInt("Char", "flags", wp.flags);
1501         wp.showCmd = GetINIInt("Char", "showCmd", wp.showCmd);
1502         wp.rcNormalPosition.left = GetINIInt("Char", "x1", wp.rcNormalPosition.left);
1503         wp.rcNormalPosition.top = GetINIInt("Char", "y1", wp.rcNormalPosition.top);
1504         wp.rcNormalPosition.right = GetINIInt("Char", "x2", wp.rcNormalPosition.right);
1505         wp.rcNormalPosition.bottom = GetINIInt("Char", "y2", wp.rcNormalPosition.bottom);
1506
1507         SetWindowPlacement(hCharWnd, &wp);
1508
1509         if (wp.showCmd == SW_HIDE)
1510                 SendMessage(hToolBar, TB_SETSTATE, ID_TBCHARWIN, MAKELONG(TBSTATE_ENABLED, 0));
1511
1512 //  CString lastFile = theApplicationObject.GetProfileString(version, "currentFile", "");
1513 //  int lastChar = theApplicationObject.GetProfileInt(version, "currentChar", 0);
1514 //  if (lastFile.GetLength())
1515 //  {
1516 //    // Attempt to restore the last session by open the last file used, etc...
1517 //    if (!pDoc->m_myFont.Load(lastFile))
1518 //    {
1519 //      // Err, make sure you can support any allegations you make below, buddy!
1520 //      AfxMessageBox("The last file opened with TTF Edit\n\rseems to have been moved or deleted.");
1521 //    }
1522 //    else
1523 //    {
1524 //      pDoc->m_myFont.SetGlyph(lastChar);  // Set TTF object to last used char
1525 //      pDoc->character_num = lastChar;
1526 //      pDoc->SetPathName(lastFile);
1527 //
1528 //      BYTE name[512];
1529 //      pDoc->m_myFont.GetCharName(lastChar, name);
1530 //      m_wndOwned.SetWindowText((char *)name);
1531 //    }
1532 //  }
1533
1534         return true;
1535 }
1536
1537 //
1538 // Initialization
1539 //
1540 bool Initialization(void)
1541 {
1542         WNDCLASSEX wcex;
1543
1544         if (!LoadResources())
1545                 return false;
1546
1547         RtlFillMemory(&wcex, sizeof(WNDCLASSEX), 0);
1548         wcex.cbSize = sizeof(WNDCLASSEX);
1549         wcex.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
1550         wcex.lpfnWndProc = WndProc;
1551         wcex.hInstance = hInst;
1552         wcex.hIcon = LoadIcon(hInst, MAKEINTRESOURCE(IDI_ICON));
1553         wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
1554         wcex.lpszMenuName = MAKEINTRESOURCE(IDM_MENU);
1555         wcex.lpszClassName = className;
1556         wcex.hIconSm = (HICON)LoadImage(hInst, MAKEINTRESOURCE(IDI_ICON), IMAGE_ICON, 16, 16, NULL);
1557
1558         if (!RegisterClassEx(&wcex))
1559                 return false;
1560
1561         hMainWnd = CreateWindowEx(NULL, className, className, WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN,
1562                 0, 0, 0x1A0, 0x180, NULL, NULL, hInst, NULL);
1563
1564         if (!hMainWnd)
1565                 return false;
1566
1567         ShowWindow(hMainWnd, nCmdShow);
1568         UpdateWindow(hMainWnd);
1569
1570         // Character window creation
1571
1572         wcex.lpfnWndProc = WndProcCW;
1573         wcex.lpszMenuName = NULL;
1574         wcex.lpszClassName = CNCharWin;
1575         wcex.hCursor = LoadCursor(NULL, IDC_ARROW);     // Owned windows have "regular" cursors
1576
1577         if (!RegisterClassEx(&wcex))
1578                 return false;
1579
1580         hCharWnd = CreateWindowEx(WS_EX_WINDOWEDGE | WS_EX_TOOLWINDOW, CNCharWin,
1581                 curCharName, WS_POPUP | WS_CAPTION | WS_VISIBLE | WS_THICKFRAME,
1582                 100, 100, 120, 120, hMainWnd, NULL, hInst, NULL);
1583
1584         if (!hCharWnd)
1585                 return false;
1586
1587         ShowWindow(hCharWnd, SW_SHOWNORMAL);
1588         UpdateWindow(hCharWnd);
1589         SetFocus(hMainWnd);                                                     // Make sure main wnd has focus!
1590
1591         // Tool palette window creation
1592
1593         wcex.lpfnWndProc = WndProcTP;
1594         wcex.lpszClassName = CNToolPal;
1595
1596         if (!RegisterClassEx(&wcex))
1597                 return false;
1598
1599         hToolPalWnd = CreateWindowEx(WS_EX_WINDOWEDGE, CNToolPal, NULL, WS_POPUP,
1600                 0, 0, sizeTPBM.x, sizeTPBM.y, hMainWnd, NULL, hInst, NULL);
1601
1602         if (!hToolPalWnd)
1603                 return false;
1604
1605 // Note: A better way to handle this would be to have a sub that registers ALL
1606 //       classes beforehand and passes a TRUE/FALSE back depending on whether or not
1607 //       all the classes were able to be registered or not, THEN create the windows
1608 //       and controls...
1609
1610         RestoreAppState();                                                      // Restore app related stuff
1611
1612         return true;
1613 }
1614
1615 #endif