solvespace/src/style.cpp
whitequark eb5501ecd6 Implement a platform abstraction for settings.
This commit mostly just changes the settings code to be in line with
the rest of the platform abstractions, although it also fixes some
settings names to be consistent with others, and uses native bool
types where applicable.

This commit also makes settings-related operations much less
wasteful, not that it should matter.
2018-07-17 15:01:58 +00:00

872 lines
30 KiB
C++

//-----------------------------------------------------------------------------
// Implementation of a cosmetic line style, which determines the color and
// other appearance of a line or curve on-screen and in exported files. Some
// styles are predefined, and others can be created by the user.
//
// Copyright 2008-2013 Jonathan Westhues.
//-----------------------------------------------------------------------------
#include "solvespace.h"
const Style::Default Style::Defaults[] = {
{ { ACTIVE_GRP }, "ActiveGrp", RGBf(1.0, 1.0, 1.0), 1.5, 4 },
{ { CONSTRUCTION }, "Construction", RGBf(0.1, 0.7, 0.1), 1.5, 0 },
{ { INACTIVE_GRP }, "InactiveGrp", RGBf(0.5, 0.3, 0.0), 1.5, 3 },
{ { DATUM }, "Datum", RGBf(0.0, 0.8, 0.0), 1.5, 0 },
{ { SOLID_EDGE }, "SolidEdge", RGBf(0.8, 0.8, 0.8), 1.0, 2 },
{ { CONSTRAINT }, "Constraint", RGBf(1.0, 0.1, 1.0), 1.0, 0 },
{ { SELECTED }, "Selected", RGBf(1.0, 0.0, 0.0), 1.5, 0 },
{ { HOVERED }, "Hovered", RGBf(1.0, 1.0, 0.0), 1.5, 0 },
{ { CONTOUR_FILL }, "ContourFill", RGBf(0.0, 0.1, 0.1), 1.0, 0 },
{ { NORMALS }, "Normals", RGBf(0.0, 0.4, 0.4), 1.0, 0 },
{ { ANALYZE }, "Analyze", RGBf(0.0, 1.0, 1.0), 3.0, 0 },
{ { DRAW_ERROR }, "DrawError", RGBf(1.0, 0.0, 0.0), 8.0, 0 },
{ { DIM_SOLID }, "DimSolid", RGBf(0.1, 0.1, 0.1), 1.0, 0 },
{ { HIDDEN_EDGE }, "HiddenEdge", RGBf(0.8, 0.8, 0.8), 1.0, 1 },
{ { OUTLINE }, "Outline", RGBf(0.8, 0.8, 0.8), 3.0, 5 },
{ { 0 }, NULL, RGBf(0.0, 0.0, 0.0), 0.0, 0 }
};
std::string Style::CnfColor(const std::string &prefix) {
return "Style_" + prefix + "_Color";
}
std::string Style::CnfWidth(const std::string &prefix) {
return "Style_" + prefix + "_Width";
}
std::string Style::CnfTextHeight(const std::string &prefix) {
return "Style_" + prefix + "_TextHeight";
}
std::string Style::CnfPrefixToName(const std::string &prefix) {
std::string name = "#def-";
for(size_t i = 0; i < prefix.length(); i++) {
if(isupper(prefix[i]) && i != 0)
name += '-';
name += tolower(prefix[i]);
}
return name;
}
void Style::CreateAllDefaultStyles() {
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 = {};
FillDefaultStyle(&ns, d);
ns.h = h;
if(isDefaultStyle) {
ns.name = CnfPrefixToName(d->cnfPrefix);
} else {
ns.name = "new-custom-style";
}
SK.style.Add(&ns);
}
void Style::FillDefaultStyle(Style *s, const Default *d, bool factory) {
Platform::SettingsRef settings = Platform::GetSettings();
if(d == NULL) d = &Defaults[0];
s->color = (factory)
? d->color
: settings->ThawColor(CnfColor(d->cnfPrefix), d->color);
s->width = (factory)
? d->width
: settings->ThawFloat(CnfWidth(d->cnfPrefix), (float)(d->width));
s->widthAs = UnitsAs::PIXELS;
s->textHeight = (factory) ? 11.5
: settings->ThawFloat(CnfTextHeight(d->cnfPrefix), 11.5);
s->textHeightAs = UnitsAs::PIXELS;
s->textOrigin = TextOrigin::NONE;
s->textAngle = 0;
s->visible = true;
s->exportable = true;
s->filled = false;
s->fillColor = RGBf(0.3, 0.3, 0.3);
s->stippleType = (d->h.v == Style::HIDDEN_EDGE) ? StipplePattern::DASH
: StipplePattern::CONTINUOUS;
s->stippleScale = 15.0;
s->zIndex = d->zIndex;
}
void Style::LoadFactoryDefaults() {
const Default *d;
for(d = &(Defaults[0]); d->h.v; d++) {
Style *s = Get(d->h);
FillDefaultStyle(s, d, /*factory=*/true);
}
SS.backgroundColor = RGBi(0, 0, 0);
}
void Style::FreezeDefaultStyles(Platform::SettingsRef settings) {
const Default *d;
for(d = &(Defaults[0]); d->h.v; d++) {
settings->FreezeColor(CnfColor(d->cnfPrefix), Color(d->h));
settings->FreezeFloat(CnfWidth(d->cnfPrefix), (float)Width(d->h));
settings->FreezeFloat(CnfTextHeight(d->cnfPrefix), (float)TextHeight(d->h));
}
}
uint32_t Style::CreateCustomStyle(bool rememberForUndo) {
if(rememberForUndo) SS.UndoRemember();
uint32_t vs = max((uint32_t)Style::FIRST_CUSTOM, SK.style.MaximumId() + 1);
hStyle hs = { vs };
(void)Style::Get(hs);
return hs.v;
}
void Style::AssignSelectionToStyle(uint32_t v) {
bool showError = false;
SS.GW.GroupSelection();
SS.UndoRemember();
int i;
for(i = 0; i < SS.GW.gs.entities; i++) {
hEntity he = SS.GW.gs.entity[i];
Entity *e = SK.GetEntity(he);
if(!e->IsStylable()) continue;
if(!he.isFromRequest()) {
showError = true;
continue;
}
hRequest hr = he.request();
Request *r = SK.GetRequest(hr);
r->style.v = v;
SS.MarkGroupDirty(r->group);
}
for(i = 0; i < SS.GW.gs.constraints; i++) {
hConstraint hc = SS.GW.gs.constraint[i];
Constraint *c = SK.GetConstraint(hc);
if(!c->IsStylable()) continue;
c->disp.style.v = v;
SS.MarkGroupDirty(c->group);
}
if(showError) {
Error(_("Can't assign style to an entity that's derived from another "
"entity; try assigning a style to this entity's parent."));
}
SS.GW.ClearSelection();
SS.GW.Invalidate();
// And show that style's info screen in the text window.
SS.TW.GoToScreen(TextWindow::Screen::STYLE_INFO);
SS.TW.shown.style.v = v;
SS.ScheduleShowTW();
}
//-----------------------------------------------------------------------------
// 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) {
if(h.v == 0) h.v = ACTIVE_GRP;
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.
//-----------------------------------------------------------------------------
RgbaColor Style::Color(int s, bool forExport) {
hStyle hs = { (uint32_t)s };
return Color(hs, forExport);
}
double Style::Width(int s) {
hStyle hs = { (uint32_t)s };
return Width(hs);
}
//-----------------------------------------------------------------------------
// If a color is almost white, then we can rewrite it to black, just so that
// it won't disappear on file formats with a light background.
//-----------------------------------------------------------------------------
RgbaColor Style::RewriteColor(RgbaColor rgbin) {
Vector rgb = Vector::From(rgbin.redF(), rgbin.greenF(), rgbin.blueF());
rgb = rgb.Minus(Vector::From(1, 1, 1));
if(rgb.Magnitude() < 0.4 && SS.fixExportColors) {
// This is an almost-white color in a default style, which is
// good for the default on-screen view (black bg) but probably
// not desired in the exported files, which typically are shown
// against white backgrounds.
return RGBi(0, 0, 0);
} else {
return rgbin;
}
}
//-----------------------------------------------------------------------------
// Return the stroke color associated with our style as 8-bit RGB.
//-----------------------------------------------------------------------------
RgbaColor Style::Color(hStyle h, bool forExport) {
Style *s = Get(h);
if(forExport) {
return RewriteColor(s->color);
} else {
return s->color;
}
}
//-----------------------------------------------------------------------------
// Return the fill color associated with our style as 8-bit RGB.
//-----------------------------------------------------------------------------
RgbaColor Style::FillColor(hStyle h, bool forExport) {
Style *s = Get(h);
if(forExport) {
return RewriteColor(s->fillColor);
} else {
return s->fillColor;
}
}
//-----------------------------------------------------------------------------
// Return the width associated with our style in pixels..
//-----------------------------------------------------------------------------
double Style::Width(hStyle h) {
Style *s = Get(h);
switch(s->widthAs) {
case UnitsAs::MM: return s->width * SS.GW.scale;
case UnitsAs::PIXELS: return s->width;
}
ssassert(false, "Unexpected units");
}
//-----------------------------------------------------------------------------
// Return the width associated with our style in millimeters..
//-----------------------------------------------------------------------------
double Style::WidthMm(int hs) {
double widthpx = Width(hs);
return widthpx / SS.GW.scale;
}
//-----------------------------------------------------------------------------
// Return the associated text height, in pixels.
//-----------------------------------------------------------------------------
double Style::TextHeight(hStyle h) {
Style *s = Get(h);
switch(s->textHeightAs) {
case UnitsAs::MM: return s->textHeight * SS.GW.scale;
case UnitsAs::PIXELS: return s->textHeight;
}
ssassert(false, "Unexpected units");
}
double Style::DefaultTextHeight() {
hStyle hs { Style::CONSTRAINT };
return TextHeight(hs);
}
//-----------------------------------------------------------------------------
// Return the parameters of this style, as a canvas stroke.
//-----------------------------------------------------------------------------
Canvas::Stroke Style::Stroke(hStyle hs) {
Canvas::Stroke stroke = {};
Style *style = Style::Get(hs);
stroke.color = style->color;
stroke.stipplePattern = style->stippleType;
stroke.stippleScale = style->stippleScale;
stroke.width = style->width;
switch(style->widthAs) {
case Style::UnitsAs::PIXELS:
stroke.unit = Canvas::Unit::PX;
break;
case Style::UnitsAs::MM:
stroke.unit = Canvas::Unit::MM;
break;
}
return stroke;
}
Canvas::Stroke Style::Stroke(int hsv) {
hStyle hs = { (uint32_t) hsv };
return Style::Stroke(hs);
}
//-----------------------------------------------------------------------------
// Should lines and curves from this style appear in the output file? Only
// if it's both shown and exportable.
//-----------------------------------------------------------------------------
bool Style::Exportable(int si) {
hStyle hs = { (uint32_t)si };
Style *s = Get(hs);
return (s->exportable) && (s->visible);
}
//-----------------------------------------------------------------------------
// 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;
}
StipplePattern Style::PatternType(hStyle hs) {
Style *s = Get(hs);
return s->stippleType;
}
double Style::StippleScaleMm(hStyle hs) {
Style *s = Get(hs);
if(s->widthAs == UnitsAs::MM) {
return s->stippleScale;
} else if(s->widthAs == UnitsAs::PIXELS) {
return s->stippleScale / SS.GW.scale;
}
return 1.0;
}
std::string Style::DescriptionString() const {
if(name.empty()) {
return ssprintf("s%03x-(unnamed)", h.v);
} else {
return ssprintf("s%03x-%s", h.v, name.c_str());
}
}
void TextWindow::ScreenShowListOfStyles(int link, uint32_t v) {
SS.TW.GoToScreen(Screen::LIST_OF_STYLES);
}
void TextWindow::ScreenShowStyleInfo(int link, uint32_t v) {
SS.TW.GoToScreen(Screen::STYLE_INFO);
SS.TW.shown.style.v = v;
}
void TextWindow::ScreenLoadFactoryDefaultStyles(int link, uint32_t v) {
Style::LoadFactoryDefaults();
SS.TW.GoToScreen(Screen::LIST_OF_STYLES);
}
void TextWindow::ScreenCreateCustomStyle(int link, uint32_t v) {
Style::CreateCustomStyle();
}
void TextWindow::ScreenChangeBackgroundColor(int link, uint32_t v) {
RgbaColor rgb = SS.backgroundColor;
SS.TW.ShowEditControlWithColorPicker(3, rgb);
SS.TW.edit.meaning = Edit::BACKGROUND_COLOR;
}
void TextWindow::ShowListOfStyles() {
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 %Bz %Bp %Fl%Ll%f%D%s%E",
darkbg ? 'd' : 'a',
&s->color,
darkbg ? 'd' : 'a',
ScreenShowStyleInfo, s->h.v,
s->DescriptionString().c_str());
darkbg = !darkbg;
}
Printf(true, " %Fl%Ll%fcreate a new custom style%E",
&ScreenCreateCustomStyle);
Printf(false, "");
RgbaColor rgb = SS.backgroundColor;
Printf(false, "%Ft background color (r, g, b)%E");
Printf(false, "%Ba %@, %@, %@ %Fl%D%f%Ll[change]%E",
rgb.redF(), rgb.greenF(), rgb.blueF(),
top[rows-1] + 2, &ScreenChangeBackgroundColor);
Printf(false, "");
Printf(false, " %Fl%Ll%fload factory defaults%E",
&ScreenLoadFactoryDefaultStyles);
}
void TextWindow::ScreenChangeStyleName(int link, uint32_t v) {
hStyle hs = { v };
Style *s = Style::Get(hs);
SS.TW.ShowEditControl(12, s->name);
SS.TW.edit.style = hs;
SS.TW.edit.meaning = Edit::STYLE_NAME;
}
void TextWindow::ScreenDeleteStyle(int link, uint32_t 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);
SS.GW.Invalidate();
}
void TextWindow::ScreenChangeStylePatternType(int link, uint32_t v) {
hStyle hs = { v };
Style *s = Style::Get(hs);
s->stippleType = (StipplePattern)(link - 1);
SS.GW.persistentDirty = true;
}
void TextWindow::ScreenChangeStyleMetric(int link, uint32_t v) {
hStyle hs = { v };
Style *s = Style::Get(hs);
double val;
Style::UnitsAs units;
Edit meaning;
int col;
switch(link) {
case 't':
val = s->textHeight;
units = s->textHeightAs;
col = 10;
meaning = Edit::STYLE_TEXT_HEIGHT;
break;
case 's':
val = s->stippleScale;
units = s->widthAs;
col = 17;
meaning = Edit::STYLE_STIPPLE_PERIOD;
break;
case 'w':
case 'W':
val = s->width;
units = s->widthAs;
col = 9;
meaning = Edit::STYLE_WIDTH;
break;
default: ssassert(false, "Unexpected link");
}
std::string edit_value;
if(units == Style::UnitsAs::PIXELS) {
edit_value = ssprintf("%.2f", val);
} else {
edit_value = SS.MmToString(val);
}
SS.TW.ShowEditControl(col, edit_value);
SS.TW.edit.style = hs;
SS.TW.edit.meaning = meaning;
}
void TextWindow::ScreenChangeStyleTextAngle(int link, uint32_t v) {
hStyle hs = { v };
Style *s = Style::Get(hs);
SS.TW.ShowEditControl(9, ssprintf("%.2f", s->textAngle));
SS.TW.edit.style = hs;
SS.TW.edit.meaning = Edit::STYLE_TEXT_ANGLE;
}
void TextWindow::ScreenChangeStyleColor(int link, uint32_t v) {
hStyle hs = { v };
Style *s = Style::Get(hs);
// Same function used for stroke and fill colors
Edit em;
RgbaColor rgb;
if(link == 's') {
em = Edit::STYLE_COLOR;
rgb = s->color;
} else if(link == 'f') {
em = Edit::STYLE_FILL_COLOR;
rgb = s->fillColor;
} else ssassert(false, "Unexpected link");
SS.TW.ShowEditControlWithColorPicker(13, rgb);
SS.TW.edit.style = hs;
SS.TW.edit.meaning = em;
}
void TextWindow::ScreenChangeStyleYesNo(int link, uint32_t v) {
SS.UndoRemember();
hStyle hs = { v };
Style *s = Style::Get(hs);
switch(link) {
// Units for the width
case 'w':
if(s->widthAs != Style::UnitsAs::MM) {
s->widthAs = Style::UnitsAs::MM;
s->width /= SS.GW.scale;
s->stippleScale /= SS.GW.scale;
}
break;
case 'W':
if(s->widthAs != Style::UnitsAs::PIXELS) {
s->widthAs = Style::UnitsAs::PIXELS;
s->width *= SS.GW.scale;
s->stippleScale *= SS.GW.scale;
}
break;
// Units for the height
case 'g':
if(s->textHeightAs != Style::UnitsAs::MM) {
s->textHeightAs = Style::UnitsAs::MM;
s->textHeight /= SS.GW.scale;
}
break;
case 'G':
if(s->textHeightAs != Style::UnitsAs::PIXELS) {
s->textHeightAs = Style::UnitsAs::PIXELS;
s->textHeight *= SS.GW.scale;
}
break;
case 'e':
s->exportable = !(s->exportable);
break;
case 'v':
s->visible = !(s->visible);
break;
case 'f':
s->filled = !(s->filled);
break;
// Horizontal text alignment
case 'L':
s->textOrigin = (Style::TextOrigin)((uint32_t)s->textOrigin | (uint32_t)Style::TextOrigin::LEFT);
s->textOrigin = (Style::TextOrigin)((uint32_t)s->textOrigin & ~(uint32_t)Style::TextOrigin::RIGHT);
break;
case 'H':
s->textOrigin = (Style::TextOrigin)((uint32_t)s->textOrigin & ~(uint32_t)Style::TextOrigin::LEFT);
s->textOrigin = (Style::TextOrigin)((uint32_t)s->textOrigin & ~(uint32_t)Style::TextOrigin::RIGHT);
break;
case 'R':
s->textOrigin = (Style::TextOrigin)((uint32_t)s->textOrigin & ~(uint32_t)Style::TextOrigin::LEFT);
s->textOrigin = (Style::TextOrigin)((uint32_t)s->textOrigin | (uint32_t)Style::TextOrigin::RIGHT);
break;
// Vertical text alignment
case 'B':
s->textOrigin = (Style::TextOrigin)((uint32_t)s->textOrigin | (uint32_t)Style::TextOrigin::BOT);
s->textOrigin = (Style::TextOrigin)((uint32_t)s->textOrigin & ~(uint32_t)Style::TextOrigin::TOP);
break;
case 'V':
s->textOrigin = (Style::TextOrigin)((uint32_t)s->textOrigin & ~(uint32_t)Style::TextOrigin::BOT);
s->textOrigin = (Style::TextOrigin)((uint32_t)s->textOrigin & ~(uint32_t)Style::TextOrigin::TOP);
break;
case 'T':
s->textOrigin = (Style::TextOrigin)((uint32_t)s->textOrigin & ~(uint32_t)Style::TextOrigin::BOT);
s->textOrigin = (Style::TextOrigin)((uint32_t)s->textOrigin | (uint32_t)Style::TextOrigin::TOP);
break;
}
SS.GW.Invalidate(/*clearPersistent=*/true);
}
bool TextWindow::EditControlDoneForStyles(const std::string &str) {
Style *s;
switch(edit.meaning) {
case Edit::STYLE_STIPPLE_PERIOD:
case Edit::STYLE_TEXT_HEIGHT:
case Edit::STYLE_WIDTH: {
SS.UndoRemember();
s = Style::Get(edit.style);
double v;
Style::UnitsAs units = (edit.meaning == Edit::STYLE_TEXT_HEIGHT) ?
s->textHeightAs : s->widthAs;
if(units == Style::UnitsAs::MM) {
v = SS.StringToMm(str);
} else {
v = atof(str.c_str());
}
v = max(0.0, v);
if(edit.meaning == Edit::STYLE_TEXT_HEIGHT) {
s->textHeight = v;
} else if(edit.meaning == Edit::STYLE_STIPPLE_PERIOD) {
s->stippleScale = v;
} else {
s->width = v;
}
break;
}
case Edit::STYLE_TEXT_ANGLE:
SS.UndoRemember();
s = Style::Get(edit.style);
s->textAngle = WRAP_SYMMETRIC(atof(str.c_str()), 360);
break;
case Edit::BACKGROUND_COLOR:
case Edit::STYLE_FILL_COLOR:
case Edit::STYLE_COLOR: {
Vector rgb;
if(sscanf(str.c_str(), "%lf, %lf, %lf", &rgb.x, &rgb.y, &rgb.z)==3) {
rgb = rgb.ClampWithin(0, 1);
if(edit.meaning == Edit::STYLE_COLOR) {
SS.UndoRemember();
s = Style::Get(edit.style);
s->color = RGBf(rgb.x, rgb.y, rgb.z);
} else if(edit.meaning == Edit::STYLE_FILL_COLOR) {
SS.UndoRemember();
s = Style::Get(edit.style);
s->fillColor = RGBf(rgb.x, rgb.y, rgb.z);
} else {
SS.backgroundColor = RGBf(rgb.x, rgb.y, rgb.z);
}
} else {
Error(_("Bad format: specify color as r, g, b"));
}
break;
}
case Edit::STYLE_NAME:
if(str.empty()) {
Error(_("Style name cannot be empty"));
} else {
SS.UndoRemember();
s = Style::Get(edit.style);
s->name = str;
}
break;
default: return false;
}
SS.GW.persistentDirty = true;
return true;
}
void TextWindow::ShowStyleInfo() {
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().c_str());
} else {
Printf(true, "%FtSTYLE %E%s "
"[%Fl%Ll%D%frename%E/%Fl%Ll%D%fdel%E]",
s->DescriptionString().c_str(),
s->h.v, &ScreenChangeStyleName,
s->h.v, &ScreenDeleteStyle);
}
Printf(true, "%Ft line stroke style%E");
Printf(false, "%Ba %Ftcolor %E%Bz %Ba (%@, %@, %@) %D%f%Ls%Fl[change]%E",
&s->color,
s->color.redF(), s->color.greenF(), s->color.blueF(),
s->h.v, ScreenChangeStyleColor);
// The line width, and its units
if(s->widthAs == Style::UnitsAs::PIXELS) {
Printf(false, " %Ftwidth%E %@ %D%f%Lp%Fl[change]%E",
s->width,
s->h.v, &ScreenChangeStyleMetric,
(s->h.v < Style::FIRST_CUSTOM) ? 'w' : 'W');
} else {
Printf(false, " %Ftwidth%E %s %D%f%Lp%Fl[change]%E",
SS.MmToString(s->width).c_str(),
s->h.v, &ScreenChangeStyleMetric,
(s->h.v < Style::FIRST_CUSTOM) ? 'w' : 'W');
}
if(s->widthAs == Style::UnitsAs::PIXELS) {
Printf(false, "%Ba %Ftstipple width%E %@ %D%f%Lp%Fl[change]%E",
s->stippleScale,
s->h.v, &ScreenChangeStyleMetric, 's');
} else {
Printf(false, "%Ba %Ftstipple width%E %s %D%f%Lp%Fl[change]%E",
SS.MmToString(s->stippleScale).c_str(),
s->h.v, &ScreenChangeStyleMetric, 's');
}
bool widthpx = (s->widthAs == Style::UnitsAs::PIXELS);
if(s->h.v < Style::FIRST_CUSTOM) {
Printf(false," %Ftin units of %Fdpixels%E");
} else {
Printf(false,"%Ba %Ftin units of %Fd"
"%D%f%LW%s pixels%E "
"%D%f%Lw%s %s",
s->h.v, &ScreenChangeStyleYesNo,
widthpx ? RADIO_TRUE : RADIO_FALSE,
s->h.v, &ScreenChangeStyleYesNo,
!widthpx ? RADIO_TRUE : RADIO_FALSE,
SS.UnitName());
}
Printf(false,"%Ba %Ftstipple type:%E");
const size_t patternCount = (size_t)StipplePattern::LAST + 1;
const char *patternsSource[patternCount] = {
"___________",
"- - - - ",
"- - - - - -",
"__ __ __ __",
"-.-.-.-.-.-",
"..-..-..-..",
"...........",
"~~~~~~~~~~~",
"__~__~__~__"
};
std::string patterns[patternCount];
for(uint32_t i = 0; i <= (uint32_t)StipplePattern::LAST; i++) {
const char *str = patternsSource[i];
do {
switch(*str) {
case ' ': patterns[i] += " "; break;
case '.': patterns[i] += "\xEE\x80\x84"; break;
case '_': patterns[i] += "\xEE\x80\x85"; break;
case '-': patterns[i] += "\xEE\x80\x86"; break;
case '~': patterns[i] += "\xEE\x80\x87"; break;
default: ssassert(false, "Unexpected stipple pattern element");
}
} while(*(++str));
}
for(uint32_t i = 0; i <= (uint32_t)StipplePattern::LAST; i++) {
const char *radio = s->stippleType == (StipplePattern)i ? RADIO_TRUE : RADIO_FALSE;
Printf(false, "%Bp %D%f%Lp%s %s%E",
(i % 2 == 0) ? 'd' : 'a',
s->h.v, &ScreenChangeStylePatternType,
i + 1, radio, patterns[i].c_str());
}
if(s->h.v >= Style::FIRST_CUSTOM) {
// The fill color, and whether contours are filled
Printf(false, "");
Printf(false, "%Ft contour fill style%E");
Printf(false,
"%Ba %Ftcolor %E%Bz %Ba (%@, %@, %@) %D%f%Lf%Fl[change]%E",
&s->fillColor,
s->fillColor.redF(), s->fillColor.greenF(), s->fillColor.blueF(),
s->h.v, ScreenChangeStyleColor);
Printf(false, "%Bd %D%f%Lf%s contours are filled%E",
s->h.v, &ScreenChangeStyleYesNo,
s->filled ? CHECK_TRUE : CHECK_FALSE);
}
// The text height, and its units
Printf(false, "");
Printf(false, "%Ft text style%E");
if(s->textHeightAs == Style::UnitsAs::PIXELS) {
Printf(false, "%Ba %Ftheight %E%@ %D%f%Lt%Fl%s%E",
s->textHeight,
s->h.v, &ScreenChangeStyleMetric,
"[change]");
} else {
Printf(false, "%Ba %Ftheight %E%s %D%f%Lt%Fl%s%E",
SS.MmToString(s->textHeight).c_str(),
s->h.v, &ScreenChangeStyleMetric,
"[change]");
}
bool textHeightpx = (s->textHeightAs == Style::UnitsAs::PIXELS);
if(s->h.v < Style::FIRST_CUSTOM) {
Printf(false,"%Bd %Ftin units of %Fdpixels");
} else {
Printf(false,"%Bd %Ftin units of %Fd"
"%D%f%LG%s pixels%E "
"%D%f%Lg%s %s",
s->h.v, &ScreenChangeStyleYesNo,
textHeightpx ? RADIO_TRUE : RADIO_FALSE,
s->h.v, &ScreenChangeStyleYesNo,
!textHeightpx ? RADIO_TRUE : RADIO_FALSE,
SS.UnitName());
}
if(s->h.v >= Style::FIRST_CUSTOM) {
Printf(false, "%Ba %Ftangle %E%@ %D%f%Ll%Fl[change]%E",
s->textAngle,
s->h.v, &ScreenChangeStyleTextAngle);
Printf(false, "");
Printf(false, "%Ft text comment alignment%E");
bool neither;
neither = !((uint32_t)s->textOrigin & ((uint32_t)Style::TextOrigin::LEFT | (uint32_t)Style::TextOrigin::RIGHT));
Printf(false, "%Ba "
"%D%f%LL%s left%E "
"%D%f%LH%s center%E "
"%D%f%LR%s right%E ",
s->h.v, &ScreenChangeStyleYesNo,
((uint32_t)s->textOrigin & (uint32_t)Style::TextOrigin::LEFT) ? RADIO_TRUE : RADIO_FALSE,
s->h.v, &ScreenChangeStyleYesNo,
neither ? RADIO_TRUE : RADIO_FALSE,
s->h.v, &ScreenChangeStyleYesNo,
((uint32_t)s->textOrigin & (uint32_t)Style::TextOrigin::RIGHT) ? RADIO_TRUE : RADIO_FALSE);
neither = !((uint32_t)s->textOrigin & ((uint32_t)Style::TextOrigin::BOT | (uint32_t)Style::TextOrigin::TOP));
Printf(false, "%Bd "
"%D%f%LB%s bottom%E "
"%D%f%LV%s center%E "
"%D%f%LT%s top%E ",
s->h.v, &ScreenChangeStyleYesNo,
((uint32_t)s->textOrigin & (uint32_t)Style::TextOrigin::BOT) ? RADIO_TRUE : RADIO_FALSE,
s->h.v, &ScreenChangeStyleYesNo,
neither ? RADIO_TRUE : RADIO_FALSE,
s->h.v, &ScreenChangeStyleYesNo,
((uint32_t)s->textOrigin & (uint32_t)Style::TextOrigin::TOP) ? RADIO_TRUE : RADIO_FALSE);
}
if(s->h.v >= Style::FIRST_CUSTOM) {
Printf(false, "");
Printf(false, " %Fd%D%f%Lv%s show these objects on screen%E",
s->h.v, &ScreenChangeStyleYesNo,
s->visible ? CHECK_TRUE : CHECK_FALSE);
Printf(false, " %Fd%D%f%Le%s export these objects%E",
s->h.v, &ScreenChangeStyleYesNo,
s->exportable ? CHECK_TRUE : CHECK_FALSE);
Printf(false, "");
Printf(false, "To assign lines or curves to this style,");
Printf(false, "right-click them on the drawing.");
}
}
void TextWindow::ScreenAssignSelectionToStyle(int link, uint32_t v) {
Style::AssignSelectionToStyle(v);
}