Example #1
0
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  %Bz   %Bp   %Fl%Ll%f%D%s%E",
            darkbg ? 'd' : 'a',
            &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, "");

    RgbColor 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, "%Ft background bitmap image%E");
    if(SS.bgImage.fromFile) {
        Printf(false, "%Ba   %Ftwidth:%E %dpx   %Ftheight:%E %dpx",
            SS.bgImage.w, SS.bgImage.h);

        Printf(false, "   %Ftscale:%E %# px/%s %Fl%Ll%f%D[change]%E",
            SS.bgImage.scale*SS.MmPerUnit(),
            SS.UnitName(),
            &ScreenChangeBackgroundImageScale, top[rows-1] + 2);

        Printf(false, "%Ba   %Fl%Lc%fclear background image%E",
            &ScreenBackgroundImage);
    } else {
        Printf(false, "%Ba   none - %Fl%Ll%fload background image%E",
            &ScreenBackgroundImage);
        Printf(false, "   (bottom left will be center of view)");
    }

    Printf(false, "");
    Printf(false, "  %Fl%Ll%fload factory defaults%E",
        &ScreenLoadFactoryDefaultStyles);
}
Example #2
0
void GraphicsWindow::ContextMenuListStyles(void) {
    CreateContextSubmenu();
    Style *s;
    bool empty = true;
    for(s = SK.style.First(); s; s = SK.style.NextAfter(s)) {
        if(s->h.v < Style::FIRST_CUSTOM) continue;

        AddContextMenuItem(s->DescriptionString(), CMNU_FIRST_STYLE + s->h.v);
        empty = false;
    }

    if(!empty) AddContextMenuItem(NULL, CONTEXT_SEPARATOR);

    AddContextMenuItem("No Style", CMNU_NO_STYLE);
    AddContextMenuItem("Newly Created Custom Style...", CMNU_NEW_CUSTOM_STYLE);
}
void TextWindow::DescribeSelection(void) {
    Entity *e;
    Vector p;
    int i;
    Printf(false, "");

    if(gs.n == 1 && (gs.points == 1 || gs.entities == 1)) {
        e = SK.GetEntity(gs.points == 1 ? gs.point[0] : gs.entity[0]);

#define COSTR(p) \
    SS.MmToString((p).x), SS.MmToString((p).y), SS.MmToString((p).z)
#define PT_AS_STR "(%Fi%s%E, %Fi%s%E, %Fi%s%E)"
#define PT_AS_NUM "(%Fi%3%E, %Fi%3%E, %Fi%3%E)"
        switch(e->type) {
            case Entity::POINT_IN_3D:
            case Entity::POINT_IN_2D:
            case Entity::POINT_N_TRANS:
            case Entity::POINT_N_ROT_TRANS:
            case Entity::POINT_N_COPY:
            case Entity::POINT_N_ROT_AA:
                p = e->PointGetNum();
                Printf(false, "%FtPOINT%E at " PT_AS_STR, COSTR(p));
                break;

            case Entity::NORMAL_IN_3D:
            case Entity::NORMAL_IN_2D:
            case Entity::NORMAL_N_COPY:
            case Entity::NORMAL_N_ROT:
            case Entity::NORMAL_N_ROT_AA: {
                Quaternion q = e->NormalGetNum();
                p = q.RotationN();
                Printf(false, "%FtNORMAL / COORDINATE SYSTEM%E");
                Printf(true,  "  basis n = " PT_AS_NUM, CO(p));
                p = q.RotationU();
                Printf(false, "        u = " PT_AS_NUM, CO(p));
                p = q.RotationV();
                Printf(false, "        v = " PT_AS_NUM, CO(p));
                break;
            }
            case Entity::WORKPLANE: {
                p = SK.GetEntity(e->point[0])->PointGetNum();
                Printf(false, "%FtWORKPLANE%E");
                Printf(true, "   origin = " PT_AS_STR, COSTR(p));
                Quaternion q = e->Normal()->NormalGetNum();
                p = q.RotationN();
                Printf(true, "   normal = " PT_AS_NUM, CO(p));
                break;
            }
            case Entity::LINE_SEGMENT: {
                Vector p0 = SK.GetEntity(e->point[0])->PointGetNum();
                p = p0;
                Printf(false, "%FtLINE SEGMENT%E");
                Printf(true,  "   thru " PT_AS_STR, COSTR(p));
                Vector p1 = SK.GetEntity(e->point[1])->PointGetNum();
                p = p1;
                Printf(false, "        " PT_AS_STR, COSTR(p));
                Printf(true,  "   len = %Fi%s%E",
                    SS.MmToString((p1.Minus(p0).Magnitude())));
                break;
            }
            case Entity::CUBIC_PERIODIC:
            case Entity::CUBIC:
                int pts;
                if(e->type == Entity::CUBIC_PERIODIC) {
                    Printf(false, "%FtPERIODIC C2 CUBIC SPLINE%E");
                    pts = (3 + e->extraPoints);
                } else if(e->extraPoints > 0) {
                    Printf(false, "%FtINTERPOLATING C2 CUBIC SPLINE%E");
                    pts = (4 + e->extraPoints);
                } else {
                    Printf(false, "%FtCUBIC BEZIER CURVE%E");
                    pts = 4;
                }
                for(i = 0; i < pts; i++) {
                    p = SK.GetEntity(e->point[i])->PointGetNum();
                    Printf((i==0), "   p%d = " PT_AS_STR, i, COSTR(p));
                }
                break;

            case Entity::ARC_OF_CIRCLE: {
                Printf(false, "%FtARC OF A CIRCLE%E");
                p = SK.GetEntity(e->point[0])->PointGetNum();
                Printf(true,  "     center = " PT_AS_STR, COSTR(p));
                p = SK.GetEntity(e->point[1])->PointGetNum();
                Printf(true,  "  endpoints = " PT_AS_STR, COSTR(p));
                p = SK.GetEntity(e->point[2])->PointGetNum();
                Printf(false, "              " PT_AS_STR, COSTR(p));
                double r = e->CircleGetRadiusNum();
                Printf(true, "   diameter =  %Fi%s", SS.MmToString(r*2));
                Printf(false, "     radius =  %Fi%s", SS.MmToString(r));
                double thetas, thetaf, dtheta;
                e->ArcGetAngles(&thetas, &thetaf, &dtheta);
                Printf(false, "    arc len =  %Fi%s", SS.MmToString(dtheta*r));
                break;
            }
            case Entity::CIRCLE: {
                Printf(false, "%FtCIRCLE%E");
                p = SK.GetEntity(e->point[0])->PointGetNum();
                Printf(true,  "     center = " PT_AS_STR, COSTR(p));
                double r = e->CircleGetRadiusNum();
                Printf(true,  "   diameter =  %Fi%s", SS.MmToString(r*2));
                Printf(false, "     radius =  %Fi%s", SS.MmToString(r));
                break;
            }
            case Entity::FACE_NORMAL_PT:
            case Entity::FACE_XPROD:
            case Entity::FACE_N_ROT_TRANS:
            case Entity::FACE_N_ROT_AA:
            case Entity::FACE_N_TRANS:
                Printf(false, "%FtPLANE FACE%E");
                p = e->FaceGetNormalNum();
                Printf(true,  "   normal = " PT_AS_NUM, CO(p));
                p = e->FaceGetPointNum();
                Printf(false, "     thru = " PT_AS_STR, COSTR(p));
                break;

            case Entity::TTF_TEXT: {
                Printf(false, "%FtTRUETYPE FONT TEXT%E");
                Printf(true, "  font = '%Fi%s%E'", e->font.str);
                if(e->h.isFromRequest()) {
                    Printf(false, "  text = '%Fi%s%E' %Fl%Ll%f%D[change]%E",
                        e->str.str, &ScreenEditTtfText, e->h.request());
                    Printf(true, "  select new font");
                    SS.fonts.LoadAll();
                    int i;
                    for(i = 0; i < SS.fonts.l.n; i++) {
                        TtfFont *tf = &(SS.fonts.l.elem[i]);
                        if(strcmp(e->font.str, tf->FontFileBaseName())==0) {
                            Printf(false, "%Bp    %s",
                                (i & 1) ? 'd' : 'a',
                                tf->name.str);
                        } else {
                            Printf(false, "%Bp    %f%D%Fl%Ll%s%E%Bp",
                                (i & 1) ? 'd' : 'a',
                                &ScreenSetTtfFont, i,
                                tf->name.str,
                                (i & 1) ? 'd' : 'a');
                        }
                    }
                } else {
                    Printf(false, "  text = '%Fi%s%E'", e->str.str);
                }
                break;
            }

            default:
                Printf(true, "%Ft?? ENTITY%E");
                break;
        }

        Group *g = SK.GetGroup(e->group);
        Printf(false, "");
        Printf(false, "%FtIN GROUP%E      %s", g->DescriptionString());
        if(e->workplane.v == Entity::FREE_IN_3D.v) {
            Printf(false, "%FtNOT LOCKED IN WORKPLANE%E"); 
        } else {
            Entity *w = SK.GetEntity(e->workplane);
            Printf(false, "%FtIN WORKPLANE%E  %s", w->DescriptionString());
        }
        if(e->style.v) {
            Style *s = Style::Get(e->style);
            Printf(false, "%FtIN STYLE%E      %s", s->DescriptionString());
        } else {
            Printf(false, "%FtIN STYLE%E      none");
        }
        if(e->construction) {
            Printf(false, "%FtCONSTRUCTION");
        }
    } else if(gs.n == 2 && gs.points == 2) {
        Printf(false, "%FtTWO POINTS");
        Vector p0 = SK.GetEntity(gs.point[0])->PointGetNum();
        Printf(true,  "   at " PT_AS_STR, COSTR(p0));
        Vector p1 = SK.GetEntity(gs.point[1])->PointGetNum();
        Printf(false, "      " PT_AS_STR, COSTR(p1));
        double d = (p1.Minus(p0)).Magnitude();
        Printf(true, "  d = %Fi%s", SS.MmToString(d));
    } else if(gs.n == 2 && gs.faces == 1 && gs.points == 1) {
        Printf(false, "%FtA POINT AND A PLANE FACE");
        Vector pt = SK.GetEntity(gs.point[0])->PointGetNum();
        Printf(true,  "        point = " PT_AS_STR, COSTR(pt));
        Vector n = SK.GetEntity(gs.face[0])->FaceGetNormalNum();
        Printf(true,  " plane normal = " PT_AS_NUM, CO(n));
        Vector pl = SK.GetEntity(gs.face[0])->FaceGetPointNum();
        Printf(false, "   plane thru = " PT_AS_STR, COSTR(pl));
        double dd = n.Dot(pl) - n.Dot(pt);
        Printf(true,  "     distance = %Fi%s", SS.MmToString(dd));
    } else if(gs.n == 3 && gs.points == 2 && gs.vectors == 1) {
        Printf(false, "%FtTWO POINTS AND A VECTOR");
        Vector p0 = SK.GetEntity(gs.point[0])->PointGetNum();
        Printf(true,  "  pointA = " PT_AS_STR, COSTR(p0));
        Vector p1 = SK.GetEntity(gs.point[1])->PointGetNum();
        Printf(false, "  pointB = " PT_AS_STR, COSTR(p1));
        Vector v  = SK.GetEntity(gs.vector[0])->VectorGetNum();
        v = v.WithMagnitude(1);
        Printf(true,  "  vector = " PT_AS_NUM, CO(v));
        double d = (p1.Minus(p0)).Dot(v);
        Printf(true,  "  proj_d = %Fi%s", SS.MmToString(d));
    } else if(gs.n == 2 && gs.lineSegments == 1 && gs.points == 1) {
        Entity *ln = SK.GetEntity(gs.entity[0]);
        Vector lp0 = SK.GetEntity(ln->point[0])->PointGetNum(),
               lp1 = SK.GetEntity(ln->point[1])->PointGetNum();
        Printf(false, "%FtLINE SEGMENT AND POINT%E");
        Printf(true,  "   ln thru " PT_AS_STR, COSTR(lp0));
        Printf(false, "           " PT_AS_STR, COSTR(lp1));
        Vector pp = SK.GetEntity(gs.point[0])->PointGetNum();
        Printf(true,  "     point " PT_AS_STR, COSTR(pp));
        Printf(true,  " pt-ln distance = %Fi%s%E",
            SS.MmToString(pp.DistanceToLine(lp0, lp1.Minus(lp0))));
    } else if(gs.n == 2 && gs.vectors == 2) {
        Printf(false, "%FtTWO VECTORS");

        Vector v0 = SK.GetEntity(gs.entity[0])->VectorGetNum(),
               v1 = SK.GetEntity(gs.entity[1])->VectorGetNum();
        v0 = v0.WithMagnitude(1);
        v1 = v1.WithMagnitude(1);

        Printf(true,  "  vectorA = " PT_AS_NUM, CO(v0));
        Printf(false, "  vectorB = " PT_AS_NUM, CO(v1));

        double theta = acos(v0.Dot(v1));
        Printf(true,  "    angle = %Fi%2%E degrees", theta*180/PI);
        while(theta < PI/2) theta += PI;
        while(theta > PI/2) theta -= PI; 
        Printf(false, " or angle = %Fi%2%E (mod 180)", theta*180/PI);
    } else if(gs.n == 2 && gs.faces == 2) {
        Printf(false, "%FtTWO PLANE FACES");

        Vector n0 = SK.GetEntity(gs.face[0])->FaceGetNormalNum();
        Printf(true,  " planeA normal = " PT_AS_NUM, CO(n0));
        Vector p0 = SK.GetEntity(gs.face[0])->FaceGetPointNum();
        Printf(false, "   planeA thru = " PT_AS_STR, COSTR(p0));

        Vector n1 = SK.GetEntity(gs.face[1])->FaceGetNormalNum();
        Printf(true,  " planeB normal = " PT_AS_NUM, CO(n1));
        Vector p1 = SK.GetEntity(gs.face[1])->FaceGetPointNum();
        Printf(false, "   planeB thru = " PT_AS_STR, COSTR(p1));

        double theta = acos(n0.Dot(n1));
        Printf(true,  "         angle = %Fi%2%E degrees", theta*180/PI);
        while(theta < PI/2) theta += PI;
        while(theta > PI/2) theta -= PI; 
        Printf(false, "      or angle = %Fi%2%E (mod 180)", theta*180/PI);

        if(fabs(theta) < 0.01) {
            double d = (p1.Minus(p0)).Dot(n0);
            Printf(true,  "      distance = %Fi%s", SS.MmToString(d));
        }
    } else if(gs.n == 0 && gs.stylables > 0) {
        Printf(false, "%FtSELECTED:%E comment text");
    } else if(gs.n == 0 && gs.constraints == 1) {
        Printf(false, "%FtSELECTED:%E %s",
            SK.GetConstraint(gs.constraint[0])->DescriptionString());
    } else {
        int n = SS.GW.selection.n;
        Printf(false, "%FtSELECTED:%E %d item%s", n, n == 1 ? "" : "s");
    }

    if(shown.screen == SCREEN_STYLE_INFO && 
       shown.style.v >= Style::FIRST_CUSTOM && gs.stylables > 0)
    {
        // If we are showing a screen for a particular style, then offer the
        // option to assign our selected entities to that style.
        Style *s = Style::Get(shown.style);
        Printf(true, "%Fl%D%f%Ll(assign to style %s)%E",
            shown.style.v,
            &ScreenAssignSelectionToStyle,
            s->DescriptionString());
    }
    // If any of the selected entities have an assigned style, then offer
    // the option to remove that style.
    bool styleAssigned = false;
    for(i = 0; i < gs.entities; i++) {
        Entity *e = SK.GetEntity(gs.entity[i]);
        if(e->style.v != 0) {
            styleAssigned = true;
        }
    }
    for(i = 0; i < gs.constraints; i++) {
        Constraint *c = SK.GetConstraint(gs.constraint[i]);
        if(c->type == Constraint::COMMENT && c->disp.style.v != 0) {
            styleAssigned = true;
        }
    }
    if(styleAssigned) {
        Printf(true, "%Fl%D%f%Ll(remove assigned style)%E",
            0,
            &ScreenAssignSelectionToStyle);
    }

    Printf(true, "%Fl%f%Ll(unselect all)%E", &TextWindow::ScreenUnselectAll);
}
Example #4
0
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, "%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::UNITS_AS_PIXELS) {
        Printf(false, "   %Ftwidth%E %@ %D%f%Lp%Fl[change]%E",
            s->width,
            s->h.v, &ScreenChangeStyleWidthOrTextHeight,
            (s->h.v < Style::FIRST_CUSTOM) ? 'w' : 'W');
    } else {
        Printf(false, "   %Ftwidth%E %s %D%f%Lp%Fl[change]%E",
            SS.MmToString(s->width),
            s->h.v, &ScreenChangeStyleWidthOrTextHeight,
            (s->h.v < Style::FIRST_CUSTOM) ? 'w' : 'W');
    }

    bool widthpx = (s->widthAs == Style::UNITS_AS_PIXELS);
    if(s->h.v < Style::FIRST_CUSTOM) {
        Printf(false,"%Ba   %Ftin units of %Fdpixels%E");
    } else {
        Printf(false,"%Ba   %Ftin units of  %Fd"
                            "%D%f%LW%c pixels%E  "
                            "%D%f%Lw%c %s",
            s->h.v, &ScreenChangeStyleYesNo,
            widthpx ? RADIO_TRUE : RADIO_FALSE,
            s->h.v, &ScreenChangeStyleYesNo,
            !widthpx ? RADIO_TRUE : RADIO_FALSE,
            SS.UnitName());
    }

    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%c  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 comment style%E");

    const char *chng = (s->h.v < Style::FIRST_CUSTOM) ? "" : "[change]";
    if(s->textHeightAs == Style::UNITS_AS_PIXELS) {
        Printf(false, "%Ba   %Ftheight %E%@ %D%f%Lt%Fl%s%E",
            s->textHeight,
            s->h.v, &ScreenChangeStyleWidthOrTextHeight,
            chng);
    } else {
        Printf(false, "%Ba   %Ftheight %E%s %D%f%Lt%Fl%s%E",
            SS.MmToString(s->textHeight),
            s->h.v, &ScreenChangeStyleWidthOrTextHeight,
            chng);
    }

    bool textHeightpx = (s->textHeightAs == Style::UNITS_AS_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%c pixels%E  "
                            "%D%f%Lg%c %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 = !(s->textOrigin & (Style::ORIGIN_LEFT | Style::ORIGIN_RIGHT));
        Printf(false, "%Ba   "
                      "%D%f%LL%c left%E    "
                      "%D%f%LH%c center%E  "
                      "%D%f%LR%c right%E  ",
            s->h.v, &ScreenChangeStyleYesNo,
            (s->textOrigin & Style::ORIGIN_LEFT) ? RADIO_TRUE : RADIO_FALSE,
            s->h.v, &ScreenChangeStyleYesNo,
            neither ? RADIO_TRUE : RADIO_FALSE,
            s->h.v, &ScreenChangeStyleYesNo,
            (s->textOrigin & Style::ORIGIN_RIGHT) ? RADIO_TRUE : RADIO_FALSE);

        neither = !(s->textOrigin & (Style::ORIGIN_BOT | Style::ORIGIN_TOP));
        Printf(false, "%Bd   "
                      "%D%f%LB%c bottom%E  "
                      "%D%f%LV%c center%E  "
                      "%D%f%LT%c top%E  ",
            s->h.v, &ScreenChangeStyleYesNo,
            (s->textOrigin & Style::ORIGIN_BOT) ? RADIO_TRUE : RADIO_FALSE,
            s->h.v, &ScreenChangeStyleYesNo,
            neither ? RADIO_TRUE : RADIO_FALSE,
            s->h.v, &ScreenChangeStyleYesNo,
            (s->textOrigin & Style::ORIGIN_TOP) ? RADIO_TRUE : RADIO_FALSE);
    }

    if(s->h.v >= Style::FIRST_CUSTOM) {
        Printf(false, "");

        Printf(false, "  %Fd%D%f%Lv%c  show these objects on screen%E",
                s->h.v, &ScreenChangeStyleYesNo,
                s->visible ? CHECK_TRUE : CHECK_FALSE);

        Printf(false, "  %Fd%D%f%Le%c  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.");
    }
}
Example #5
0
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.");
    }
}