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