From 517c5edbfa3ae8dc5df4b7d651ba6a3cbc5278de Mon Sep 17 00:00:00 2001 From: Jonathan Westhues Date: Fri, 18 Sep 2009 00:14:15 -0800 Subject: [PATCH] Add user interface to modify styles: change the color, line width, line width units, on-screen and export visibility. So now we can use that to modify the default styles, or to create custom styles. Also add code to draw fat lines, with round endcaps, since gl doesn't do that. Next we need some user interface to assign styles to entities, and to make all the export file formats support the style attributes. [git-p4: depot-paths = "//depot/solvespace/": change = 2029] --- draw.cpp | 4 +- drawentity.cpp | 71 ++++++++++-- dsc.h | 9 +- file.cpp | 1 + graphicswin.cpp | 9 +- request.cpp | 4 + sketch.h | 15 ++- solvespace.cpp | 8 ++ solvespace.h | 2 + style.cpp | 282 ++++++++++++++++++++++++++++++++++++++++++++++-- textscreens.cpp | 20 ++-- textwin.cpp | 2 + ui.h | 23 ++++ util.cpp | 11 ++ 14 files changed, 428 insertions(+), 33 deletions(-) diff --git a/draw.cpp b/draw.cpp index ddefaf7..73bd6a0 100644 --- a/draw.cpp +++ b/draw.cpp @@ -1018,7 +1018,9 @@ void GraphicsWindow::Paint(int w, int h) { glDepthFunc(GL_LEQUAL); if(SS.AllGroupsOkay()) { - glClearColor(0, 0, 0, 1.0f); + glClearColor(REDf(SS.backgroundColor), + GREENf(SS.backgroundColor), + BLUEf(SS.backgroundColor), 1.0f); } else { // Draw a different background whenever we're having solve problems. DWORD rgb = Style::Color(Style::DRAW_ERROR); diff --git a/drawentity.cpp b/drawentity.cpp index 1153579..19360a6 100644 --- a/drawentity.cpp +++ b/drawentity.cpp @@ -10,14 +10,70 @@ char *Entity::DescriptionString(void) { } } -void Entity::LineDrawOrGetDistance(Vector a, Vector b) { +void Entity::FatLineEndcap(Vector p, Vector u, Vector v) { + // A table of cos and sin of (pi*i/10 + pi/2), as i goes from 0 to 10 + static const double Circle[11][2] = { + { 0.0000, 1.0000 }, + { -0.3090, 0.9511 }, + { -0.5878, 0.8090 }, + { -0.8090, 0.5878 }, + { -0.9511, 0.3090 }, + { -1.0000, 0.0000 }, + { -0.9511, -0.3090 }, + { -0.8090, -0.5878 }, + { -0.5878, -0.8090 }, + { -0.3090, -0.9511 }, + { 0.0000, -1.0000 }, + }; + glBegin(GL_TRIANGLE_FAN); + for(int i = 0; i <= 10; i++) { + double c = Circle[i][0], s = Circle[i][1]; + glxVertex3v(p.Plus(u.ScaledBy(c)).Plus(v.ScaledBy(s))); + } + glEnd(); +} + +void Entity::FatLine(Vector a, Vector b) { + // The half-width of the line we're drawing. + double hw = (dogd.lineWidth/SS.GW.scale) / 2; + Vector ab = b.Minus(a); + Vector gn = (SS.GW.projRight).Cross(SS.GW.projUp); + Vector abn = (ab.Cross(gn)).WithMagnitude(1); + abn = abn.Minus(gn.ScaledBy(gn.Dot(abn))); + // So now abn is normal to the projection of ab into the screen, so the + // line will always have constant thickness as the view is rotated. + + abn = abn.WithMagnitude(hw); + ab = gn.Cross(abn); + ab = ab. WithMagnitude(hw); + + // The body of a line is a quad + glBegin(GL_QUADS); + glxVertex3v(a.Minus(abn)); + glxVertex3v(b.Minus(abn)); + glxVertex3v(b.Plus (abn)); + glxVertex3v(a.Plus (abn)); + glEnd(); + // And the line has two semi-circular end caps. + FatLineEndcap(a, ab, abn); + FatLineEndcap(b, ab.ScaledBy(-1), abn); +} + +void Entity::LineDrawOrGetDistance(Vector a, Vector b, bool maybeFat) { if(dogd.drawing) { // Draw lines from active group in front of those from previous glxDepthRangeOffset((group.v == SS.GW.activeGroup.v) ? 4 : 3); - glBegin(GL_LINES); - glxVertex3v(a); - glxVertex3v(b); - glEnd(); + // Narrow lines are drawn as lines, but fat lines must be drawn as + // filled polygons, to get the line join style right. + if(!maybeFat || dogd.lineWidth < 3) { + glBegin(GL_LINES); + glxVertex3v(a); + glxVertex3v(b); + glEnd(); + } else { + FatLine(a, b); + } + glxDepthRangeOffset(0); } else { Point2d ap = SS.GW.ProjectPoint(a); @@ -100,7 +156,8 @@ void Entity::DrawAll(void) { void Entity::Draw(void) { hStyle hs = Style::ForEntity(h); - glLineWidth(Style::Width(hs)); + dogd.lineWidth = Style::Width(hs); + glLineWidth((float)dogd.lineWidth); glxColorRGB(Style::Color(hs)); dogd.drawing = true; @@ -479,7 +536,7 @@ void Entity::DrawOrGetDistance(void) { int i; for(i = 0; i < sel.l.n; i++) { SEdge *se = &(sel.l.elem[i]); - LineDrawOrGetDistance(se->a, se->b); + LineDrawOrGetDistance(se->a, se->b, true); } sel.Clear(); } diff --git a/dsc.h b/dsc.h index 4af4082..11403a8 100644 --- a/dsc.h +++ b/dsc.h @@ -219,15 +219,18 @@ public: int n; int elemsAllocated; - H AddAndAssignId(T *t) { - int i; + DWORD MaximumId(void) { DWORD id = 0; + int i; for(i = 0; i < n; i++) { id = max(id, elem[i].h.v); } + return id; + } - t->h.v = (id + 1); + H AddAndAssignId(T *t) { + t->h.v = (MaximumId() + 1); Add(t); return t->h; diff --git a/file.cpp b/file.cpp index 814df0f..17dee19 100644 --- a/file.cpp +++ b/file.cpp @@ -152,6 +152,7 @@ const SolveSpace::SaveTable SolveSpace::SAVED[] = { { 'c', "Constraint.disp.style", 'x', &(SS.sv.c.disp.style) }, { 's', "Style.h.v", 'x', &(SS.sv.s.h.v) }, + { 's', "Style.name", 'N', &(SS.sv.s.name) }, { 's', "Style.width", 'f', &(SS.sv.s.width) }, { 's', "Style.widthHow", 'd', &(SS.sv.s.widthHow) }, { 's', "Style.color", 'x', &(SS.sv.s.color) }, diff --git a/graphicswin.cpp b/graphicswin.cpp index 4555327..510af86 100644 --- a/graphicswin.cpp +++ b/graphicswin.cpp @@ -585,9 +585,16 @@ void GraphicsWindow::MenuEdit(int id) { switch(id) { case MNU_UNSELECT_ALL: SS.GW.GroupSelection(); + // If there's nothing selected to de-select, and no operation + // to cancel, then perhaps they want to return to the home + // screen in the text window. if(SS.GW.gs.n == 0 && SS.GW.pending.operation == 0) { if(!TextEditControlIsVisible()) { - SS.TW.ClearSuper(); + if(SS.TW.shown.screen == TextWindow::SCREEN_STYLE_INFO) { + SS.TW.GoToScreen(TextWindow::SCREEN_LIST_OF_STYLES); + } else { + SS.TW.ClearSuper(); + } } } SS.GW.ClearSuper(); diff --git a/request.cpp b/request.cpp index 21f16e4..f45abe5 100644 --- a/request.cpp +++ b/request.cpp @@ -65,6 +65,7 @@ void Request::Generate(IdList *entity, // Generate the entity that's specific to this request. e.type = et; e.group = group; + e.style = style; e.workplane = workplane; e.construction = construction; e.str.strcpy(str.str); @@ -79,6 +80,7 @@ void Request::Generate(IdList *entity, // points start from entity 1, except for datum point case p.h = h.entity(i+(et ? 1 : 0)); p.group = group; + p.style = style; if(workplane.v == Entity::FREE_IN_3D.v) { p.type = Entity::POINT_IN_3D; @@ -101,6 +103,7 @@ void Request::Generate(IdList *entity, n.workplane = workplane; n.h = h.entity(32); n.group = group; + n.style = style; if(workplane.v == Entity::FREE_IN_3D.v) { n.type = Entity::NORMAL_IN_3D; n.param[0] = AddParam(param, h.param(32+0)); @@ -125,6 +128,7 @@ void Request::Generate(IdList *entity, d.workplane = workplane; d.h = h.entity(64); d.group = group; + d.style = style; d.type = Entity::DISTANCE; d.param[0] = AddParam(param, h.param(64)); entity->Add(&d); diff --git a/sketch.h b/sketch.h index 9aef0ec..5eb23a1 100644 --- a/sketch.h +++ b/sketch.h @@ -414,8 +414,11 @@ public: Point2d mp; double dmin; Vector refp; + double lineWidth; } dogd; // state for drawing or getting distance (for hit testing) - void LineDrawOrGetDistance(Vector a, Vector b); + void FatLine(Vector a, Vector b); + void FatLineEndcap(Vector p, Vector u, Vector v); + void LineDrawOrGetDistance(Vector a, Vector b, bool maybeFat=false); void DrawOrGetDistance(void); bool IsVisible(void); @@ -620,12 +623,12 @@ public: static const int DRAW_ERROR = 12; static const int DIM_SOLID = 13; - static const int FIRST_CUSTOM = 0x1000; + static const int FIRST_CUSTOM = 0x100; NameStr name; - static const int WIDTH_MM = 0; - static const int WIDTH_PIXELS = 1; + static const int WIDTH_AS_MM = 0; + static const int WIDTH_AS_PIXELS = 1; double width; int widthHow; DWORD color; @@ -645,6 +648,8 @@ public: static char *CnfColor(char *prefix); static char *CnfWidth(char *prefix); static char *CnfPrefixToName(char *prefix); + + static void CreateAllDefaultStyles(void); static void CreateDefaultStyle(hStyle h); static void FreezeDefaultStyles(void); static void LoadFactoryDefaults(void); @@ -655,6 +660,8 @@ public: static DWORD Color(int hs, bool forExport=false); static float Width(int hs); static hStyle ForEntity(hEntity he); + + char *DescriptionString(void); }; diff --git a/solvespace.cpp b/solvespace.cpp index 2f5333e..579bc60 100644 --- a/solvespace.cpp +++ b/solvespace.cpp @@ -74,6 +74,8 @@ void SolveSpace::Init(char *cmdLine) { exportShadedTriangles = CnfThawDWORD(1, "ExportShadedTriangles"); // Export pwl curves (instead of exact) always exportPwlCurves = CnfThawDWORD(0, "ExportPwlCurves"); + // Background color on-screen + backgroundColor = CnfThawDWORD(RGB(0, 0, 0), "BackgroundColor"); // Whether export canvas size is fixed or derived from bbox exportCanvasSizeAuto = CnfThawDWORD(1, "ExportCanvasSizeAuto"); // Margins for automatic canvas size @@ -156,6 +158,8 @@ void SolveSpace::Exit(void) { CnfFreezeDWORD(exportShadedTriangles, "ExportShadedTriangles"); // Export pwl curves (instead of exact) always CnfFreezeDWORD(exportPwlCurves, "ExportPwlCurves"); + // Background color on-screen + CnfFreezeDWORD(backgroundColor, "BackgroundColor"); // Whether export canvas size is fixed or derived from bbox CnfFreezeDWORD(exportCanvasSizeAuto, "ExportCanvasSizeAuto"); // Margins for automatic canvas size @@ -274,6 +278,10 @@ void SolveSpace::AfterNewFile(void) { // Then zoom to fit again, to fit the triangles GW.ZoomToFit(false); + // Create all the default styles; they'll get created on the fly anyways, + // but can't hurt to do it now. + Style::CreateAllDefaultStyles(); + UpdateWindowTitle(); } diff --git a/solvespace.h b/solvespace.h index 9de5233..1adba2c 100644 --- a/solvespace.h +++ b/solvespace.h @@ -207,6 +207,7 @@ void MakeMatrix(double *mat, double a11, double a12, double a13, double a14, double a41, double a42, double a43, double a44); void MakePathRelative(char *base, char *path); void MakePathAbsolute(char *base, char *path); +bool StringAllPrintable(char *str); class System { public: @@ -531,6 +532,7 @@ public: float exportOffset; int drawBackFaces; int showToolbar; + DWORD backgroundColor; int exportShadedTriangles; int exportPwlCurves; int exportCanvasSizeAuto; diff --git a/style.cpp b/style.cpp index f63bed4..2f0c178 100644 --- a/style.cpp +++ b/style.cpp @@ -1,5 +1,7 @@ #include "solvespace.h" +#define clamp01(x) (max(0, min(1, (x)))) + const Style::Default Style::Defaults[] = { { ACTIVE_GRP, "ActiveGrp", RGBf(1.0, 1.0, 1.0), 1.5, }, { CONSTRUCTION, "Construction", RGBf(0.1, 0.7, 0.1), 1.5, }, @@ -30,7 +32,9 @@ char *Style::CnfWidth(char *prefix) { char *Style::CnfPrefixToName(char *prefix) { static char name[100]; - int i = 0, j = 0; + int i = 0, j; + strcpy(name, "def-"); + j = 4; while(prefix[i] && j < 90) { if(isupper(prefix[i]) && i != 0) { name[j++] = '-'; @@ -42,7 +46,15 @@ char *Style::CnfPrefixToName(char *prefix) { return name; } +void Style::CreateAllDefaultStyles(void) { + const Default *d; + for(d = &(Defaults[0]); d->h.v; d++) { + (void)Get(d->h); + } +} + void Style::CreateDefaultStyle(hStyle h) { + bool isDefaultStyle = true; const Default *d; for(d = &(Defaults[0]); d->h.v; d++) { if(d->h.v == h.v) break; @@ -51,17 +63,22 @@ void Style::CreateDefaultStyle(hStyle h) { // Not a default style; so just create it the same as our default // active group entity style. d = &(Defaults[0]); + isDefaultStyle = false; } Style ns; ZERO(&ns); ns.color = CnfThawDWORD(d->color, CnfColor(d->cnfPrefix)); ns.width = CnfThawFloat((float)(d->width), CnfWidth(d->cnfPrefix)); - ns.widthHow = WIDTH_PIXELS; + ns.widthHow = WIDTH_AS_PIXELS; ns.visible = true; ns.exportable = true; - ns.name.strcpy(CnfPrefixToName(d->cnfPrefix)); ns.h = h; + if(isDefaultStyle) { + ns.name.strcpy(CnfPrefixToName(d->cnfPrefix)); + } else { + ns.name.strcpy("new-custom-style"); + } SK.style.Add(&ns); } @@ -73,11 +90,12 @@ void Style::LoadFactoryDefaults(void) { s->color = d->color; s->width = d->width; - s->widthHow = WIDTH_PIXELS; + s->widthHow = WIDTH_AS_PIXELS; s->visible = true; s->exportable = true; s->name.strcpy(CnfPrefixToName(d->cnfPrefix)); } + SS.backgroundColor = RGB(0, 0, 0); } void Style::FreezeDefaultStyles(void) { @@ -131,9 +149,9 @@ DWORD Style::Color(hStyle h, bool forExport) { float Style::Width(hStyle h) { double r = 1.0; Style *s = Get(h); - if(s->widthHow == WIDTH_MM) { + if(s->widthHow == WIDTH_AS_MM) { r = s->width * SS.GW.scale; - } else if(s->widthHow == WIDTH_PIXELS) { + } else if(s->widthHow == WIDTH_AS_PIXELS) { r = s->width; } // This returns a float because glLineWidth expects a float, avoid casts. @@ -165,3 +183,255 @@ hStyle Style::ForEntity(hEntity he) { return hs; } +char *Style::DescriptionString(void) { + static char ret[100]; + if(name.str[0]) { + sprintf(ret, "s%03x-%s", h.v, name.str); + } else { + sprintf(ret, "s%03x-(unnamed)", h.v); + } + return ret; +} + + +void TextWindow::ScreenShowListOfStyles(int link, DWORD v) { + SS.TW.GoToScreen(SCREEN_LIST_OF_STYLES); +} +void TextWindow::ScreenShowStyleInfo(int link, DWORD v) { + SS.TW.GoToScreen(SCREEN_STYLE_INFO); + SS.TW.shown.style.v = v; +} + +void TextWindow::ScreenLoadFactoryDefaultStyles(int link, DWORD v) { + Style::LoadFactoryDefaults(); + SS.TW.GoToScreen(SCREEN_LIST_OF_STYLES); +} + +void TextWindow::ScreenCreateCustomStyle(int link, DWORD v) { + SS.UndoRemember(); + DWORD vs = max(Style::FIRST_CUSTOM, SK.style.MaximumId() + 1); + hStyle hs = { vs }; + (void)Style::Get(hs); +} + +void TextWindow::ScreenChangeBackgroundColor(int link, DWORD v) { + DWORD rgb = SS.backgroundColor; + char str[300]; + sprintf(str, "%.2f, %.2f, %.2f", REDf(rgb), GREENf(rgb), BLUEf(rgb)); + ShowTextEditControl(v, 3, str); + SS.TW.edit.meaning = EDIT_BACKGROUND_COLOR; +} + +void TextWindow::ShowListOfStyles(void) { + Printf(true, "%Ft color style-name"); + + bool darkbg = false; + Style *s; + for(s = SK.style.First(); s; s = SK.style.NextAfter(s)) { + Printf(false, "%Bp %Bp %Bp %Fl%Ll%f%D%s%E", + darkbg ? 'd' : 'a', + 0x80000000 | s->color, + darkbg ? 'd' : 'a', + ScreenShowStyleInfo, s->h.v, + s->DescriptionString()); + + darkbg = !darkbg; + } + + Printf(true, " %Fl%Ll%fcreate a new custom style%E", + &ScreenCreateCustomStyle); + + Printf(false, ""); + + DWORD rgb = SS.backgroundColor; + Printf(false, "%Ft background color (r, g, b)%E"); + Printf(false, "%Ba %@, %@, %@ %Fl%D%f%Ll[change]%E", + REDf(rgb), GREENf(rgb), BLUEf(rgb), + top[rows-1] + 2, &ScreenChangeBackgroundColor); + + Printf(false, ""); + Printf(false, " %Fl%Ll%fload factory defaults%E", + &ScreenLoadFactoryDefaultStyles); +} + + +void TextWindow::ScreenChangeStyleName(int link, DWORD v) { + hStyle hs = { v }; + Style *s = Style::Get(hs); + ShowTextEditControl(10, 13, s->name.str); + SS.TW.edit.style = hs; + SS.TW.edit.meaning = EDIT_STYLE_NAME; +} + +void TextWindow::ScreenDeleteStyle(int link, DWORD v) { + SS.UndoRemember(); + hStyle hs = { v }; + Style *s = SK.style.FindByIdNoOops(hs); + if(s) { + SK.style.RemoveById(hs); + // And it will get recreated automatically if something is still using + // the style, so no need to do anything else. + } + SS.TW.GoToScreen(SCREEN_LIST_OF_STYLES); + InvalidateGraphics(); +} + +void TextWindow::ScreenChangeStyleWidth(int link, DWORD v) { + hStyle hs = { v }; + Style *s = Style::Get(hs); + char str[300]; + if(s->widthHow == Style::WIDTH_AS_PIXELS) { + sprintf(str, "%.2f", s->width); + } else { + strcpy(str, SS.MmToString(s->width)); + } + ShowTextEditControl(16, 8, str); + SS.TW.edit.style = hs; + SS.TW.edit.meaning = EDIT_STYLE_WIDTH; +} + +void TextWindow::ScreenChangeStyleColor(int link, DWORD v) { + hStyle hs = { v }; + Style *s = Style::Get(hs); + char str[300]; + sprintf(str, "%.2f, %.2f, %.2f", + REDf(s->color), GREENf(s->color), BLUEf(s->color)); + ShowTextEditControl(13, 12, str); + SS.TW.edit.style = hs; + SS.TW.edit.meaning = EDIT_STYLE_COLOR; +} + +void TextWindow::ScreenChangeStyleYesNo(int link, DWORD v) { + SS.UndoRemember(); + hStyle hs = { v }; + Style *s = Style::Get(hs); + switch(link) { + case 'w': + if(s->widthHow == Style::WIDTH_AS_PIXELS) { + s->widthHow = Style::WIDTH_AS_MM; + } else { + s->widthHow = Style::WIDTH_AS_PIXELS; + } + break; + + case 'e': + s->exportable = !(s->exportable); + break; + + case 'v': + s->visible = !(s->visible); + break; + } + InvalidateGraphics(); +} + +bool TextWindow::EditControlDoneForStyles(char *str) { + Style *s; + switch(edit.meaning) { + case EDIT_STYLE_WIDTH: + SS.UndoRemember(); + s = Style::Get(edit.style); + if(s->widthHow == Style::WIDTH_AS_MM) { + s->width = SS.StringToMm(str); + } else { + s->width = atof(str); + } + s->width = max(0, s->width); + return true; + + case EDIT_BACKGROUND_COLOR: + case EDIT_STYLE_COLOR: { + double r, g, b; + if(sscanf(str, "%lf, %lf, %lf", &r, &g, &b)==3) { + r = clamp01(r); + g = clamp01(g); + b = clamp01(b); + if(edit.meaning == EDIT_STYLE_COLOR) { + SS.UndoRemember(); + s = Style::Get(edit.style); + s->color = RGBf(r, g, b); + } else { + SS.backgroundColor = RGBf(r, g, b); + } + } else { + Error("Bad format: specify color as r, g, b"); + } + return true; + } + case EDIT_STYLE_NAME: + if(!StringAllPrintable(str) || !*str) { + Error("Invalid characters. Allowed are: A-Z a-z 0-9 _ -"); + } else { + SS.UndoRemember(); + s = Style::Get(edit.style); + s->name.strcpy(str); + } + return true; + + default: return false; + } +} + +void TextWindow::ShowStyleInfo(void) { + Printf(true, "%Fl%f%Ll(back to list of styles)%E", &ScreenShowListOfStyles); + + Style *s = Style::Get(shown.style); + + if(s->h.v < Style::FIRST_CUSTOM) { + Printf(true, "%FtSTYLE %E%s ", s->DescriptionString()); + } else { + Printf(true, "%FtSTYLE %E%s " + "[%Fl%Ll%D%frename%E/%Fl%Ll%D%fdel%E]", + s->DescriptionString(), + s->h.v, &ScreenChangeStyleName, + s->h.v, &ScreenDeleteStyle); + } + + Printf(true, "%FtCOLOR %E%Bp %Bd (%@, %@, %@) %D%f%Ll%Fl[change]%E", + 0x80000000 | s->color, + REDf(s->color), GREENf(s->color), BLUEf(s->color), + s->h.v, ScreenChangeStyleColor); + + if(s->widthHow == Style::WIDTH_AS_PIXELS) { + Printf(true, "%FtWIDTH %E%@ %D%f%Ll%Fl[change]%E", + s->width, + s->h.v, &ScreenChangeStyleWidth); + } else { + Printf(true, "%FtWIDTH %E%s %D%f%Ll%Fl[change]%E", + SS.MmToString(s->width), + s->h.v, &ScreenChangeStyleWidth); + } + + char *unit = (SS.viewUnits == SolveSpace::UNIT_INCHES) ? "inches" : "mm"; + bool widthpx = (s->widthHow == Style::WIDTH_AS_PIXELS); + + if(s->h.v < Style::FIRST_CUSTOM) { + Printf(false,"%FtUNITS %Fspixels%E"); + } else { + Printf(false,"%FtUNITS %Fh%D%f%Lw%s%E%Fs%s%E / %Fh%D%f%Lw%s%E%Fs%s%E", + s->h.v, &ScreenChangeStyleYesNo, + ( widthpx ? "" : "pixels"), + ( widthpx ? "pixels" : ""), + s->h.v, &ScreenChangeStyleYesNo, + (!widthpx ? "" : unit), + (!widthpx ? unit : "")); + } + + if(s->h.v >= Style::FIRST_CUSTOM) { + Printf(true, "%FtSHOW %Fh%D%f%Lv%s%E%Fs%s%E / %Fh%D%f%Lv%s%E%Fs%s%E", + s->h.v, &ScreenChangeStyleYesNo, + ( s->visible ? "" : "yes"), + ( s->visible ? "yes" : ""), + s->h.v, &ScreenChangeStyleYesNo, + (!s->visible ? "" : "no"), + (!s->visible ? "no" : "")); + Printf(false,"%FtEXPORT %Fh%D%f%Le%s%E%Fs%s%E / %Fh%D%f%Le%s%E%Fs%s%E", + s->h.v, &ScreenChangeStyleYesNo, + ( s->exportable ? "" : "yes"), + ( s->exportable ? "yes" : ""), + s->h.v, &ScreenChangeStyleYesNo, + (!s->exportable ? "" : "no"), + (!s->exportable ? "no" : "")); + } +} + diff --git a/textscreens.cpp b/textscreens.cpp index c9a9cd2..53125f6 100644 --- a/textscreens.cpp +++ b/textscreens.cpp @@ -143,7 +143,8 @@ void TextWindow::ShowListOfGroups(void) { Printf(true, " %Fl%Ls%fshow all%E / %Fl%Lh%fhide all%E", &(TextWindow::ScreenShowGroupsSpecial), &(TextWindow::ScreenShowGroupsSpecial)); - Printf(false, " %Fl%Ls%fconfiguration%E", + Printf(true, " %Fl%Ls%fline styles%E / %Fl%Ls%fconfiguration%E", + &(TextWindow::ScreenShowListOfStyles), &(TextWindow::ScreenShowConfiguration)); // Show license info @@ -915,7 +916,8 @@ void TextWindow::EditControlDone(char *s) { // already constrained, because that would break // convergence. if(c == 0) { - SK.GetParam(g->h.param(3))->val = PI/(2*ev); + double copies = (g->skipFirst) ? (ev + 1) : ev; + SK.GetParam(g->h.param(3))->val = PI/(2*copies); } } @@ -927,14 +929,7 @@ void TextWindow::EditControlDone(char *s) { break; } case EDIT_GROUP_NAME: { - char *t; - bool invalid = false; - for(t = s; *t; t++) { - if(!(isalnum(*t) || *t == '-' || *t == '_')) { - invalid = true; - } - } - if(invalid || !*s) { + if(!StringAllPrintable(s) || !*s) { Error("Invalid characters. Allowed are: A-Z a-z 0-9 _ -"); } else { SS.UndoRemember(); @@ -942,7 +937,6 @@ void TextWindow::EditControlDone(char *s) { Group *g = SK.GetGroup(edit.group); g->name.strcpy(s); } - SS.unsaved = true; break; } case EDIT_LIGHT_INTENSITY: @@ -1081,6 +1075,10 @@ void TextWindow::EditControlDone(char *s) { } break; } + + default: + EditControlDoneForStyles(s); + break; } InvalidateGraphics(); SS.later.showTW = true; diff --git a/textwin.cpp b/textwin.cpp index bee48e9..2ae8bd1 100644 --- a/textwin.cpp +++ b/textwin.cpp @@ -224,6 +224,8 @@ void TextWindow::Show(void) { case SCREEN_CONFIGURATION: ShowConfiguration(); break; case SCREEN_STEP_DIMENSION: ShowStepDimension(); break; case SCREEN_MESH_VOLUME: ShowMeshVolume(); break; + case SCREEN_LIST_OF_STYLES: ShowListOfStyles(); break; + case SCREEN_STYLE_INFO: ShowStyleInfo(); break; } } Printf(false, ""); diff --git a/ui.h b/ui.h index 0e3b0f2..32cc89f 100644 --- a/ui.h +++ b/ui.h @@ -52,10 +52,13 @@ public: static const int SCREEN_CONFIGURATION = 3; static const int SCREEN_STEP_DIMENSION = 4; static const int SCREEN_MESH_VOLUME = 5; + static const int SCREEN_LIST_OF_STYLES = 6; + static const int SCREEN_STYLE_INFO = 7; typedef struct { int screen; hGroup group; + hStyle style; hConstraint constraint; bool dimIsDistance; @@ -89,11 +92,17 @@ public: // For the step dimension screen static const int EDIT_STEP_DIM_FINISH = 40; static const int EDIT_STEP_DIM_STEPS = 41; + // For the styles stuff + static const int EDIT_STYLE_WIDTH = 50; + static const int EDIT_STYLE_COLOR = 51; + static const int EDIT_STYLE_NAME = 52; + static const int EDIT_BACKGROUND_COLOR = 53; struct { int meaning; int i; hGroup group; hRequest request; + hStyle style; } edit; static void ReportHowGroupSolved(hGroup hg); @@ -107,6 +116,8 @@ public: void ShowGroupInfo(void); void ShowGroupSolveInfo(void); void ShowConfiguration(void); + void ShowListOfStyles(void); + void ShowStyleInfo(void); void ShowStepDimension(void); void ShowMeshVolume(void); // Special screen, based on selection @@ -142,6 +153,13 @@ public: static void ScreenChangeHelixParameter(int link, DWORD v); static void ScreenColor(int link, DWORD v); + static void ScreenShowListOfStyles(int link, DWORD v); + static void ScreenShowStyleInfo(int link, DWORD v); + static void ScreenDeleteStyle(int link, DWORD v); + static void ScreenChangeStyleYesNo(int link, DWORD v); + static void ScreenCreateCustomStyle(int link, DWORD v); + static void ScreenLoadFactoryDefaultStyles(int link, DWORD v); + static void ScreenShowConfiguration(int link, DWORD v); static void ScreenGoToWebsite(int link, DWORD v); @@ -168,7 +186,12 @@ public: static void ScreenChangeCameraTangent(int link, DWORD v); static void ScreenChangeExportScale(int link, DWORD v); static void ScreenChangeExportOffset(int link, DWORD v); + static void ScreenChangeStyleName(int link, DWORD v); + static void ScreenChangeStyleWidth(int link, DWORD v); + static void ScreenChangeStyleColor(int link, DWORD v); + static void ScreenChangeBackgroundColor(int link, DWORD v); + bool EditControlDoneForStyles(char *s); void EditControlDone(char *s); }; diff --git a/util.cpp b/util.cpp index 9a462a7..02fd0b1 100644 --- a/util.cpp +++ b/util.cpp @@ -77,6 +77,17 @@ void MakePathAbsolute(char *basep, char *pathp) { strcpy(pathp, out); } +bool StringAllPrintable(char *str) +{ + char *t; + for(t = str; *t; t++) { + if(!(isalnum(*t) || *t == '-' || *t == '_')) { + return false; + } + } + return true; +} + void MakeMatrix(double *mat, double a11, double a12, double a13, double a14, double a21, double a22, double a23, double a24, double a31, double a32, double a33, double a34,