2 // TTEDIT.CPP - The TrueType Editor
\r
3 // by James L. Hammons
\r
4 // (C) 2004 Underground Software
\r
6 // JLH = James L. Hammons <jlhamm@acm.org>
\r
9 // --- ---------- -------------------------------------------------------------
\r
10 // JLH 08/28/2008 Created this file
\r
15 // STILL TO BE DONE:
\r
17 // - Fix bug in Glyphpoints when dragging on an empty canvas or loading a font
\r
18 // - Fix scrolling, zooming, settings (ini)
\r
21 // Uncomment this for debugging...
\r
23 #define DEBUGFOO // Various tool debugging...
\r
24 #define DEBUGTP // Toolpalette debugging...
\r
26 #include "editwindow.h"
\r
28 #include "toolwindow.h"
\r
33 BEGIN_EVENT_TABLE(TTEditWindow, wxWindow)
\r
34 EVT_PAINT(TTEditWindow::OnPaint)
\r
35 EVT_MOUSE_EVENTS(TTEditWindow::OnMouseEvent)
\r
38 TTEditWindow::TTEditWindow(wxFrame * parent, const wxPoint &pos, const wxSize &size, long style):
\r
39 wxWindow(parent, -1, pos, size, style | wxFULL_REPAINT_ON_RESIZE),
\r
40 app(wxGetApp()), scale(1.0), offsetX(-10), offsetY(-10), tool(TOOLSelect),
\r
41 ptHighlight(-1), oldPtHighlight(-1), ptNextHighlight(-1), oldPtNextHighlight(-1),
\r
42 polyFirstPoint(true), bmp(NULL)
\r
44 SetCursor(*(app.cur[tool]));
\r
45 SetBackgroundColour(wxColour(0xFF, 0xFF, 0xFF));
\r
48 s.Printf(_("Zoom: %.2f%%"), scale * 100.0);
\r
49 parent->SetStatusText(s, 1);
\r
52 TTEditWindow::~TTEditWindow(void)
\r
58 void TTEditWindow::OnPaint(wxPaintEvent &e)
\r
62 //dc.SetBackground(*wxWHITE_BRUSH);
\r
64 // Due to the screwiness of wxWidgets coord system, the origin is ALWAYS
\r
65 // the upper left corner--regardless of axis orientation, etc...
\r
66 wxCoord width, height;
\r
67 dc.GetSize(&width, &height);
\r
69 dc.SetDeviceOrigin(-offsetX, height - (-offsetY));
\r
70 dc.SetAxisOrientation(true, true);
\r
72 // Scrolling can be done by using OffsetViewportOrgEx
\r
73 // Scaling can be done by adjusting SetWindowExtEx (it's denominator of txform)
\r
74 // you'd use: % = ViewportExt / WindowExt
\r
75 // But it makes the window look like crap: fuggetuboutit.
\r
76 // Instead, we have to scale EVERYTHING by hand. Crap!
\r
77 // It's not *that* bad, but not as convenient either...
\r
79 dc.SetPen(*(wxThePenList->FindOrCreatePen(wxColour(0x00, 0x00, 0xFF), 1, wxDOT)));
\r
80 // dc.DrawLine(0, 0, 10, 10);
\r
82 // Draw coordinate axes
\r
88 for(int i=0; i<pts.GetNumPoints(); i++)
\r
90 if (i == ptHighlight)
\r
92 dc.SetPen(*(wxThePenList->FindOrCreatePen(wxColour(0xFF, 0x00, 0x00), 1, wxSOLID)));
\r
93 // SelectObject(hdc, hRedPen1);
\r
95 if (pts.GetOnCurve(i))
\r
97 DrawSquareDotN(dc, pts.GetX(i), pts.GetY(i), 7);
\r
98 DrawSquareDotN(dc, pts.GetX(i), pts.GetY(i), 9);
\r
102 DrawRoundDotN(dc, pts.GetX(i), pts.GetY(i), 7);
\r
103 DrawRoundDotN(dc, pts.GetX(i), pts.GetY(i), 9);
\r
106 else if ((i == ptHighlight || i == ptNextHighlight) && tool == TOOLAddPt)
\r
108 dc.SetPen(*(wxThePenList->FindOrCreatePen(wxColour(0x00, 0xAF, 0x00), 1, wxSOLID)));
\r
109 // SelectObject(hdc, hGreenPen1);
\r
111 if (pts.GetOnCurve(i))
\r
113 DrawSquareDotN(dc, pts.GetX(i), pts.GetY(i), 7);
\r
114 DrawSquareDotN(dc, pts.GetX(i), pts.GetY(i), 9);
\r
118 DrawRoundDotN(dc, pts.GetX(i), pts.GetY(i), 7);
\r
119 DrawRoundDotN(dc, pts.GetX(i), pts.GetY(i), 9);
\r
124 dc.SetPen(*(wxThePenList->FindOrCreatePen(wxColour(0x00, 0x00, 0x00), 1, wxSOLID)));
\r
125 // SelectObject(hdc, hBlackPen1);
\r
127 if (pts.GetOnCurve(i))
\r
128 DrawSquareDot(dc, pts.GetX(i), pts.GetY(i));
\r
130 DrawRoundDot(dc, pts.GetX(i), pts.GetY(i));
\r
133 if (tool == TOOLDelPt && i == ptHighlight)
\r
135 dc.SetPen(*(wxThePenList->FindOrCreatePen(wxColour(0xFF, 0x00, 0x00), 1, wxSOLID)));
\r
136 // SelectObject(hdc, hRedPen1);
\r
137 // MoveToEx(hdc, pts.GetX(i) - 5, pts.GetY(i) - 5, NULL);
\r
138 // LineTo(hdc, pts.GetX(i) + 5, pts.GetY(i) + 5);
\r
139 // LineTo(hdc, pts.GetX(i) - 5, pts.GetY(i) - 5);//Lameness!
\r
140 // MoveToEx(hdc, pts.GetX(i) - 5, pts.GetY(i) + 5, NULL);
\r
141 // LineTo(hdc, pts.GetX(i) + 5, pts.GetY(i) - 5);
\r
142 // LineTo(hdc, pts.GetX(i) - 5, pts.GetY(i) + 5);//More lameness!!
\r
143 dc.DrawLine(pts.GetX(i) - 5, pts.GetY(i) - 5, pts.GetX(i) + 5, pts.GetY(i) + 5);
\r
144 dc.DrawLine(pts.GetX(i) + 5, pts.GetY(i) - 5, pts.GetX(i) - 5, pts.GetY(i) + 5);
\r
148 // SelectObject(hdc, hBlackPen1);
\r
149 dc.SetPen(*(wxThePenList->FindOrCreatePen(wxColour(0x00, 0x00, 0x00), 1, wxSOLID)));
\r
151 // Draw curve formed by points
\r
153 for(int poly=0; poly<pts.GetNumPolys(); poly++)
\r
155 if (pts.GetNumPoints(poly) > 2)
\r
158 // If it's not on curve, then move to it, otherwise move to last point...
\r
162 if (pts.GetOnCurve(poly, pts.GetNumPoints(poly) - 1))
\r
163 x = (wxCoord)pts.GetX(poly, pts.GetNumPoints(poly) - 1), y = (wxCoord)pts.GetY(poly, pts.GetNumPoints(poly) - 1);
\r
165 x = (wxCoord)pts.GetX(poly, 0), y = (wxCoord)pts.GetY(poly, 0);
\r
167 for(int i=0; i<pts.GetNumPoints(poly); i++)
\r
169 if (pts.GetOnCurve(poly, i))
\r
170 // LineTo(hdc, pts.GetX(poly, i), pts.GetY(poly, i));
\r
172 dc.DrawLine(x, y, pts.GetX(poly, i), pts.GetY(poly, i));
\r
173 x = (wxCoord)pts.GetX(poly, i), y = (wxCoord)pts.GetY(poly, i);
\r
177 uint32 prev = pts.GetPrev(poly, i), next = pts.GetNext(poly, i);
\r
178 float px = pts.GetX(poly, prev), py = pts.GetY(poly, prev),
\r
179 nx = pts.GetX(poly, next), ny = pts.GetY(poly, next);
\r
181 if (!pts.GetOnCurve(poly, prev))
\r
182 px = (px + pts.GetX(poly, i)) / 2.0f,
\r
183 py = (py + pts.GetY(poly, i)) / 2.0f;
\r
185 if (!pts.GetOnCurve(poly, next))
\r
186 nx = (nx + pts.GetX(poly, i)) / 2.0f,
\r
187 ny = (ny + pts.GetY(poly, i)) / 2.0f;
\r
189 Bezier(dc, point(px, py), point(pts.GetX(poly, i), pts.GetY(poly, i)), point(nx, ny));
\r
190 x = (wxCoord)nx, y = (wxCoord)ny;
\r
192 if (pts.GetOnCurve(poly, next))
\r
193 i++; // Following point is on curve, so move past it
\r
199 // SelectObject(hdc, oldPen); // Restore the stuff we disrupted...
\r
200 dc.SetPen(wxNullPen);
\r
201 // SelectObject(hdc, oldBrush);
\r
202 // EndPaint(hWnd, &ps);
\r
205 void TTEditWindow::OnMouseEvent(wxMouseEvent &e)
\r
208 //printf("!!! This window %s focus...!\n", (HasCapture() ? "has" : "doesn't have"));
\r
212 // Handle tool palette (NOTE: tool palette deals with RightUp event.)
\r
214 wxPoint pt = ClientToScreen(e.GetPosition());
\r
215 app.toolPalette->Move(pt);
\r
216 app.toolPalette->Show(true);
\r
217 SetCursor(*wxSTANDARD_CURSOR);
\r
218 app.toolPalette->SetCursor(*wxSTANDARD_CURSOR);
\r
219 app.toolPalette->prevTool = TOOLSelect;
\r
220 app.toolPalette->Refresh(false);
\r
223 else if (e.RightUp())
\r
225 ToolType newTool = app.toolPalette->FindSelectedTool();//, oldTool = tool;
\r
227 // We only change the tool if a new one was actually selected. Otherwise, we do nothing.
\r
228 if (newTool != TOOLNone)
\r
232 if (tool == TOOLScroll || tool == TOOLZoom || tool == TOOLAddPoly
\r
233 || tool == TOOLDelPoly)
\r
236 if (tool == TOOLAddPoly)
\r
237 polyFirstPoint = true;
\r
241 app.toolPalette->Show(false);
\r
242 SetCursor(*(app.cur[tool]));
\r
244 else if (e.LeftDown())
\r
246 if (tool == TOOLScroll || tool == TOOLZoom)
\r
247 CaptureMouse(); // Make sure we capture the mouse when in scroll/zoom mode
\r
248 else if (tool == TOOLAddPt) // "Add Point" tool
\r
250 if (pts.GetNumPoints() > 0)
\r
252 wxPoint pt = GetAdjustedMousePosition(e);
\r
253 pts.InsertPoint(pts.GetNext(ptHighlight), pt.x, pt.y, (e.ShiftDown() | e.ControlDown() ? false : true));
\r
254 ptHighlight = ptNextHighlight;
\r
258 else if (tool == TOOLAddPoly) // "Add Poly" tool
\r
261 WriteLogMsg("Adding point... # polys: %u, # points: %u", pts.GetNumPolys(), pts.GetNumPoints());
\r
263 if (polyFirstPoint)
\r
265 polyFirstPoint = false;
\r
266 pts.AddNewPolyAtEnd();
\r
269 wxPoint pt = GetAdjustedMousePosition(e);
\r
270 // Append a point to the end of the structure
\r
271 pts += IPoint(pt.x, pt.y, (e.ShiftDown() | e.ControlDown() ? false : true));
\r
272 ptHighlight = pts.GetNumPoints() - 1;
\r
275 WriteLogMsg(" --> [# polys: %u, # points: %u]\n", pts.GetNumPolys(), pts.GetNumPoints());
\r
278 else if (tool == TOOLSelect || tool == TOOLPolySelect)
\r
280 if (pts.GetNumPoints() > 0)
\r
282 pt = GetAdjustedClientPosition(pts.GetX(ptHighlight), pts.GetY(ptHighlight));
\r
283 WarpPointer(pt.x, pt.y);
\r
285 if (e.ShiftDown() | e.ControlDown())
\r
286 pts.SetOnCurve(ptHighlight, !pts.GetOnCurve(ptHighlight));
\r
289 else if (tool == TOOLDelPt)
\r
291 if (pts.GetNumPoints() > 0)
\r
293 // if (ptHighlight != -1)
\r
295 //This assumes that WM_MOUSEMOVE happens before this!
\r
296 //The above commented out line should take care of this contingency... !!! FIX !!!
\r
297 pts.DeletePoint(ptHighlight);
\r
302 else if (e.LeftUp())
\r
304 // mouseDown = false;
\r
306 if (tool == TOOLScroll || tool == TOOLZoom)
\r
309 else if (e.Dragging())
\r
311 if (e.RightIsDown())
\r
313 ToolType newTool = app.toolPalette->FindSelectedTool();
\r
315 if (newTool != app.toolPalette->prevTool)
\r
317 app.toolPalette->prevTool = newTool;
\r
318 app.toolPalette->Refresh(false);
\r
324 // Extract current point from lParam/calc offset from previous point
\r
326 pt = e.GetPosition();
\r
327 ptOffset.x = pt.x - ptPrevious.x,
\r
328 ptOffset.y = pt.y - ptPrevious.y;
\r
330 // if (e.LeftIsDown())
\r
332 if (tool == TOOLScroll)
\r
334 // NOTE: OffsetViewportOrg operates in DEVICE UNITS...
\r
336 //Seems there's no equivalent for this in wxWidgets...!
\r
338 // hdc = GetDC(hWnd);
\r
339 // OffsetViewportOrgEx(hdc, ptOffset.x, ptOffset.y, NULL);
\r
340 // ReleaseDC(hWnd, hdc);
\r
342 // this shows that it works, so the logic above must be faulty...
\r
343 // And it is. It should convert the coords first, then do the subtraction to figure the offset...
\r
345 // Then multiply it by the scaling factor. Whee!
\r
346 // This looks wacky because we're using screen coords for the offset...
\r
347 // Otherwise, we would subtract both offsets!
\r
348 offsetX -= ptOffset.x, offsetY += ptOffset.y;
\r
351 else if (tool == TOOLAddPt || tool == TOOLAddPoly || tool == TOOLSelect)
\r
353 if (tool != TOOLAddPt || pts.GetNumPoints() > 0)//yecch.
\r
355 //temporary, for testing. BTW, Select drag bug is here...!
\r
357 wxPoint pt2 = GetAdjustedMousePosition(e);
\r
358 pts.SetXY(ptHighlight, pt2.x, pt2.y);
\r
363 else if (tool == TOOLPolySelect)
\r
365 if (pts.GetNumPoints() > 0)
\r
367 wxPoint pt2 = GetAdjustedMousePosition(e);
\r
368 // Should also set onCurve here as well, depending on keystate
\r
370 pts.OffsetPoly(pts.GetPoly(ptHighlight), pt2.x - pts.GetX(ptHighlight), pt2.y - pts.GetY(ptHighlight));
\r
378 else if (e.Moving())
\r
380 // else // Moving, not dragging...
\r
382 if (tool == TOOLSelect || tool == TOOLDelPt || tool == TOOLAddPt
\r
383 || tool == TOOLPolySelect)// || tool == TOOLAddPoly)
\r
385 wxPoint pt2 = GetAdjustedMousePosition(e);
\r
386 double closest = 1.0e+99;
\r
388 for(int i=0; i<pts.GetNumPoints(); i++)
\r
390 double dist = ((pt2.x - pts.GetX(i)) * (pt2.x - pts.GetX(i)))
\r
391 + ((pt2.y - pts.GetY(i)) * (pt2.y - pts.GetY(i)));
\r
393 if (dist < closest)
\r
394 closest = dist, ptHighlight = i;
\r
397 if (ptHighlight != oldPtHighlight)
\r
399 oldPtHighlight = ptHighlight;
\r
403 // What follows here looks like voodoo, but is really simple. What we do is
\r
404 // check to see if the mouse point has a perpendicular intersection with any of
\r
405 // the line segments. If it does, calculate the length of the perpendicular
\r
406 // and choose the smallest length. If there is no perpendicular, then choose the
\r
407 // length of line connecting the closer of either the first endpoint or the
\r
408 // second and choose the smallest of those.
\r
410 // There is one bit of math that looks like voodoo to me ATM--will explain once
\r
411 // I understand it better (the calculation of the length of the perpendicular).
\r
413 if (pts.GetNumPoints() > 1 && tool == TOOLAddPt)
\r
415 double smallest = 1.0e+99;
\r
417 for(int i=0; i<pts.GetNumPoints(); i++)
\r
419 int32 p1x = pts.GetX(i), p1y = pts.GetY(i),
\r
420 p2x = pts.GetX(pts.GetNext(i)), p2y = pts.GetY(pts.GetNext(i));
\r
422 vector ls(p2x, p2y, 0, p1x, p1y, 0), v1(pt2.x, pt2.y, 0, p1x, p1y, 0),
\r
423 v2(pt2.x, pt2.y, 0, p2x, p2y, 0);
\r
424 double pp = ls.dot(v1) / ls.length(), dist;
\r
425 // Geometric interpretation:
\r
426 // pp is the paremeterized point on the vector ls where the perpendicular intersects ls.
\r
427 // If pp < 0, then the perpendicular lies beyond the 1st endpoint. If pp > length of ls,
\r
428 // then the perpendicular lies beyond the 2nd endpoint.
\r
431 dist = v1.length();
\r
432 else if (pp > ls.length())
\r
433 dist = v2.length();
\r
434 else // distance = ?Det?(ls, v1) / |ls|
\r
435 dist = fabs((ls.x * v1.y - v1.x * ls.y) / ls.length());
\r
437 //The answer to the above looks like it might be found here:
\r
439 //If the segment endpoints are s and e, and the point is p, then the test for the perpendicular
\r
440 //intercepting the segment is equivalent to insisting that the two dot products {s-e}.{s-p} and
\r
441 //{e-s}.{e-p} are both non-negative. Perpendicular distance from the point to the segment is
\r
442 //computed by first computing the area of the triangle the three points form, then dividing by the
\r
443 //length of the segment. Distances are done just by the Pythagorean theorem. Twice the area of the
\r
444 //triangle formed by three points is the determinant of the following matrix:
\r
450 //(???) By translating the start point to the origin, this can be rewritten as:
\r
451 //By subtracting row 1 from all rows, you get the following:
\r
454 //(ex - sx) (ey - sy) 0
\r
455 //(px - sx) (py - sy) 0
\r
457 //which greatly simplifies the calculation of the determinant.
\r
459 if (dist < smallest)
\r
460 smallest = dist, ptNextHighlight = pts.GetNext(i), ptHighlight = i;
\r
463 if (ptNextHighlight != oldPtNextHighlight)
\r
465 oldPtNextHighlight = ptNextHighlight;
\r
472 ptPrevious = e.GetPosition();
\r
476 wxPoint TTEditWindow::GetAdjustedMousePosition(wxMouseEvent &e)
\r
478 wxCoord width, height;
\r
479 wxClientDC dc(this);
\r
481 dc.GetSize(&width, &height);
\r
482 dc.SetDeviceOrigin(-offsetX, height - (-offsetY));
\r
483 dc.SetAxisOrientation(true, true);
\r
486 wxStatusBar * sb = ((wxFrame *)GetParent())->GetStatusBar();
\r
488 s.Printf("Logical mouse pos: %d, %d (%d, %d)", pt.x, pt.y, width, height);
\r
489 sb->SetStatusText(s);
\r
492 return e.GetLogicalPosition(dc);
\r
495 wxPoint TTEditWindow::GetAdjustedClientPosition(wxCoord x, wxCoord y)
\r
497 wxCoord width, height;
\r
498 wxClientDC dc(this);
\r
500 dc.GetSize(&width, &height);
\r
501 dc.SetDeviceOrigin(-offsetX, height - (-offsetY));
\r
502 dc.SetAxisOrientation(true, true);
\r
504 return wxPoint(dc.LogicalToDeviceX(x), dc.LogicalToDeviceY(y));
\r
508 // Draw a round dot (5x5, centered on [x, y])
\r
510 void TTEditWindow::DrawRoundDot(wxDC &dc, int32 x, int32 y)
\r
514 pt[0].x = x - 1, pt[0].y = y - 2;
\r
515 pt[1].x = x + 1, pt[1].y = y - 2;
\r
516 pt[2].x = x + 2, pt[2].y = y - 1;
\r
517 pt[3].x = x + 2, pt[3].y = y + 1;
\r
518 pt[4].x = x + 1, pt[4].y = y + 2;
\r
519 pt[5].x = x - 1, pt[5].y = y + 2;
\r
520 pt[6].x = x - 2, pt[6].y = y + 1;
\r
521 pt[7].x = x - 2, pt[7].y = y - 1;
\r
523 dc.DrawPolygon(8, pt);
\r
527 // Draw a sqaure dot (5x5, centered on [x, y])
\r
529 void TTEditWindow::DrawSquareDot(wxDC &dc, int32 x, int32 y)
\r
533 pt[0].x = x - 2, pt[0].y = y - 2;
\r
534 pt[1].x = x + 2, pt[1].y = y - 2;
\r
535 pt[2].x = x + 2, pt[2].y = y + 2;
\r
536 pt[3].x = x - 2, pt[3].y = y + 2;
\r
538 dc.DrawPolygon(4, pt);
\r
542 // Draw a sqaure dot (nxn, centered on [x, y])
\r
544 void TTEditWindow::DrawSquareDotN(wxDC &dc, int32 x, int32 y, uint32 n)
\r
547 uint32 offset = (n - 1) / 2;
\r
549 pt[0].x = x - offset, pt[0].y = y - offset;
\r
550 pt[1].x = x + offset, pt[1].y = y - offset;
\r
551 pt[2].x = x + offset, pt[2].y = y + offset;
\r
552 pt[3].x = x - offset, pt[3].y = y + offset;
\r
554 dc.DrawPolygon(4, pt);
\r
558 // Draw a round dot (nxn, centered on [x, y])
\r
560 void TTEditWindow::DrawRoundDotN(wxDC &dc, int32 x, int32 y, uint32 n)
\r
562 dc.DrawCircle(x, y, (n / 2) + 1);
\r
571 LRESULT CALLBACK WndProc(HWND hWnd, UINT msgID, WPARAM wParam, LPARAM lParam)
\r
575 POINT pt, ptOffset;
\r
583 MiscCenterWnd(hWnd, GetDesktopWindow());
\r
584 InitCommonControls();
\r
585 hStatusBar = CreateStatusWindow(WS_CHILD | WS_VISIBLE, statusBarTxt, hWnd, ID_STATUSBAR);
\r
590 // clean this crap up!
\r
591 // well, the only crappy thing here is using a POINT as an int array, but otherwise, this is OK
\r
592 wsprintf(strBuf, zoom, 1000);
\r
594 GetTextExtentPoint32(hdc, strBuf, lstrlen(strBuf), &sz);
\r
595 ReleaseDC(hWnd, hdc);
\r
596 zoomWndWidth = sz.cx;
\r
597 wsprintf(strBuf, zoom, 100);
\r
599 GetClientRect(hWnd, &rc1);
\r
600 pt.x = rc1.right - zoomWndWidth, pt.y = -1;
\r
601 SendMessage(hStatusBar, SB_SETPARTS, 2, (LPARAM)&pt);
\r
602 SendMessage(hStatusBar, SB_SETTEXT, (0 | SBT_NOBORDERS), (LPARAM)statusBarTxt);
\r
603 SendMessage(hStatusBar, SB_SETTEXT, 1, (LPARAM)strBuf);
\r
605 hToolBar = CreateToolbarEx(hWnd, WS_CHILD | WS_BORDER | WS_VISIBLE | TBSTYLE_TOOLTIPS,
\r
606 IDR_TOOLBAR1, 3, hInst, IDB_TOOLBAR1, tbButtons, 4, 16, 16, 16, 16, sizeof(TBBUTTON));
\r
613 // The following can only be done because we use a private DC (using "CS_OWNDC")
\r
614 // (Is that true???)
\r
615 // Set the mapping to draw the character so it fits in the viewport...
\r
617 GetClientRect(hWnd, &rc1);
\r
618 GetClientRect(hStatusBar, &rc2);
\r
619 rc1.bottom -= rc2.bottom;
\r
620 SetMapMode(hdc, MM_ISOTROPIC);
\r
621 SetWindowExtEx(hdc, rc1.right, rc1.bottom, NULL);
\r
622 SetViewportExtEx(hdc, rc1.right, -rc1.bottom, NULL);
\r
623 SetViewportOrgEx(hdc, 0, rc1.bottom, NULL);
\r
624 ReleaseDC(hWnd, hdc);
\r
631 wpM.length = wpC.length = sizeof(WINDOWPLACEMENT);
\r
632 GetWindowPlacement(hMainWnd, &wpM);
\r
633 GetWindowPlacement(hCharWnd, &wpC);
\r
635 if (!IsWindowVisible(hCharWnd)) // Needed because Windows lies about visibility
\r
636 wpC.showCmd = SW_HIDE;
\r
639 GetViewportOrgEx(hdc, &ptVPM);
\r
640 ReleaseDC(hWnd, hdc);
\r
642 DestroyWindow(hWnd);
\r
649 PostQuitMessage(0);
\r
652 case WM_NCLBUTTONDOWN:
\r
654 if (wParam == HTCAPTION)
\r
656 NCMouseDown = true;
\r
657 GetWindowRect(hMainWnd, &rc1);
\r
658 GetWindowRect(hCharWnd, &rc2);
\r
659 ptWinOffset.x = rc2.left - rc1.left;
\r
660 ptWinOffset.y = rc2.top - rc1.top;
\r
663 // Let Windows do its thing with this msg, or weird things will happen...
\r
665 DefWindowProc(hWnd, msgID, wParam, lParam);
\r
666 NCMouseDown = false;
\r
669 case WM_WINDOWPOSCHANGING:
\r
673 WINDOWPOS * wp = (WINDOWPOS *)lParam;
\r
675 if (wp->hwnd == hMainWnd && !(wp->flags & SWP_NOMOVE))
\r
676 SetWindowPos(hCharWnd, 0, wp->x + ptWinOffset.x, wp->y + ptWinOffset.y,
\r
677 0, 0, SWP_NOZORDER | SWP_NOSIZE | SWP_NOACTIVATE);
\r
680 return DefWindowProc(hWnd, msgID, wParam, lParam); // Seems this is needed... Bleah!
\r
684 hdc = BeginPaint(hWnd, &ps);
\r
686 // Scrolling can be done by using OffsetViewportOrgEx
\r
687 // Scaling can be done by adjusting SetWindowExtEx (it's denominator of txform)
\r
688 // you'd use: % = ViewportExt / WindowExt
\r
689 // But it makes the window look like crap: fuggetuboutit.
\r
690 // Instead, we have to scale EVERYTHING by hand. Crap!
\r
692 // Apparently, you *must* save the individual object types (pen, brush, etc.)
\r
694 HGDIOBJ oldPen = SelectObject(hdc, hBluePen1),
\r
695 oldBrush = SelectObject(hdc, hNullBrush);
\r
697 // Draw coordinate axes
\r
699 MoveToEx(hdc, 0, -32000, NULL);
\r
700 LineTo(hdc, 0, 32000);
\r
701 MoveToEx(hdc, -32000, 0, NULL);
\r
702 LineTo(hdc, 32000, 0);
\r
706 for(int i=0; i<pts.GetNumPoints(); i++)
\r
708 if (i == ptHighlight)
\r
710 SelectObject(hdc, hRedPen1);
\r
712 if (pts.GetOnCurve(i))
\r
714 DrawSquareDotN(hdc, pts.GetX(i), pts.GetY(i), 7);
\r
715 DrawSquareDotN(hdc, pts.GetX(i), pts.GetY(i), 9);
\r
719 DrawRoundDotN(hdc, pts.GetX(i), pts.GetY(i), 7);
\r
720 DrawRoundDotN(hdc, pts.GetX(i), pts.GetY(i), 9);
\r
723 else if ((i == ptHighlight || i == ptNextHighlight) && currentTool == TOOLAddPt)
\r
725 SelectObject(hdc, hGreenPen1);
\r
727 if (pts.GetOnCurve(i))
\r
729 DrawSquareDotN(hdc, pts.GetX(i), pts.GetY(i), 7);
\r
730 DrawSquareDotN(hdc, pts.GetX(i), pts.GetY(i), 9);
\r
734 DrawRoundDotN(hdc, pts.GetX(i), pts.GetY(i), 7);
\r
735 DrawRoundDotN(hdc, pts.GetX(i), pts.GetY(i), 9);
\r
740 SelectObject(hdc, hBlackPen1);
\r
742 if (pts.GetOnCurve(i))
\r
743 DrawSquareDot(hdc, pts.GetX(i), pts.GetY(i));
\r
745 DrawRoundDot(hdc, pts.GetX(i), pts.GetY(i));
\r
748 if (currentTool == TOOLDelPt && i == ptHighlight)
\r
750 SelectObject(hdc, hRedPen1);
\r
751 MoveToEx(hdc, pts.GetX(i) - 5, pts.GetY(i) - 5, NULL);
\r
752 LineTo(hdc, pts.GetX(i) + 5, pts.GetY(i) + 5);
\r
753 LineTo(hdc, pts.GetX(i) - 5, pts.GetY(i) - 5);//Lameness!
\r
754 MoveToEx(hdc, pts.GetX(i) - 5, pts.GetY(i) + 5, NULL);
\r
755 LineTo(hdc, pts.GetX(i) + 5, pts.GetY(i) - 5);
\r
756 LineTo(hdc, pts.GetX(i) - 5, pts.GetY(i) + 5);//More lameness!!
\r
760 SelectObject(hdc, hBlackPen1);
\r
762 // Draw curve formed by points
\r
764 for(int poly=0; poly<pts.GetNumPolys(); poly++)
\r
766 if (pts.GetNumPoints(poly) > 2)
\r
769 // If it's not on curve, then move to it, otherwise move to last point...
\r
771 if (pts.GetOnCurve(poly, pts.GetNumPoints(poly) - 1))
\r
772 MoveToEx(hdc, pts.GetX(poly, pts.GetNumPoints(poly) - 1), pts.GetY(poly, pts.GetNumPoints(poly) - 1), NULL);
\r
774 MoveToEx(hdc, pts.GetX(poly, 0), pts.GetY(poly, 0), NULL);
\r
776 for(int i=0; i<pts.GetNumPoints(poly); i++)
\r
778 if (pts.GetOnCurve(poly, i))
\r
779 LineTo(hdc, pts.GetX(poly, i), pts.GetY(poly, i));
\r
782 uint32 prev = pts.GetPrev(poly, i), next = pts.GetNext(poly, i);
\r
783 float px = pts.GetX(poly, prev), py = pts.GetY(poly, prev),
\r
784 nx = pts.GetX(poly, next), ny = pts.GetY(poly, next);
\r
786 if (!pts.GetOnCurve(poly, prev))
\r
787 px = (px + pts.GetX(poly, i)) / 2.0f,
\r
788 py = (py + pts.GetY(poly, i)) / 2.0f;
\r
790 if (!pts.GetOnCurve(poly, next))
\r
791 nx = (nx + pts.GetX(poly, i)) / 2.0f,
\r
792 ny = (ny + pts.GetY(poly, i)) / 2.0f;
\r
794 Bezier(hdc, point(px, py), point(pts.GetX(poly, i), pts.GetY(poly, i)), point(nx, ny));
\r
796 if (pts.GetOnCurve(poly, next))
\r
797 i++; // Following point is on curve, so move past it
\r
803 SelectObject(hdc, oldPen); // Restore the stuff we disrupted...
\r
804 SelectObject(hdc, oldBrush);
\r
805 EndPaint(hWnd, &ps);
\r
810 // Apparently this is needed since these windows don't update themselves.
\r
811 SendMessage(hStatusBar, msgID, wParam, lParam);
\r
812 SendMessage(hToolBar, msgID, wParam, lParam);
\r
814 // This is needed to make the 2nd status pane visible
\r
815 GetClientRect(hWnd, &rc1);
\r
816 pt.x = rc1.right - zoomWndWidth, pt.y = -1;
\r
817 SendMessage(hStatusBar, SB_SETPARTS, 2, (LPARAM)&pt);
\r
820 case WM_RBUTTONDOWN:
\r
823 SetWindowPos(hToolPalWnd, 0, pt.x, pt.y, 0, 0, SWP_NOZORDER | SWP_NOSIZE | SWP_SHOWWINDOW);
\r
824 SetFocus(hToolPalWnd);
\r
825 SetCapture(hToolPalWnd); // Ensure tool palette gets RButtonUp
\r
826 SetCursor(LoadCursor(NULL, MAKEINTRESOURCE(IDC_ARROW))); // Tool pallete has "regular" cursor
\r
829 case WM_LBUTTONDOWN:
\r
833 if (currentTool == TOOLScroll || currentTool == TOOLZoom)
\r
834 SetCapture(hWnd); // Make sure we capture the mouse when in scroll/zoom mode
\r
835 else if (currentTool == TOOLAddPt) // "Add Point" tool
\r
837 if (pts.GetNumPoints() > 0)
\r
839 //Do we really need to put a cap on this???
\r
841 // if (pts.GetNumPoints() < 16)
\r
843 pt.x = lParam & 0xFFFF, pt.y = lParam >> 16;
\r
845 DPtoLP(hdc, &pt, 1);
\r
846 pts.InsertPoint(pts.GetNext(ptHighlight), pt.x, pt.y, (wParam & (MK_SHIFT | MK_CONTROL) ? false : true));
\r
847 ptHighlight = ptNextHighlight;
\r
848 ReleaseDC(hWnd, hdc);
\r
849 InvalidateRect(hWnd, NULL, TRUE);
\r
853 else if (currentTool == TOOLAddPoly) // "Add Poly" tool
\r
856 wsprintf(strBuf, "Adding point... # polys: %u, # points: %u", pts.GetNumPolys(), pts.GetNumPoints());
\r
857 WriteLogMsg(strBuf);
\r
859 if (polyFirstPoint)
\r
861 polyFirstPoint = false;
\r
862 pts.AddNewPolyAtEnd();
\r
865 //Do we really need to put a cap on this???
\r
867 // if (pts.GetNumPoints() < 16)
\r
869 pt.x = lParam & 0xFFFF, pt.y = lParam >> 16;
\r
871 DPtoLP(hdc, &pt, 1);
\r
872 ReleaseDC(hWnd, hdc);
\r
873 // Append a point to the end of the structure
\r
874 pts += IPoint(pt.x, pt.y, (wParam & (MK_SHIFT | MK_CONTROL) ? false : true));
\r
875 ptHighlight = pts.GetNumPoints() - 1;
\r
876 InvalidateRect(hWnd, NULL, TRUE);
\r
879 wsprintf(strBuf, " --> [# polys: %u, # points: %u]\xD\xA", pts.GetNumPolys(), pts.GetNumPoints());
\r
880 WriteLogMsg(strBuf);
\r
883 else if (currentTool == TOOLSelect || currentTool == TOOLPolySelect)
\r
885 if (pts.GetNumPoints() > 0)
\r
887 pt.x = pts.GetX(ptHighlight), pt.y = pts.GetY(ptHighlight);
\r
889 LPtoDP(hdc, &pt, 1);
\r
890 ClientToScreen(hWnd, &pt);
\r
891 SetCursorPos(pt.x, pt.y);
\r
892 ReleaseDC(hWnd, hdc);
\r
894 if (wParam & (MK_SHIFT | MK_CONTROL))
\r
895 pts.SetOnCurve(ptHighlight, !pts.GetOnCurve(ptHighlight));
\r
898 else if (currentTool == TOOLDelPt)
\r
900 if (pts.GetNumPoints() > 0)
\r
902 // if (ptHighlight != -1)
\r
904 //This assumes that WM_MOUSEMOVE happens before this!
\r
905 //The above commented out line should take care of this contingency... !!! FIX !!!
\r
906 pts.DeletePoint(ptHighlight);
\r
907 InvalidateRect(hWnd, NULL, TRUE);
\r
917 if (currentTool == TOOLScroll || currentTool == TOOLZoom)
\r
924 SetCursor(hCur[currentTool]);
\r
926 // Extract current point from lParam/calc offset from previous point
\r
928 pt.x = lParam & 0xFFFF, pt.y = lParam >> 16;
\r
929 ptOffset.x = pt.x - ptPrevious.x,
\r
930 ptOffset.y = pt.y - ptPrevious.y;
\r
934 if (currentTool == TOOLScroll)
\r
936 // NOTE: OffsetViewportOrg operates in DEVICE UNITS...
\r
939 OffsetViewportOrgEx(hdc, ptOffset.x, ptOffset.y, NULL);
\r
940 ReleaseDC(hWnd, hdc);
\r
942 // this shows that it works, so the logic above must be faulty...
\r
943 // And it is. It should convert the coords first, then do the subtraction to figure the offset...
\r
945 // Then multiply it by the scaling factor. Whee!
\r
947 InvalidateRect(hWnd, NULL, TRUE);
\r
948 // SendMessage(hWnd, WM_PAINT, NULL, NULL);
\r
950 else if (currentTool == TOOLAddPt || currentTool == TOOLAddPoly || currentTool == TOOLSelect)
\r
952 if (currentTool != TOOLAddPt || pts.GetNumPoints() > 0)//yecch.
\r
955 pt2.x = pt.x, pt2.y = pt.y;
\r
956 // Should also set onCurve here as well, depending on keystate
\r
959 DPtoLP(hdc, &pt2, 1);
\r
960 pts.SetXY(ptHighlight, pt2.x, pt2.y);
\r
961 ReleaseDC(hWnd, hdc);
\r
962 InvalidateRect(hWnd, NULL, TRUE);
\r
965 else if (currentTool == TOOLPolySelect)
\r
967 if (pts.GetNumPoints() > 0)
\r
970 pt2.x = pt.x, pt2.y = pt.y;
\r
971 // Should also set onCurve here as well, depending on keystate
\r
974 DPtoLP(hdc, &pt2, 1);
\r
975 pts.OffsetPoly(pts.GetPoly(ptHighlight), pt2.x - pts.GetX(ptHighlight), pt2.y - pts.GetY(ptHighlight));
\r
976 ReleaseDC(hWnd, hdc);
\r
977 InvalidateRect(hWnd, NULL, TRUE);
\r
983 if (currentTool == TOOLSelect || currentTool == TOOLDelPt || currentTool == TOOLAddPt
\r
984 || currentTool == TOOLPolySelect)// || currentTool == TOOLAddPoly)
\r
987 pt2.x = pt.x, pt2.y = pt.y;
\r
989 DPtoLP(hdc, &pt2, 1);
\r
990 ReleaseDC(hWnd, hdc);
\r
992 double closest = 1.0e+99;
\r
994 for(int i=0; i<pts.GetNumPoints(); i++)
\r
996 double dist = ((pt2.x - pts.GetX(i)) * (pt2.x - pts.GetX(i)))
\r
997 + ((pt2.y - pts.GetY(i)) * (pt2.y - pts.GetY(i)));
\r
999 if (dist < closest)
\r
1000 closest = dist, ptHighlight = i;
\r
1003 if (ptHighlight != oldPtHighlight)
\r
1005 oldPtHighlight = ptHighlight;
\r
1006 InvalidateRect(hWnd, NULL, TRUE);
\r
1009 // What follows here looks like voodoo, but is really simple. What we do is
\r
1010 // check to see if the mouse point has a perpendicular intersection with any of
\r
1011 // the line segments. If it does, calculate the length of the perpendicular
\r
1012 // and choose the smallest length. If there is no perpendicular, then choose the
\r
1013 // length of line connecting the closer of either the first endpoint or the
\r
1014 // second and choose the smallest of those.
\r
1016 // There is one bit of math that looks like voodoo to me ATM--will explain once
\r
1017 // I understand it better (the calculation of the length of the perpendicular).
\r
1019 if (pts.GetNumPoints() > 1 && currentTool == TOOLAddPt)
\r
1021 double smallest = 1.0e+99;
\r
1023 for(int i=0; i<pts.GetNumPoints(); i++)
\r
1025 int32 p1x = pts.GetX(i), p1y = pts.GetY(i),
\r
1026 p2x = pts.GetX(pts.GetNext(i)), p2y = pts.GetY(pts.GetNext(i));
\r
1028 vector ls(p2x, p2y, 0, p1x, p1y, 0), v1(pt2.x, pt2.y, 0, p1x, p1y, 0),
\r
1029 v2(pt2.x, pt2.y, 0, p2x, p2y, 0);
\r
1030 double pp = ls.dot(v1) / ls.length(), dist;
\r
1031 // Geometric interpretation:
\r
1032 // pp is the paremeterized point on the vector ls where the perpendicular intersects ls.
\r
1033 // If pp < 0, then the perpendicular lies beyond the 1st endpoint. If pp > length of ls,
\r
1034 // then the perpendicular lies beyond the 2nd endpoint.
\r
1037 dist = v1.length();
\r
1038 else if (pp > ls.length())
\r
1039 dist = v2.length();
\r
1040 else // distance = ?Det?(ls, v1) / |ls|
\r
1041 dist = abs((ls.x * v1.y - v1.x * ls.y) / ls.length());
\r
1043 //The answer to the above looks like it might be found here:
\r
1045 //If the segment endpoints are s and e, and the point is p, then the test for the perpendicular
\r
1046 //intercepting the segment is equivalent to insisting that the two dot products {s-e}.{s-p} and
\r
1047 //{e-s}.{e-p} are both non-negative. Perpendicular distance from the point to the segment is
\r
1048 //computed by first computing the area of the triangle the three points form, then dividing by the
\r
1049 //length of the segment. Distances are done just by the Pythagorean theorem. Twice the area of the
\r
1050 //triangle formed by three points is the determinant of the following matrix:
\r
1056 //(???) By translating the start point to the origin, this can be rewritten as:
\r
1057 //By subtracting row 1 from all rows, you get the following:
\r
1060 //(ex - sx) (ey - sy) 0
\r
1061 //(px - sx) (py - sy) 0
\r
1063 //which greatly simplifies the calculation of the determinant.
\r
1065 if (dist < smallest)
\r
1066 smallest = dist, ptNextHighlight = pts.GetNext(i), ptHighlight = i;
\r
1069 if (ptNextHighlight != oldPtNextHighlight)
\r
1071 oldPtNextHighlight = ptNextHighlight;
\r
1072 InvalidateRect(hWnd, NULL, TRUE);
\r
1078 ptPrevious.x = pt.x, ptPrevious.y = pt.y;
\r
1084 if (((NMHDR *)lParam)->code == TTN_NEEDTEXT)
\r
1086 LoadString(hInst, ((TOOLTIPTEXT *)lParam)->hdr.idFrom + 0x80, toolTipTxt, 16);
\r
1087 ((TOOLTIPTEXT *)lParam)->lpszText = toolTipTxt;
\r
1092 case WM_MENUSELECT:
\r
1094 statusBarTxt[0] = 0; // Clear status bar text
\r
1095 uint16 flags = wParam >> 16; // Extract flags
\r
1097 if (!(flags & MFT_SEPARATOR))
\r
1099 uint16 id = wParam & 0xFFFF;
\r
1101 if (flags & MF_POPUP)
\r
1103 if (flags & MF_SYSMENU)
\r
1106 id = IDM_FILEMENU + wParam;
\r
1109 LoadString(hInst, id, statusBarTxt, 64);
\r
1112 SendMessage(hStatusBar, SB_SETTEXT, 0 + SBT_NOBORDERS, (LPARAM)statusBarTxt);
\r
1117 uint16 cmd = wParam & 0xFFFF;
\r
1119 if (cmd == IDM_NEW)
\r
1121 // call CmdIDM_NEW
\r
1123 else if (cmd == IDM_OPEN)
\r
1125 // call SaveChanges
\r
1127 // movmov ofn.hwndOwner, eax, hMainWnd
\r
1128 // mov ofn.Flags, OFN_PATHMUSTEXIST + OFN_FILEMUSTEXIST
\r
1129 // invoke GetOpenFileName, ADDR ofn
\r
1133 //szDMsg1a BYTE "Could not open the file (GetOpenFileName)...", 0
\r
1134 //szDMsg1b BYTE "Open error!", 0
\r
1135 //szDMsg1c BYTE "About to attempt to open file...", 0
\r
1137 ////invoke MessageBox, hWnd, ADDR szDMsg1a, ADDR szDMsg1b, MB_ICONERROR or MB_OK
\r
1138 //invoke MessageBox, hMainWnd, ADDR szDMsg1c, ADDR szFile, MB_ICONERROR or MB_OK
\r
1139 // invoke LoadTTF, ADDR szFile
\r
1142 // // <<< FILE OPEN CODE HERE >>>
\r
1143 // or fFileStatus, NAMEDbit
\r
1144 // and fFileStatus, NOT CHANGEDbit
\r
1145 // call NewWindowName
\r
1146 // mov eax, TRUE // return TRUE
\r
1148 // //��������������������������������������
\r
1149 //OpenError: invoke GetLastError
\r
1150 // // <<< FILE OPEN ERROR CODE HERE >>>
\r
1152 // zero eax // return FALSE
\r
1155 else if (cmd == IDM_SAVEAS)
\r
1157 // and fFileStatus, NOT NAMEDbit
\r
1158 // call CmdIDM_SAVE
\r
1160 else if (cmd == IDM_SAVE)
\r
1162 // call CmdIDM_SAVE
\r
1164 else if (cmd == IDM_ABOUT)
\r
1165 DialogBoxParam(hInst, MAKEINTRESOURCE(IDD_ABOUT), hMainWnd, AboutProc, NULL);
\r
1166 else if (cmd == IDM_EXIT)
\r
1167 SendMessage(hWnd, WM_CLOSE, 0, 0);
\r
1168 else if (cmd == ID_TBCHARWIN)
\r
1170 ShowWindow(hCharWnd, (IsWindowVisible(hCharWnd) ? SW_HIDE : SW_SHOWNOACTIVATE));
\r
1173 wpC.length = sizeof(WINDOWPLACEMENT);
\r
1174 GetWindowPlacement(hCharWnd, &wpC);
\r
1175 wsprintf(strBuf, "Char window showCmd = %08X...\n", wpC.showCmd);
\r
1176 WriteLogMsg(strBuf);
\r
1180 return DefWindowProc(hWnd, msgID, wParam, lParam);
\r
1185 return DefWindowProc(hWnd, msgID, wParam, lParam);
\r
1192 // Initialize TTF data
\r
1194 void CreateNewDoc(void)
\r
1199 // Save changes to document before quitting
\r
1201 bool SaveChanges(void)
\r
1209 // ABOUT Dialog WndProc
\r
1211 BOOL CALLBACK AboutProc(HWND hDlg, UINT msgID, WPARAM wParam, LPARAM lParam)
\r
1215 case WM_INITDIALOG:
\r
1217 MiscCenterWnd(hDlg, hMainWnd);
\r
1222 if (wParam == IDOK || wParam == IDCANCEL)
\r
1223 EndDialog(hDlg, TRUE);
\r
1235 // Character Window WndProc
\r
1237 WndProcCW PROC STDCALL, hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM
\r
1239 mov eax, uMsg // pickup our message
\r
1240 .IF (eax == WM_PAINT)
\r
1241 mov eax, 0 // Non-sense... (placeholder!)
\r
1242 // Scan conversion etc. goes here...
\r
1243 .ELSEIF (eax == WM_LBUTTONDOWN)
\r
1244 invoke SetCapture, hCharWnd
\r
1245 .ELSEIF (eax == WM_LBUTTONUP)
\r
1246 invoke ReleaseCapture
\r
1247 invoke SetFocus, hMainWnd // Make sure the main wnd keeps focus!
\r
1248 .ELSEIF (eax == WM_NCLBUTTONDOWN)
\r
1249 invoke DefWindowProc, hWnd, uMsg, wParam, lParam // Let it do its thing
\r
1250 invoke SetFocus, hMainWnd // Make sure the main wnd keeps focus!
\r
1252 DefProc: invoke DefWindowProc, hWnd, uMsg, wParam, lParam
\r
1259 // Character Window WndProc
\r
1261 LRESULT CALLBACK WndProcCW(HWND hWnd, UINT msgID, WPARAM wParam, LPARAM lParam)
\r
1266 return DefWindowProc(hWnd, msgID, wParam, lParam);
\r
1273 // Function prototypes
\r
1275 int32 FindSelectedTool(void);
\r
1278 // Tool Palette WndProc
\r
1280 LRESULT CALLBACK WndProcTP(HWND hWnd, UINT msgID, WPARAM wParam, LPARAM lParam)
\r
1285 static uint32 prevTool = -1;
\r
1291 hdc = BeginPaint(hWnd, &ps);
\r
1292 HDC newDC = CreateCompatibleDC(NULL);
\r
1293 SelectObject(newDC, hBMToolPal1);
\r
1294 BitBlt(hdc, 0, 0, sizeTPBM.x, sizeTPBM.y, newDC, 0, 0, SRCCOPY);
\r
1297 // This is crappy. Find some way to tighten this up!
\r
1298 int32 tool = FindSelectedTool();
\r
1302 newDC = CreateCompatibleDC(NULL);
\r
1303 SelectObject(newDC, hBMToolPal1);
\r
1304 //need ul corner of bitmap, ul corner of dest, width/height
\r
1305 pt.x = sizeStamp.x * (tool & 0x03), pt.y = sizeStamp.y * (tool >> 2);
\r
1306 BitBlt(hdc, pt.x, pt.y, sizeStamp.x, sizeStamp.y, newDC, pt.x, pt.y, NOTSRCCOPY);
\r
1310 EndPaint(hWnd, &ps);
\r
1313 case WM_MOUSEMOVE:
\r
1315 int32 tool = FindSelectedTool();
\r
1317 if (tool != prevTool)
\r
1320 InvalidateRect(hWnd, NULL, FALSE);
\r
1325 case WM_RBUTTONUP:
\r
1327 int32 tool = FindSelectedTool(), oldTool = currentTool;
\r
1330 currentTool = tool;
\r
1332 if (currentTool != TOOLSelect && currentTool != TOOLDelPt && currentTool != TOOLAddPt
\r
1333 && currentTool != TOOLPolySelect)
\r
1336 if (currentTool != oldTool)
\r
1337 InvalidateRect(hMainWnd, NULL, TRUE);
\r
1339 if (currentTool == TOOLAddPoly)
\r
1343 polyFirstPoint = true;
\r
1345 wsprintf(strBuf, "--> Selected poly tool, polyFirstPoint is %s\n", polyFirstPoint ? "true" : "false");
\r
1346 WriteLogMsg(strBuf);
\r
1351 ShowWindow(hToolPalWnd, SW_HIDE);
\r
1352 SetFocus(hMainWnd); // Make sure the main wnd keeps focus!
\r
1357 return DefWindowProc(hWnd, msgID, wParam, lParam);
\r
1364 // Find which tool we're pointing at
\r
1365 // Use: xcoord = mouse.x / (bmsize.x/4), ycoord = mouse.y / (bmsize.y/2)
\r
1367 int32 FindSelectedTool(void)
\r
1371 GetCursorPos(&pt);
\r
1372 ScreenToClient(hToolPalWnd, &pt);
\r
1374 uint32 x = (uint32)pt.x / sizeStamp.x, y = (uint32)pt.y / sizeStamp.y, tool = -1;
\r
1376 if (x < 4 && y < 2)
\r
1378 tool = (y * 4) + x;
\r
1381 // tool = -1; // 7 has no tool...
\r
1389 // Misc center window
\r
1391 void MiscCenterWnd(HWND hChild, HWND hParent)
\r
1393 RECT parent, child;
\r
1395 if (!GetWindowRect(hParent, &parent) || !GetWindowRect(hChild, &child))
\r
1398 int32 x = parent.left + (((parent.right - parent.left) - (child.right - child.left)) / 2),
\r
1399 y = parent.top + (((parent.bottom - parent.top) - (child.bottom - child.top)) / 2);
\r
1403 else if (x > GetSystemMetrics(SM_CXFULLSCREEN) - (child.right - child.left))
\r
1404 x = GetSystemMetrics(SM_CXFULLSCREEN) - (child.right - child.left);
\r
1408 else if (y > GetSystemMetrics(SM_CYFULLSCREEN) - (child.bottom - child.top))
\r
1409 y = GetSystemMetrics(SM_CYFULLSCREEN) - (child.bottom - child.top);
\r
1411 SetWindowPos(hChild, NULL, x, y, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
\r
1415 // Allow only one instance
\r
1417 bool OnlyOneInstance(void)
\r
1419 HWND window = FindWindow(className, NULL);
\r
1421 if (window == NULL)
\r
1424 ShowWindow(window, SW_SHOWNORMAL);
\r
1425 SetWindowPos(window, NULL, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE);
\r
1431 // Load/Allocate all resources
\r
1433 bool LoadResources(void)
\r
1435 hCur[0] = LoadCursor(hInst, MAKEINTRESOURCE(IDC_CURSOR1));
\r
1436 hCur[1] = LoadCursor(hInst, MAKEINTRESOURCE(IDC_CURSOR2));
\r
1437 hCur[2] = LoadCursor(hInst, MAKEINTRESOURCE(IDC_CURSOR3));
\r
1438 hCur[3] = LoadCursor(hInst, MAKEINTRESOURCE(IDC_CURSOR4));
\r
1439 hCur[4] = LoadCursor(hInst, MAKEINTRESOURCE(IDC_CURSOR5));
\r
1440 hCur[5] = LoadCursor(hInst, MAKEINTRESOURCE(IDC_CURSOR6));
\r
1441 hCur[6] = LoadCursor(hInst, MAKEINTRESOURCE(IDC_CURSOR7));
\r
1442 hCur[7] = LoadCursor(hInst, MAKEINTRESOURCE(IDC_CURSOR8));
\r
1446 hBMToolPal1 = LoadBitmap(hInst, MAKEINTRESOURCE(IDB_TOOLPAL1));
\r
1447 GetObject(hBMToolPal1, sizeof(bm), &bm);
\r
1451 sizeTPBM.x = bm.bmWidth, sizeTPBM.y = bm.bmHeight;
\r
1452 sizeStamp.x = bm.bmWidth / 4, sizeStamp.y = bm.bmHeight / 2;
\r
1454 hBluePen1 = CreatePen(PS_DOT, 1, 0x00FF0000);
\r
1455 hRedPen1 = CreatePen(PS_SOLID, 1, 0x000000FF);
\r
1456 hGreenPen1 = CreatePen(PS_SOLID, 1, 0x0000AF00);
\r
1457 hBlackPen1 = CreatePen(PS_SOLID, 1, 0x00000000);
\r
1459 LOGBRUSH lb = { BS_NULL, 0, 0 };
\r
1461 hNullBrush = CreateBrushIndirect(&lb);
\r
1467 // Deallocate all resources
\r
1469 void DeallocateResources(void)
\r
1471 DeleteObject(hBMToolPal1);
\r
1472 DeleteObject(hBluePen1);
\r
1473 DeleteObject(hRedPen1);
\r
1474 DeleteObject(hGreenPen1);
\r
1475 DeleteObject(hBlackPen1);
\r
1476 DeleteObject(hNullBrush);
\r
1480 // Save all application specific data, so we can pick up where we last left off...
\r
1482 void SaveAppState(void)
\r
1484 SetINIInt("Main", "flags", wpM.flags);
\r
1485 SetINIInt("Main", "showCmd", wpM.showCmd);
\r
1486 SetINIInt("Main", "x1", wpM.rcNormalPosition.left);
\r
1487 SetINIInt("Main", "y1", wpM.rcNormalPosition.top);
\r
1488 SetINIInt("Main", "x2", wpM.rcNormalPosition.right);
\r
1489 SetINIInt("Main", "y2", wpM.rcNormalPosition.bottom);
\r
1491 SetINIInt("Main", "vpx", ptVPM.x);
\r
1492 SetINIInt("Main", "vpy", ptVPM.y);
\r
1494 SetINIInt("Char", "flags", wpC.flags);
\r
1495 SetINIInt("Char", "showCmd", wpC.showCmd);
\r
1496 SetINIInt("Char", "x1", wpC.rcNormalPosition.left);
\r
1497 SetINIInt("Char", "y1", wpC.rcNormalPosition.top);
\r
1498 SetINIInt("Char", "x2", wpC.rcNormalPosition.right);
\r
1499 SetINIInt("Char", "y2", wpC.rcNormalPosition.bottom);
\r
1501 // Need to write out currently opened font, character looking at, other misc. crap
\r
1502 // SetINIString("Main", "currentFile", pDoc->GetPathName());
\r
1503 // SetINIInt("Main", "currentChar", pDoc->character_num);
\r
1507 // Restore all application specific data previously saved
\r
1509 bool RestoreAppState(void)
\r
1513 WINDOWPLACEMENT wp;
\r
1514 wp.length = sizeof(WINDOWPLACEMENT);
\r
1515 GetWindowPlacement(hMainWnd, &wp);
\r
1517 wp.flags = GetINIInt("Main", "flags", wp.flags);
\r
1518 wp.showCmd = GetINIInt("Main", "showCmd", wp.showCmd);
\r
1519 wp.rcNormalPosition.left = GetINIInt("Main", "x1", wp.rcNormalPosition.left);
\r
1520 wp.rcNormalPosition.top = GetINIInt("Main", "y1", wp.rcNormalPosition.top);
\r
1521 wp.rcNormalPosition.right = GetINIInt("Main", "x2", wp.rcNormalPosition.right);
\r
1522 wp.rcNormalPosition.bottom = GetINIInt("Main", "y2", wp.rcNormalPosition.bottom);
\r
1524 SetWindowPlacement(hMainWnd, &wp);
\r
1528 hdc = GetDC(hMainWnd);
\r
1529 GetViewportOrgEx(hdc, &pt);
\r
1531 pt.x = GetINIInt("Main", "vpx", pt.x);
\r
1532 pt.y = GetINIInt("Main", "vpy", pt.y);
\r
1534 SetViewportOrgEx(hdc, pt.x, pt.y, NULL);
\r
1535 ReleaseDC(hMainWnd, hdc);
\r
1537 GetWindowPlacement(hCharWnd, &wp);
\r
1539 wp.flags = GetINIInt("Char", "flags", wp.flags);
\r
1540 wp.showCmd = GetINIInt("Char", "showCmd", wp.showCmd);
\r
1541 wp.rcNormalPosition.left = GetINIInt("Char", "x1", wp.rcNormalPosition.left);
\r
1542 wp.rcNormalPosition.top = GetINIInt("Char", "y1", wp.rcNormalPosition.top);
\r
1543 wp.rcNormalPosition.right = GetINIInt("Char", "x2", wp.rcNormalPosition.right);
\r
1544 wp.rcNormalPosition.bottom = GetINIInt("Char", "y2", wp.rcNormalPosition.bottom);
\r
1546 SetWindowPlacement(hCharWnd, &wp);
\r
1548 if (wp.showCmd == SW_HIDE)
\r
1549 SendMessage(hToolBar, TB_SETSTATE, ID_TBCHARWIN, MAKELONG(TBSTATE_ENABLED, 0));
\r
1551 // CString lastFile = theApplicationObject.GetProfileString(version, "currentFile", "");
\r
1552 // int lastChar = theApplicationObject.GetProfileInt(version, "currentChar", 0);
\r
1553 // if (lastFile.GetLength())
\r
1555 // // Attempt to restore the last session by open the last file used, etc...
\r
1556 // if (!pDoc->m_myFont.Load(lastFile))
\r
1558 // // Err, make sure you can support any allegations you make below, buddy!
\r
1559 // AfxMessageBox("The last file opened with TTF Edit\n\rseems to have been moved or deleted.");
\r
1563 // pDoc->m_myFont.SetGlyph(lastChar); // Set TTF object to last used char
\r
1564 // pDoc->character_num = lastChar;
\r
1565 // pDoc->SetPathName(lastFile);
\r
1567 // BYTE name[512];
\r
1568 // pDoc->m_myFont.GetCharName(lastChar, name);
\r
1569 // m_wndOwned.SetWindowText((char *)name);
\r
1579 bool Initialization(void)
\r
1583 if (!LoadResources())
\r
1586 RtlFillMemory(&wcex, sizeof(WNDCLASSEX), 0);
\r
1587 wcex.cbSize = sizeof(WNDCLASSEX);
\r
1588 wcex.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
\r
1589 wcex.lpfnWndProc = WndProc;
\r
1590 wcex.hInstance = hInst;
\r
1591 wcex.hIcon = LoadIcon(hInst, MAKEINTRESOURCE(IDI_ICON));
\r
1592 wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
\r
1593 wcex.lpszMenuName = MAKEINTRESOURCE(IDM_MENU);
\r
1594 wcex.lpszClassName = className;
\r
1595 wcex.hIconSm = (HICON)LoadImage(hInst, MAKEINTRESOURCE(IDI_ICON), IMAGE_ICON, 16, 16, NULL);
\r
1597 if (!RegisterClassEx(&wcex))
\r
1600 hMainWnd = CreateWindowEx(NULL, className, className, WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN,
\r
1601 0, 0, 0x1A0, 0x180, NULL, NULL, hInst, NULL);
\r
1606 ShowWindow(hMainWnd, nCmdShow);
\r
1607 UpdateWindow(hMainWnd);
\r
1609 // Character window creation
\r
1611 wcex.lpfnWndProc = WndProcCW;
\r
1612 wcex.lpszMenuName = NULL;
\r
1613 wcex.lpszClassName = CNCharWin;
\r
1614 wcex.hCursor = LoadCursor(NULL, IDC_ARROW); // Owned windows have "regular" cursors
\r
1616 if (!RegisterClassEx(&wcex))
\r
1619 hCharWnd = CreateWindowEx(WS_EX_WINDOWEDGE | WS_EX_TOOLWINDOW, CNCharWin,
\r
1620 curCharName, WS_POPUP | WS_CAPTION | WS_VISIBLE | WS_THICKFRAME,
\r
1621 100, 100, 120, 120, hMainWnd, NULL, hInst, NULL);
\r
1626 ShowWindow(hCharWnd, SW_SHOWNORMAL);
\r
1627 UpdateWindow(hCharWnd);
\r
1628 SetFocus(hMainWnd); // Make sure main wnd has focus!
\r
1630 // Tool palette window creation
\r
1632 wcex.lpfnWndProc = WndProcTP;
\r
1633 wcex.lpszClassName = CNToolPal;
\r
1635 if (!RegisterClassEx(&wcex))
\r
1638 hToolPalWnd = CreateWindowEx(WS_EX_WINDOWEDGE, CNToolPal, NULL, WS_POPUP,
\r
1639 0, 0, sizeTPBM.x, sizeTPBM.y, hMainWnd, NULL, hInst, NULL);
\r
1644 // Note: A better way to handle this would be to have a sub that registers ALL
\r
1645 // classes beforehand and passes a TRUE/FALSE back depending on whether or not
\r
1646 // all the classes were able to be registered or not, THEN create the windows
\r
1647 // and controls...
\r
1649 RestoreAppState(); // Restore app related stuff
\r