/******************************************************************************* * Copyright (c) 2007, 2010 Association for Decentralized Information Management * in Industry THTH ry. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * VTT Technical Research Centre of Finland - initial API and implementation *******************************************************************************/ package org.simantics.diagram.elements; import java.awt.Color; import java.awt.Font; import java.awt.geom.AffineTransform; import java.awt.geom.Rectangle2D; import java.util.function.Consumer; import org.simantics.g2d.canvas.ICanvasContext; import org.simantics.g2d.diagram.DiagramUtils; import org.simantics.g2d.diagram.IDiagram; import org.simantics.g2d.element.ElementHints; import org.simantics.g2d.element.ElementUtils; import org.simantics.g2d.element.IElement; import org.simantics.g2d.element.SceneGraphNodeKey; import org.simantics.g2d.element.handler.HandleMouseEvent; import org.simantics.g2d.element.handler.SceneGraph; import org.simantics.g2d.utils.Alignment; import org.simantics.scenegraph.g2d.G2DParentNode; import org.simantics.scenegraph.g2d.events.MouseEvent; import org.simantics.scenegraph.g2d.events.MouseEvent.MouseEnterEvent; import org.simantics.scenegraph.g2d.events.MouseEvent.MouseExitEvent; import org.simantics.utils.datastructures.hints.IHintContext.Key; /** * ElementHandler for text elements * In-line editing supported. * * @author Marko Luukkainen */ public class TextElementNoBounds implements SceneGraph, HandleMouseEvent { private static final long serialVersionUID = -148784588840819612L; public static final TextElementNoBounds INSTANCE = new TextElementNoBounds(); public static final Key SG_NODE = new SceneGraphNodeKey(TextNode.class, "TEXT_SG_NODE"); protected static final double DEFAULT_PADDING_X = 0.5; protected static final double DEFAULT_PADDING_Y = 0.5; protected static final double DEFAULT_SCALE = 0.235; protected final double originX; protected final double originY; protected final Alignment horizontalAlignment; protected final Alignment verticalAlignment; protected final double borderWidth; protected final boolean editable; protected final double paddingX; protected final double paddingY; protected final double scale; public TextElementNoBounds() { this(0, 0, Alignment.LEADING, 0); } public TextElementNoBounds(double originX, double originY, Alignment horizontalAlignment) { this(originX, originY, horizontalAlignment, 0); } public TextElementNoBounds(double originX, double originY, Alignment horizontalAlignment, double borderWidth) { this(originX, originY, horizontalAlignment, borderWidth, DEFAULT_PADDING_X, DEFAULT_PADDING_Y, true); } public TextElementNoBounds(double originX, double originY, Alignment horizontalAlignment, double borderWidth, double paddingX, double paddingY, boolean editable) { this(originX, originY, horizontalAlignment, borderWidth, paddingX, paddingY, editable, DEFAULT_SCALE); } public TextElementNoBounds(double originX, double originY, Alignment horizontalAlignment, double borderWidth, double paddingX, double paddingY, boolean editable, double scale) { this(originX, originY, horizontalAlignment, Alignment.BASELINE, borderWidth, paddingX, paddingY, editable, scale); } public TextElementNoBounds(double originX, double originY, Alignment horizontalAlignment, Alignment verticalAlignment, double borderWidth, double paddingX, double paddingY, boolean editable, double scale) { if (horizontalAlignment == null) throw new NullPointerException("null horizontal alignment"); this.originX = originX; this.originY = originY; this.horizontalAlignment = horizontalAlignment; this.verticalAlignment = verticalAlignment; this.borderWidth = borderWidth; this.editable = editable; this.paddingX = paddingX; this.paddingY = paddingY; this.scale = scale; } @Override public void init(final IElement e, G2DParentNode parent) { TextNode node = getOrCreateTextNode(e, parent); Font font = ElementUtils.getTextFont(e); Color color = ElementUtils.getTextColor(e); Color fillColor = ElementUtils.getFillColor(e); Color borderColor = ElementUtils.getBorderColor(e, Color.BLACK); String text = ElementUtils.getText(e); AffineTransform at = ElementUtils.getTransform(e); Alignment hAlign = ElementUtils.getHintOrDefault(e, ElementHints.KEY_HORIZONTAL_ALIGN, horizontalAlignment); Alignment vAlign = ElementUtils.getHintOrDefault(e, ElementHints.KEY_VERTICAL_ALIGN, verticalAlignment); Double borderWidth = ElementUtils.getHintOrDefault(e, MonitorClass.KEY_BORDER_WIDTH, this.borderWidth); node.init(text, font, color, originX, originY, scale); node.setBackgroundColor(fillColor); node.setBorderColor(borderColor); node.setHorizontalAlignment((byte) hAlign.ordinal()); node.setVerticalAlignment((byte) vAlign.ordinal()); node.setPadding(paddingX, paddingY); node.setBorderWidth(borderWidth.floatValue()); node.setEditable(editable); if (at != null) node.setTransform(at); if(Boolean.TRUE.equals(ElementUtils.getHintOrDefault(e, ElementHints.KEY_RESIZABLE, false))) { Rectangle2D bounds = e.getHint(ElementHints.KEY_BOUNDS); if(bounds != null) { node.setTargetBounds(bounds); node.setWrapText(true); } } } protected TextNode getOrCreateTextNode(IElement e, G2DParentNode parent) { return ElementUtils.getOrCreateNode(e, parent, SG_NODE, "text", TextNode.class, new TextNodeCallBack(e)); } private class TextNodeCallBack implements Consumer { IElement e; public TextNodeCallBack(IElement e) { this.e = e; } @Override public void accept(TextNode node) { node.setTextListener(new ITextListener() { @Override public void textChanged() {} @Override public void textEditingStarted() {} @Override public void textEditingCancelled() { TextNode node = (TextNode) e.getHint(SG_NODE); if (node != null) endEdit(node); } @Override public void textEditingEnded() { TextNode node = (TextNode) e.getHint(SG_NODE); if (node == null) return; //System.out.println("Node text changed: " + node.getText()); ElementUtils.setText(e, node.getText()); IDiagram diagram = ElementUtils.getDiagram(e); DiagramUtils.synchronizeHintsToBackend(diagram, e); endEdit(node); } }); } } @Override public void cleanup(IElement e) { ElementUtils.removePossibleNode(e, SG_NODE); } // FIXME: hazardous with TextElementHandler.INSTANCE TextEditActivation editActivation = null; @Override public boolean handleMouseEvent(IElement e, ICanvasContext ctx, MouseEvent me) { if (me instanceof MouseEnterEvent) { e.setHint(ElementHints.KEY_HOVER, true); } else if (me instanceof MouseExitEvent) { e.setHint(ElementHints.KEY_HOVER, false); } return false; } protected void endEdit(TextNode node) { if (editActivation != null) { editActivation.release(); editActivation = null; node.setEditMode(false); node.repaint(); } } @Override public int hashCode() { final int prime = 31; int result = 1; long temp; temp = Double.doubleToLongBits(borderWidth); result = prime * result + (int) (temp ^ (temp >>> 32)); result = prime * result + horizontalAlignment.hashCode(); temp = Double.doubleToLongBits(originX); result = prime * result + (int) (temp ^ (temp >>> 32)); temp = Double.doubleToLongBits(originY); result = prime * result + (int) (temp ^ (temp >>> 32)); temp = Double.doubleToLongBits(paddingX); result = prime * result + (int) (temp ^ (temp >>> 32)); temp = Double.doubleToLongBits(paddingY); result = prime * result + (int) (temp ^ (temp >>> 32)); return result; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; TextElementNoBounds other = (TextElementNoBounds) obj; if (Double.doubleToLongBits(borderWidth) != Double.doubleToLongBits(other.borderWidth)) return false; if (horizontalAlignment != other.horizontalAlignment) return false; if (Double.doubleToLongBits(originX) != Double.doubleToLongBits(other.originX)) return false; if (Double.doubleToLongBits(originY) != Double.doubleToLongBits(other.originY)) return false; if (Double.doubleToLongBits(paddingX) != Double.doubleToLongBits(other.paddingX)) return false; if (Double.doubleToLongBits(paddingY) != Double.doubleToLongBits(other.paddingY)) return false; return true; } }