solvespace/style.cpp

438 lines
14 KiB
C++
Raw Normal View History

#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, },
{ INACTIVE_GRP, "InactiveGrp", RGBf(0.5, 0.3, 0.0), 1.5, },
{ DATUM, "Datum", RGBf(0.0, 0.8, 0.0), 1.5, },
{ SOLID_EDGE, "SolidEdge", RGBf(0.8, 0.8, 0.8), 1.0, },
{ CONSTRAINT, "Constraint", RGBf(1.0, 0.1, 1.0), 1.0, },
{ SELECTED, "Selected", RGBf(1.0, 0.0, 0.0), 1.5, },
{ HOVERED, "Hovered", RGBf(1.0, 1.0, 0.0), 1.5, },
{ CONTOUR_FILL, "ContourFill", RGBf(0.0, 0.1, 0.1), 1.0, },
{ NORMALS, "Normals", RGBf(0.0, 0.4, 0.4), 1.0, },
{ ANALYZE, "Analyze", RGBf(0.0, 1.0, 1.0), 1.0, },
{ DRAW_ERROR, "DrawError", RGBf(1.0, 0.0, 0.0), 8.0, },
{ DIM_SOLID, "DimSolid", RGBf(0.1, 0.1, 0.1), 1.0, },
{ 0, NULL, 0, 0.0, },
};
char *Style::CnfColor(char *prefix) {
static char name[100];
sprintf(name, "Style_%s_Color", prefix);
return name;
}
char *Style::CnfWidth(char *prefix) {
static char name[100];
sprintf(name, "Style_%s_Width", prefix);
return name;
}
char *Style::CnfPrefixToName(char *prefix) {
static char name[100];
int i = 0, j;
strcpy(name, "def-");
j = 4;
while(prefix[i] && j < 90) {
if(isupper(prefix[i]) && i != 0) {
name[j++] = '-';
}
name[j++] = tolower(prefix[i]);
i++;
}
name[j++] = '\0';
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;
}
if(!d->h.v) {
// 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_AS_PIXELS;
ns.visible = true;
ns.exportable = true;
ns.h = h;
if(isDefaultStyle) {
ns.name.strcpy(CnfPrefixToName(d->cnfPrefix));
} else {
ns.name.strcpy("new-custom-style");
}
SK.style.Add(&ns);
}
void Style::LoadFactoryDefaults(void) {
const Default *d;
for(d = &(Defaults[0]); d->h.v; d++) {
Style *s = Get(d->h);
s->color = d->color;
s->width = d->width;
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) {
const Default *d;
for(d = &(Defaults[0]); d->h.v; d++) {
CnfFreezeDWORD(Color(d->h), CnfColor(d->cnfPrefix));
CnfFreezeFloat((float)Width(d->h), CnfWidth(d->cnfPrefix));
}
}
//-----------------------------------------------------------------------------
// Look up a style by its handle. If that style does not exist, then create
// the style, according to our table of default styles.
//-----------------------------------------------------------------------------
Style *Style::Get(hStyle h) {
Style *s = SK.style.FindByIdNoOops(h);
if(s) {
// It exists, good.
return s;
} else {
// It doesn't exist; so we should create it and then return that.
CreateDefaultStyle(h);
return SK.style.FindById(h);
}
}
//-----------------------------------------------------------------------------
// A couple of wrappers, so that I can call these functions with either an
// hStyle or with the integer corresponding to that hStyle.v.
//-----------------------------------------------------------------------------
DWORD Style::Color(int s, bool forExport) {
hStyle hs = { s };
return Color(hs, forExport);
}
float Style::Width(int s) {
hStyle hs = { s };
return Width(hs);
}
//-----------------------------------------------------------------------------
// Return the color associated with our style as 8-bit RGB.
//-----------------------------------------------------------------------------
DWORD Style::Color(hStyle h, bool forExport) {
Style *s = Get(h);
return s->color;
}
//-----------------------------------------------------------------------------
// Return the width associated with our style in pixels..
//-----------------------------------------------------------------------------
float Style::Width(hStyle h) {
double r = 1.0;
Style *s = Get(h);
if(s->widthHow == WIDTH_AS_MM) {
r = s->width * SS.GW.scale;
} else if(s->widthHow == WIDTH_AS_PIXELS) {
r = s->width;
}
// This returns a float because glLineWidth expects a float, avoid casts.
return (float)r;
}
//-----------------------------------------------------------------------------
// Return the appropriate style for our entity. If the entity has a style
// explicitly assigned, then it's that style. Otherwise it's the appropriate
// default style.
//-----------------------------------------------------------------------------
hStyle Style::ForEntity(hEntity he) {
Entity *e = SK.GetEntity(he);
// If the entity has a special style, use that. If that style doesn't
// exist yet, then it will get created automatically later.
if(e->style.v != 0) {
return e->style;
}
// Otherwise, we use the default rules.
hStyle hs;
if(e->group.v != SS.GW.activeGroup.v) {
hs.v = INACTIVE_GRP;
} else if(e->construction) {
hs.v = CONSTRUCTION;
} else {
hs.v = ACTIVE_GRP;
}
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" : ""));
}
}