/* ======================================================================== * PlantUML : a free UML diagram generator * ======================================================================== * * (C) Copyright 2009-2023, Arnaud Roques * * Project Info: http://plantuml.com * * If you like this project or if you find it useful, you can support us at: * * http://plantuml.com/patreon (only 1$ per month!) * http://plantuml.com/paypal * * This file is part of PlantUML. * * PlantUML is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * PlantUML distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public * License for more details. * * You should have received a copy of the GNU General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * * Original Author: Arnaud Roques * * */ package net.sourceforge.plantuml.svek.image; import java.awt.geom.Rectangle2D; import java.util.EnumMap; import java.util.Map; import net.sourceforge.plantuml.ColorParam; import net.sourceforge.plantuml.CornerParam; import net.sourceforge.plantuml.Dimension2DDouble; import net.sourceforge.plantuml.FontParam; import net.sourceforge.plantuml.ISkinParam; import net.sourceforge.plantuml.LineConfigurable; import net.sourceforge.plantuml.LineParam; import net.sourceforge.plantuml.SkinParamUtils; import net.sourceforge.plantuml.Url; import net.sourceforge.plantuml.UseStyle; import net.sourceforge.plantuml.awt.geom.Dimension2D; import net.sourceforge.plantuml.creole.Stencil; import net.sourceforge.plantuml.cucadiagram.EntityPortion; import net.sourceforge.plantuml.cucadiagram.ILeaf; import net.sourceforge.plantuml.cucadiagram.LeafType; import net.sourceforge.plantuml.cucadiagram.PortionShower; import net.sourceforge.plantuml.cucadiagram.dot.GraphvizVersion; import net.sourceforge.plantuml.graphic.InnerStrategy; import net.sourceforge.plantuml.graphic.StringBounder; import net.sourceforge.plantuml.graphic.TextBlock; import net.sourceforge.plantuml.graphic.color.ColorType; import net.sourceforge.plantuml.style.PName; import net.sourceforge.plantuml.style.SName; import net.sourceforge.plantuml.style.Style; import net.sourceforge.plantuml.style.StyleSignatureBasic; import net.sourceforge.plantuml.svek.AbstractEntityImage; import net.sourceforge.plantuml.svek.Margins; import net.sourceforge.plantuml.svek.Ports; import net.sourceforge.plantuml.svek.ShapeType; import net.sourceforge.plantuml.svek.WithPorts; import net.sourceforge.plantuml.ugraphic.Shadowable; import net.sourceforge.plantuml.ugraphic.UComment; import net.sourceforge.plantuml.ugraphic.UGraphic; import net.sourceforge.plantuml.ugraphic.UGraphicStencil; import net.sourceforge.plantuml.ugraphic.UGroupType; import net.sourceforge.plantuml.ugraphic.URectangle; import net.sourceforge.plantuml.ugraphic.UStroke; import net.sourceforge.plantuml.ugraphic.UTranslate; import net.sourceforge.plantuml.ugraphic.color.HColor; import net.sourceforge.plantuml.ugraphic.color.HColorNone; public class EntityImageClass extends AbstractEntityImage implements Stencil, WithPorts { final private TextBlock body; final private Margins shield; final private EntityImageClassHeader header; final private Url url; final private double roundCorner; final private LeafType leafType; final private LineConfigurable lineConfig; public EntityImageClass(GraphvizVersion version, ILeaf entity, ISkinParam skinParam, PortionShower portionShower) { super(entity, entity.getColors().mute(skinParam)); this.leafType = entity.getLeafType(); this.lineConfig = entity; if (UseStyle.useBetaStyle()) this.roundCorner = getStyle().value(PName.RoundCorner).asDouble(); else this.roundCorner = getSkinParam().getRoundCorner(CornerParam.DEFAULT, null); this.shield = version != null && version.useShield() && entity.hasNearDecoration() ? Margins.uniform(16) : Margins.NONE; final boolean showMethods = portionShower.showPortion(EntityPortion.METHOD, entity); final boolean showFields = portionShower.showPortion(EntityPortion.FIELD, entity); this.body = entity.getBodier().getBody(FontParam.CLASS_ATTRIBUTE, getSkinParam(), showMethods, showFields, entity.getStereotype(), getStyle(), null); this.header = new EntityImageClassHeader(entity, getSkinParam(), portionShower); this.url = entity.getUrl99(); } public Dimension2D calculateDimension(StringBounder stringBounder) { final Dimension2D dimHeader = header.calculateDimension(stringBounder); final Dimension2D dimBody = body == null ? new Dimension2DDouble(0, 0) : body.calculateDimension(stringBounder); double width = Math.max(dimBody.getWidth(), dimHeader.getWidth()); if (width < getSkinParam().minClassWidth()) width = getSkinParam().minClassWidth(); final double height = dimBody.getHeight() + dimHeader.getHeight(); return new Dimension2DDouble(width, height); } @Override public Rectangle2D getInnerPosition(String member, StringBounder stringBounder, InnerStrategy strategy) { final Rectangle2D result = body.getInnerPosition(member, stringBounder, strategy); if (result == null) return result; final Dimension2D dimHeader = header.calculateDimension(stringBounder); final UTranslate translate = UTranslate.dy(dimHeader.getHeight()); return translate.apply(result); } final public void drawU(UGraphic ug) { ug.draw(new UComment("class " + getEntity().getCodeGetName())); if (url != null) ug.startUrl(url); final Map typeIDent = new EnumMap<>(UGroupType.class); typeIDent.put(UGroupType.CLASS, "elem " + getEntity().getCode() + " selected"); typeIDent.put(UGroupType.ID, "elem_" + getEntity().getCode()); ug.startGroup(typeIDent); drawInternal(ug); ug.closeGroup(); if (url != null) ug.closeUrl(); } private Style getStyle() { return StyleSignatureBasic.of(SName.root, SName.element, SName.classDiagram, SName.class_) // .withTOBECHANGED(getEntity().getStereotype()) // .with(getEntity().getStereostyles()) // .getMergedStyle(getSkinParam().getCurrentStyleBuilder()); } private Style getStyleHeader() { return StyleSignatureBasic.of(SName.root, SName.element, SName.classDiagram, SName.class_, SName.header) // .withTOBECHANGED(getEntity().getStereotype()) // .with(getEntity().getStereostyles()) // .getMergedStyle(getSkinParam().getCurrentStyleBuilder()); } private void drawInternal(UGraphic ug) { final StringBounder stringBounder = ug.getStringBounder(); final Dimension2D dimTotal = calculateDimension(stringBounder); final Dimension2D dimHeader = header.calculateDimension(stringBounder); final double widthTotal = dimTotal.getWidth(); final double heightTotal = dimTotal.getHeight(); final Shadowable rect = new URectangle(widthTotal, heightTotal).rounded(roundCorner) .withCommentAndCodeLine(getEntity().getCodeGetName(), getEntity().getCodeLine()); double shadow = 0; HColor classBorder = lineConfig.getColors().getColor(ColorType.LINE); HColor headerBackcolor = getEntity().getColors().getColor(ColorType.HEADER); HColor backcolor = getEntity().getColors().getColor(ColorType.BACK); if (UseStyle.useBetaStyle()) { shadow = getStyle().value(PName.Shadowing).asDouble(); if (classBorder == null) classBorder = getStyle().value(PName.LineColor).asColor(getSkinParam().getThemeStyle(), getSkinParam().getIHtmlColorSet()); if (headerBackcolor == null) headerBackcolor = backcolor == null ? getStyleHeader().value(PName.BackGroundColor) .asColor(getSkinParam().getThemeStyle(), getSkinParam().getIHtmlColorSet()) : backcolor; if (backcolor == null) backcolor = getStyle().value(PName.BackGroundColor).asColor(getSkinParam().getThemeStyle(), getSkinParam().getIHtmlColorSet()); } else { if (getSkinParam().shadowing(getEntity().getStereotype())) shadow = 4; if (classBorder == null) classBorder = SkinParamUtils.getColor(getSkinParam(), getStereo(), ColorParam.classBorder); if (backcolor == null) if (leafType == LeafType.ENUM) backcolor = SkinParamUtils.getColor(getSkinParam(), getStereo(), ColorParam.enumBackground, ColorParam.classBackground); else backcolor = SkinParamUtils.getColor(getSkinParam(), getStereo(), ColorParam.classBackground); if (headerBackcolor == null) headerBackcolor = getSkinParam().getHtmlColor(ColorParam.classHeaderBackground, getStereo(), false); } rect.setDeltaShadow(shadow); ug = ug.apply(classBorder); ug = ug.apply(backcolor.bg()); final UStroke stroke = getStroke(); UGraphic ugHeader = ug; if (roundCorner == 0 && headerBackcolor != null && backcolor.equals(headerBackcolor) == false) { ug.apply(stroke).draw(rect); final Shadowable rect2 = new URectangle(widthTotal, dimHeader.getHeight()); rect2.setDeltaShadow(0); ugHeader = ugHeader.apply(headerBackcolor.bg()); ugHeader.apply(stroke).draw(rect2); } else if (roundCorner != 0 && headerBackcolor != null && backcolor.equals(headerBackcolor) == false) { ug.apply(stroke).draw(rect); final Shadowable rect2 = new URectangle(widthTotal, dimHeader.getHeight()).rounded(roundCorner); final URectangle rect3 = new URectangle(widthTotal, roundCorner / 2); rect2.setDeltaShadow(0); rect3.setDeltaShadow(0); ugHeader = ugHeader.apply(headerBackcolor.bg()).apply(headerBackcolor); ugHeader.apply(stroke).draw(rect2); ugHeader.apply(stroke).apply(UTranslate.dy(dimHeader.getHeight() - rect3.getHeight())).draw(rect3); rect.setDeltaShadow(0); ug.apply(stroke).apply(new HColorNone().bg()).draw(rect); } else { ug.apply(stroke).draw(rect); } header.drawU(ugHeader, dimTotal.getWidth(), dimHeader.getHeight()); if (body != null) { final UGraphic ug2 = UGraphicStencil.create(ug, this, stroke); final UTranslate translate = UTranslate.dy(dimHeader.getHeight()); body.drawU(ug2.apply(translate)); } } @Override public Ports getPorts(StringBounder stringBounder) { final Dimension2D dimHeader = header.calculateDimension(stringBounder); if (body instanceof WithPorts) return ((WithPorts) body).getPorts(stringBounder).translateY(dimHeader.getHeight()); return new Ports(); } private UStroke getStroke() { if (UseStyle.useBetaStyle()) return getStyle().getStroke(); UStroke stroke = lineConfig.getColors().getSpecificLineStroke(); if (stroke == null) stroke = getSkinParam().getThickness(LineParam.classBorder, getStereo()); if (stroke == null) stroke = new UStroke(1.5); return stroke; } public ShapeType getShapeType() { if (((ILeaf) getEntity()).getPortShortNames().size() > 0) { return ShapeType.RECTANGLE_HTML_FOR_PORTS; } return ShapeType.RECTANGLE; } @Override public Margins getShield(StringBounder stringBounder) { return shield; } public double getStartingX(StringBounder stringBounder, double y) { return 0; } public double getEndingX(StringBounder stringBounder, double y) { return calculateDimension(stringBounder).getWidth(); } }