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