diff --git a/drawconstraint.cpp b/drawconstraint.cpp index fbe9f1b..d519fd7 100644 --- a/drawconstraint.cpp +++ b/drawconstraint.cpp @@ -25,7 +25,7 @@ void Constraint::LineDrawOrGetDistance(Vector a, Vector b) { } } -void Constraint::DrawOrGetDistance(void) { +void Constraint::DrawOrGetDistance(Vector *labelPos) { if(!SS.GW.showConstraints) return; // Unit vectors that describe our current view of the scene. @@ -40,6 +40,7 @@ void Constraint::DrawOrGetDistance(void) { Vector bp = SS.GetEntity(ptB)->PointGetCoords(); Vector ref = ((ap.Plus(bp)).ScaledBy(0.5)).Plus(disp.offset); + if(labelPos) *labelPos = ref; Vector ab = ap.Minus(bp); Vector ar = ap.Minus(ref); @@ -103,7 +104,7 @@ void Constraint::DrawOrGetDistance(void) { void Constraint::Draw(void) { dogd.drawing = true; - DrawOrGetDistance(); + DrawOrGetDistance(NULL); } double Constraint::GetDistance(Point2d mp) { @@ -111,8 +112,18 @@ double Constraint::GetDistance(Point2d mp) { dogd.mp = mp; dogd.dmin = 1e12; - DrawOrGetDistance(); + DrawOrGetDistance(NULL); return dogd.dmin; } +Vector Constraint::GetLabelPos(void) { + dogd.drawing = false; + dogd.mp.x = 0; dogd.mp.y = 0; + dogd.dmin = 1e12; + + Vector p; + DrawOrGetDistance(&p); + return p; +} + diff --git a/expr.cpp b/expr.cpp index e50da46..9af47e1 100644 --- a/expr.cpp +++ b/expr.cpp @@ -49,6 +49,7 @@ int Expr::Children(void) { Expr *Expr::DeepCopy(void) { Expr *n = AllocExpr(); *n = *this; + n->marker = 0; int c = n->Children(); if(c > 0) n->a = a->DeepCopy(); if(c > 1) n->b = b->DeepCopy(); @@ -58,6 +59,7 @@ Expr *Expr::DeepCopy(void) { Expr *Expr::DeepCopyKeep(void) { Expr *n = (Expr *)MemAlloc(sizeof(Expr)); *n = *this; + n->marker = 0xbad2feed; n->a = n->b = NULL; int c = n->Children(); if(c > 0) n->a = a->DeepCopyKeep(); @@ -65,6 +67,17 @@ Expr *Expr::DeepCopyKeep(void) { return n; } +void Expr::FreeKeep(Expr **e) { + if(!(*e)) oops(); + + int c = (*e)->Children(); + if(c > 0) FreeKeep(&((*e)->a)); + if(c > 1) FreeKeep(&((*e)->b)); + if((*e)->marker != 0xbad2feed) oops(); + MemFree(*e); + *e = NULL; +} + Expr *Expr::DeepCopyWithParamsAsPointers(IdList *firstTry, IdList *thenTry) { diff --git a/expr.h b/expr.h index 56ae471..326789e 100644 --- a/expr.h +++ b/expr.h @@ -6,6 +6,8 @@ class Expr; class Expr { public: + DWORD marker; + // A parameter, by the hParam handle static const int PARAM = 0; // A parameter, by a pointer straight in to the param table (faster, @@ -76,8 +78,9 @@ public: void PrintW(void); // worker // Make a copy of an expression that won't get blown away when we - // do a FreeAllExprs() + // do a FreeAllExprs(), or free it later. Expr *DeepCopyKeep(void); + static void FreeKeep(Expr **f); // or a copy that will Expr *DeepCopy(void); // number of child nodes: 0 (e.g. constant), 1 (sqrt), or 2 (+) diff --git a/graphicswin.cpp b/graphicswin.cpp index 851f839..cded333 100644 --- a/graphicswin.cpp +++ b/graphicswin.cpp @@ -229,6 +229,7 @@ void GraphicsWindow::EnsureValidActives(void) { void GraphicsWindow::MenuEdit(int id) { switch(id) { case MNU_UNSELECT_ALL: + HideGraphicsEditControl(); SS.GW.ClearSelection(); SS.GW.pendingOperation = 0; SS.GW.pendingDescription = NULL; @@ -316,6 +317,8 @@ void GraphicsWindow::UpdateDraggedPoint(Vector *pos, double mx, double my) { void GraphicsWindow::MouseMoved(double x, double y, bool leftDown, bool middleDown, bool rightDown, bool shiftDown, bool ctrlDown) { + if(GraphicsEditControlIsVisible()) return; + Point2d mp = { x, y }; if(middleDown) { @@ -460,6 +463,8 @@ void GraphicsWindow::GroupSelection(void) { } void GraphicsWindow::MouseMiddleDown(double x, double y) { + if(GraphicsEditControlIsVisible()) return; + orig.offset = offset; orig.projUp = projUp; orig.projRight = projRight; @@ -479,6 +484,8 @@ hRequest GraphicsWindow::AddRequest(int type) { } void GraphicsWindow::MouseLeftDown(double mx, double my) { + if(GraphicsEditControlIsVisible()) return; + // Make sure the hover is up to date. MouseMoved(mx, my, false, false, false, false, false); orig.mouse.x = mx; @@ -542,6 +549,31 @@ void GraphicsWindow::MouseLeftDown(double mx, double my) { InvalidateGraphics(); } +void GraphicsWindow::MouseLeftDoubleClick(double mx, double my) { + if(GraphicsEditControlIsVisible()) return; + + if(hover.constraint.v) { + ClearSelection(); + Constraint *c = SS.GetConstraint(hover.constraint); + Vector p3 = c->GetLabelPos(); + Point2d p2 = ProjectPoint(p3); + ShowGraphicsEditControl((int)p2.x, (int)p2.y, c->exprA->Print()); + constraintBeingEdited = hover.constraint; + } +} + +void GraphicsWindow::EditControlDone(char *s) { + Expr *e = Expr::FromString(s); + if(e) { + Constraint *c = SS.GetConstraint(constraintBeingEdited); + Expr::FreeKeep(&(c->exprA)); + c->exprA = e->DeepCopyKeep(); + HideGraphicsEditControl(); + } else { + Error("Not a valid number or expression: '%s'", s); + } +} + void GraphicsWindow::MouseScroll(double x, double y, int delta) { double offsetRight = offset.Dot(projRight); double offsetUp = offset.Dot(projUp); diff --git a/sketch.h b/sketch.h index 12184ae..c1483d0 100644 --- a/sketch.h +++ b/sketch.h @@ -246,9 +246,11 @@ public: double dmin; } dogd; // state for drawing or getting distance (for hit testing) void LineDrawOrGetDistance(Vector a, Vector b); + void DrawOrGetDistance(Vector *labelPos); + double GetDistance(Point2d mp); + Vector GetLabelPos(void); void Draw(void); - void DrawOrGetDistance(void); bool HasLabel(void); diff --git a/solvespace.h b/solvespace.h index 87cb4bc..827b7a7 100644 --- a/solvespace.h +++ b/solvespace.h @@ -35,6 +35,10 @@ BOOL GetOpenFile(char *file, char *defExtension, char *selPattern); void CheckMenuById(int id, BOOL checked); void EnableMenuById(int id, BOOL checked); +void ShowGraphicsEditControl(int x, int y, char *s); +void HideGraphicsEditControl(void); +BOOL GraphicsEditControlIsVisible(void); + void InvalidateGraphics(void); void InvalidateText(void); SDWORD GetMilliseconds(void); diff --git a/ui.h b/ui.h index 1dd0f06..d57b762 100644 --- a/ui.h +++ b/ui.h @@ -144,6 +144,8 @@ public: char *pendingDescription; hRequest AddRequest(int type); + // The constraint that is being edited with the on-screen textbox. + hConstraint constraintBeingEdited; // The current selection. class Selection { @@ -194,6 +196,7 @@ public: void MouseLeftDoubleClick(double x, double y); void MouseMiddleDown(double x, double y); void MouseScroll(double x, double y, int delta); + void EditControlDone(char *s); }; diff --git a/win32/w32main.cpp b/win32/w32main.cpp index 2b5fc39..8b5b90c 100644 --- a/win32/w32main.cpp +++ b/win32/w32main.cpp @@ -24,6 +24,7 @@ int TextWndScrollPos; int TextWndRows; HWND GraphicsWnd; +HWND GraphicsEditControl; HMENU SubMenus[100]; struct { int x, y; @@ -291,6 +292,18 @@ LRESULT CALLBACK TextWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) static BOOL ProcessKeyDown(WPARAM wParam) { + if(GraphicsEditControlIsVisible() && wParam != VK_ESCAPE) { + if(wParam == VK_RETURN) { + char s[1024]; + memset(s, 0, sizeof(s)); + SendMessage(GraphicsEditControl, WM_GETTEXT, 900, (LPARAM)s); + SS.GW.EditControlDone(s); + return TRUE; + } else { + return FALSE; + } + } + int c; switch(wParam) { case VK_OEM_PLUS: c = '+'; break; @@ -383,6 +396,32 @@ void InvalidateText(void) InvalidateRect(TextWnd, NULL, FALSE); } +void ShowGraphicsEditControl(int x, int y, char *s) +{ + RECT r; + GetClientRect(GraphicsWnd, &r); + x = x + (r.right - r.left)/2; + y = (r.bottom - r.top)/2 - y; + + // (x, y) are the bottom left, but the edit control is placed by its + // top left corner + y -= 21; + + MoveWindow(GraphicsEditControl, x, y, 120, 21, TRUE); + ShowWindow(GraphicsEditControl, SW_SHOW); + SendMessage(GraphicsEditControl, WM_SETTEXT, 0, (LPARAM)s); + SendMessage(GraphicsEditControl, EM_SETSEL, 0, strlen(s)); + SetFocus(GraphicsEditControl); +} +void HideGraphicsEditControl(void) +{ + ShowWindow(GraphicsEditControl, SW_HIDE); +} +BOOL GraphicsEditControlIsVisible(void) +{ + return IsWindowVisible(GraphicsEditControl); +} + LRESULT CALLBACK GraphicsWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { @@ -407,6 +446,7 @@ LRESULT CALLBACK GraphicsWndProc(HWND hwnd, UINT msg, WPARAM wParam, case WM_MOUSEMOVE: case WM_LBUTTONDOWN: + case WM_LBUTTONDBLCLK: case WM_MBUTTONDOWN: { int x = LOWORD(lParam); int y = HIWORD(lParam); @@ -421,6 +461,8 @@ LRESULT CALLBACK GraphicsWndProc(HWND hwnd, UINT msg, WPARAM wParam, if(msg == WM_LBUTTONDOWN) { SS.GW.MouseLeftDown(x, y); + } else if(msg == WM_LBUTTONDBLCLK) { + SS.GW.MouseLeftDoubleClick(x, y); } else if(msg == WM_MBUTTONDOWN) { SS.GW.MouseMiddleDown(x, y); } else if(msg == WM_MOUSEMOVE) { @@ -441,11 +483,13 @@ LRESULT CALLBACK GraphicsWndProc(HWND hwnd, UINT msg, WPARAM wParam, break; } case WM_COMMAND: { - int id = LOWORD(wParam); - for(int i = 0; SS.GW.menu[i].level >= 0; i++) { - if(id == SS.GW.menu[i].id) { - (SS.GW.menu[i].fn)((GraphicsWindow::MenuId)id); - break; + if(HIWORD(wParam) == 0) { + int id = LOWORD(wParam); + for(int i = 0; SS.GW.menu[i].level >= 0; i++) { + if(id == SS.GW.menu[i].id) { + (SS.GW.menu[i].fn)((GraphicsWindow::MenuId)id); + break; + } } } break; @@ -604,6 +648,11 @@ static void CreateMainWindows(void) 600, 300, 200, 200, NULL, top, Instance, NULL); if(!GraphicsWnd) oops(); + GraphicsEditControl = CreateWindowEx(WS_EX_CLIENTEDGE, WC_EDIT, "", + WS_CHILD | ES_AUTOHSCROLL | WS_TABSTOP | WS_CLIPSIBLINGS, + 50, 50, 100, 21, GraphicsWnd, NULL, Instance, NULL); + SendMessage(GraphicsEditControl, WM_SETFONT, (WPARAM)FixedFont, TRUE); + // The text window, with a comand line and some textual information // about the sketch. @@ -643,12 +692,7 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, { Instance = hInstance; - // Create the root windows: one for control, with text, and one for - // the graphics - CreateMainWindows(); - - ThawWindowPos(TextWnd); - ThawWindowPos(GraphicsWnd); + InitCommonControls(); // A monospaced font FixedFont = CreateFont(TEXT_HEIGHT-2, TEXT_WIDTH, 0, 0, FW_REGULAR, FALSE, @@ -662,6 +706,13 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, if(!LinkFont) LinkFont = (HFONT)GetStockObject(SYSTEM_FONT); + // Create the root windows: one for control, with text, and one for + // the graphics + CreateMainWindows(); + + ThawWindowPos(TextWnd); + ThawWindowPos(GraphicsWnd); + // Create the heap that we use to store Exprs. FreeAllExprs();