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();