X-Git-Url: https://gerrit.simantics.org/r/gitweb?p=simantics%2Fplatform.git;a=blobdiff_plain;f=bundles%2Forg.simantics.diagram%2Fsrc%2Forg%2Fsimantics%2Fdiagram%2Felements%2FTextNode.java;h=bf1bf563b32c9f9e89c2c2355902d2a5b5f1ef02;hp=85923bc35763923d5d2015178c867d73b84a98a7;hb=680a2159d36d893cf7fbf0212e7ffbff847a8f82;hpb=0ae2b770234dfc3cbb18bd38f324125cf0faca07 diff --git a/bundles/org.simantics.diagram/src/org/simantics/diagram/elements/TextNode.java b/bundles/org.simantics.diagram/src/org/simantics/diagram/elements/TextNode.java index 85923bc35..bf1bf563b 100644 --- a/bundles/org.simantics.diagram/src/org/simantics/diagram/elements/TextNode.java +++ b/bundles/org.simantics.diagram/src/org/simantics/diagram/elements/TextNode.java @@ -34,7 +34,6 @@ import java.awt.font.TextLayout; import java.awt.geom.AffineTransform; import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; -import java.io.IOException; import java.text.AttributedCharacterIterator; import java.text.AttributedString; import java.util.ArrayList; @@ -52,6 +51,8 @@ import org.simantics.scenegraph.LoaderNode; import org.simantics.scenegraph.ScenegraphUtils; import org.simantics.scenegraph.g2d.G2DNode; import org.simantics.scenegraph.g2d.G2DPDFRenderingHints; +import org.simantics.scenegraph.g2d.G2DRenderingHints; +import org.simantics.scenegraph.g2d.G2DRenderingHints.TextRenderingMode; import org.simantics.scenegraph.g2d.events.Event; import org.simantics.scenegraph.g2d.events.EventTypes; import org.simantics.scenegraph.g2d.events.KeyEvent.KeyPressedEvent; @@ -61,7 +62,6 @@ import org.simantics.scenegraph.g2d.events.MouseEvent.MouseClickEvent; import org.simantics.scenegraph.g2d.events.MouseEvent.MouseDoubleClickedEvent; import org.simantics.scenegraph.g2d.events.MouseEvent.MouseDragBegin; import org.simantics.scenegraph.g2d.events.MouseEvent.MouseMovedEvent; -import org.simantics.scenegraph.g2d.events.NodeEventHandler; import org.simantics.scenegraph.g2d.events.command.CommandEvent; import org.simantics.scenegraph.g2d.events.command.Commands; import org.simantics.scenegraph.utils.GeometryUtils; @@ -75,13 +75,7 @@ import org.simantics.ui.dnd.PlaintextTransfer; import org.simantics.ui.fonts.Fonts; import org.simantics.utils.threads.AWTThread; -import com.lowagie.text.DocumentException; -import com.lowagie.text.Element; -import com.lowagie.text.Rectangle; -import com.lowagie.text.pdf.FontMapper; -import com.lowagie.text.pdf.PdfFormField; import com.lowagie.text.pdf.PdfWriter; -import com.lowagie.text.pdf.TextField; import gnu.trove.list.array.TIntArrayList; @@ -99,7 +93,6 @@ import gnu.trove.list.array.TIntArrayList; * TODO: * o proper support for defining clipping bounds for the text (needed for page templates) (currently through fixedWidth) * o fix editing xOffset to work with fixed width and multi-line text - * o * * @see Line * @see TextLayout @@ -108,6 +101,12 @@ public class TextNode extends G2DNode implements IDynamicSelectionPainterNode, L private static final long serialVersionUID = 654692698101485672L; + public static enum TextFlipping { + Disabled, + VerticalTextUpwards, + VerticalTextDownwards, + } + /** * TODO: justify existence for this */ @@ -191,6 +190,8 @@ public class TextNode extends G2DNode implements IDynamicSelectionPainterNode, L protected transient static final int STATE_X_OFFSET_IS_DIRTY = (1 << 7); protected static final int STATE_ALWAYS_ADD_LISTENERS = (1 << 8); protected static final int STATE_LISTENERS_ADDED = (1 << 9); + protected static final int STATE_AUTOMATIC_TEXT_FLIP_ENABLED = (1 << 10); + protected static final int STATE_AUTOMATIC_TEXT_FLIP_VERTICAL_DOWN = (1 << 11); /** * A combination of all the STATE_ constants defined in this class, @@ -402,17 +403,33 @@ public class TextNode extends G2DNode implements IDynamicSelectionPainterNode, L if (hasState(STATE_EDITING)) return; - this.text = new String(text != null ? text : ""); + this.text = text; this.font = font; this.color = color; this.scale = scale; this.scaleRecip = 1.0 / scale; this.caret = 0; this.selectionTail = 0; + resetCaches(); } + public void setAutomaticTextFlipping(TextFlipping type) { + switch (type) { + case Disabled: + clearState(STATE_AUTOMATIC_TEXT_FLIP_ENABLED | STATE_AUTOMATIC_TEXT_FLIP_VERTICAL_DOWN); + break; + case VerticalTextDownwards: + setState(STATE_AUTOMATIC_TEXT_FLIP_ENABLED | STATE_AUTOMATIC_TEXT_FLIP_VERTICAL_DOWN); + break; + case VerticalTextUpwards: + setState(STATE_AUTOMATIC_TEXT_FLIP_ENABLED); + clearState(STATE_AUTOMATIC_TEXT_FLIP_VERTICAL_DOWN); + break; + } + } + @SyncField({"paddingX", "paddingY"}) public void setPadding(double x, double y) { this.paddingX = x; @@ -453,8 +470,8 @@ public class TextNode extends G2DNode implements IDynamicSelectionPainterNode, L // no value => value if(this.text == null && text != null) NodeUtil.decreasePending(this); - this.text = text != null ? text : ""; - caret = Math.min(caret, this.text.length()); + this.text = text; + caret = text != null ? Math.min(caret, text.length()) : 0; selectionTail = caret; resetCaches(); @@ -628,7 +645,8 @@ public class TextNode extends G2DNode implements IDynamicSelectionPainterNode, L // Safety for not rendering when the scale of this text is too small. // When the scale is too small it will cause internal exceptions while // stroking fonts. - double currentScale = GeometryUtils.getScale(g.getTransform()); + AffineTransform curTr = g.getTransform(); + double currentScale = GeometryUtils.getScale(curTr); //System.out.println("currentScale: " + currentScale); if (currentScale < 1e-6) return; @@ -652,6 +670,27 @@ public class TextNode extends G2DNode implements IDynamicSelectionPainterNode, L r.setRect(x, y, w, h); } + if (hasState(STATE_AUTOMATIC_TEXT_FLIP_ENABLED)) { + boolean needsXFlip; + boolean needsYFlip; + if (curTr.getScaleX() != 0) { + needsXFlip = curTr.getScaleX() < 0.0; + needsYFlip = curTr.getScaleY() < 0.0; + } else { + boolean flipAll = !hasState(STATE_AUTOMATIC_TEXT_FLIP_VERTICAL_DOWN); + needsXFlip = (curTr.getShearY() < 0.0) ^ flipAll; + needsYFlip = (curTr.getShearX() > 0.0) ^ flipAll; + } + if (needsXFlip || needsYFlip) { + double centerX = r.getWidth()*0.5 + r.getX(); + double centerY = r.getHeight()*0.5 + r.getY(); + + g.translate(centerX, centerY); + g.scale(needsXFlip ? -1.0 : 1.0, needsYFlip ? -1.0 : 1.0); + g.translate(-centerX, -centerY); + } + } + Rectangle2D textClip = r.getBounds2D(); expandBoundsUnscaled(r); @@ -673,23 +712,14 @@ public class TextNode extends G2DNode implements IDynamicSelectionPainterNode, L // PDF PdfWriter writer = (PdfWriter) g.getRenderingHint(G2DPDFRenderingHints.KEY_PDF_WRITER); - boolean isRenderingPdf = writer != null; - boolean isPdfField = false; - String fieldName = null; - if (writer != null) { - // TODO: replace this hack with proper text field name field - fieldName = NodeUtil.getNodeName(this); - isPdfField = ( fieldName.equals("approved_by") || - fieldName.equals("checked_by") || - fieldName.equals("designer name") || - fieldName.equals("created_by") ); - } + TextRenderingMode renderingMode = (TextRenderingMode) g.getRenderingHint(G2DRenderingHints.KEY_TEXT_RENDERING_MODE); + boolean renderAsText = writer != null || renderingMode == TextRenderingMode.AS_TEXT; + /// PDF Color backgroundColor = hasState(STATE_VALID) ? this.backgroundColor : Color.red; // RENDER - if ( !isPdfField ) { - + { // Fill background if necessary if (backgroundColor != null) { g.setColor(backgroundColor); @@ -703,7 +733,7 @@ public class TextNode extends G2DNode implements IDynamicSelectionPainterNode, L // Base text g.setColor(color); - renderText(g, xOffset, isRenderingPdf); + renderText(g, xOffset, renderAsText); Shape clip = g.getClip(); @@ -717,7 +747,7 @@ public class TextNode extends G2DNode implements IDynamicSelectionPainterNode, L g.fill(selShape); g.setColor(Color.WHITE); // #6459: render as text in PDF and paths on screen - if (isRenderingPdf) + if (renderAsText) g.drawString(line.getText(), 0, 0); else line.layout.draw(g, 0, 0); @@ -727,108 +757,17 @@ public class TextNode extends G2DNode implements IDynamicSelectionPainterNode, L g.setClip(clip); - // Caret - renderCaret(g); - } else { if (renderText) { g.setColor(color); - renderText(g, 0, isRenderingPdf); + renderText(g, 0, renderAsText); } } - } else { - // PDF - // TODO: multiline support -// try { - AffineTransform at = g.getTransform(); - float height = writer.getPageSize().getHeight(); - Rectangle2D rr = textClip; - // Point2D pt1 = new Point2D.Double(rr.getX(), rr.getY()+rr.getHeight()); - // Point2D pt2 = new Point2D.Double(rr.getX()+rr.getWidth(), rr.getY()); - Point2D pt1 = new Point2D.Double(0, 0); - Point2D pt2 = new Point2D.Double(47.f/*+rr.getWidth()*/, -rr.getHeight()); - pt1 = at.transform(pt1, pt1); - pt2 = at.transform(pt2, pt2); - Rectangle rectangle = new Rectangle( - (float) pt1.getX(), - height-(float) pt1.getY(), - (float) pt2.getX(), - height-(float) pt2.getY()); - - FontMapper mapper = (FontMapper) g.getRenderingHint(G2DPDFRenderingHints.KEY_PDF_FONTMAPPER); -// FontMetrics fm = g.getFontMetrics(font); - - // TODO Oikea leveys - // TODO Uniikki nimi - /* - PdfFormField field = PdfFormField.createTextField(writer, false, false, 20); - field.setFieldName(this.getId().toString()); - field.setWidget(rectangle, PdfAnnotation.HIGHLIGHT_NONE); - field.setQuadding(PdfFormField.Q_RIGHT); - field.setFieldFlags(PdfFormField.FF_READ_ONLY); - field.setRotate(90); - writer.addAnnotation(field); - */ - - - // Signature Field - /* - if (text==null) { - PdfFormField field = PdfFormField.createSignature(writer); - field.setWidget(rectangle, PdfAnnotation.HIGHLIGHT_NONE); - field.setFieldName(fieldName); - field.setQuadding(PdfFormField.Q_LEFT); - field.setFlags(PdfAnnotation.FLAGS_PRINT); - //field.setFieldFlags(PdfFormField.FF_READ_ONLY) - field.setFieldFlags(PdfFormField.FF_EDIT); - field.setPage(); - field.setMKBackgroundColor( backgroundColor!=null?Color.WHITE:backgroundColor ); - PdfAppearance tp = PdfAppearance.createAppearance(writer, 72, 48); - tp.rectangle(rectangle); - tp.stroke(); - field.setAppearance(PdfAnnotation.APPEARANCE_NORMAL, tp); - writer.addAnnotation(field); - } else */ - { - // Text Field - try { - TextField textField = new TextField(writer, rectangle, fieldName); - textField.setFieldName(fieldName); - textField.setFont(mapper.awtToPdf(font)); - textField.setBorderStyle(0); - //textField.setAlignment(Element.ALIGN_LEFT); - textField.setAlignment(Element.ALIGN_BOTTOM); - textField.setRotation(90); - textField.setOptions(TextField.EDIT|TextField.DO_NOT_SPELL_CHECK); - if ( text!=null ) { - textField.setText(text); - } - if ( color!=null ) { - textField.setTextColor(color); - } - textField.setBackgroundColor( backgroundColor!=null?Color.WHITE:backgroundColor ); - PdfFormField field = textField.getTextField(); - writer.addAnnotation(field); - } catch (IOException e) { - e.printStackTrace(); - } catch (DocumentException e) { - e.printStackTrace(); - } - } - -// } catch (IOException e) { -// // TODO Auto-generated catch block -// e.printStackTrace(); -// } catch (DocumentException e) { -// // TODO Auto-generated catch block -// e.printStackTrace(); -// } } - /// PDF g.setClip(clipSave); @@ -887,11 +826,11 @@ public class TextNode extends G2DNode implements IDynamicSelectionPainterNode, L } } } - private void renderText(Graphics2D g, float xOffset, boolean isRenderingPdf) { + private void renderText(Graphics2D g, float xOffset, boolean renderAsText) { //g.draw(tightBoundsCache); // DEBUG for (Line line : lines) { // #6459: render as text in PDF and paths on screen - if (isRenderingPdf) + if (renderAsText) g.drawString(line.getText(), line.alignedPosX + xOffset, line.alignedPosY); else line.layout.draw(g, line.alignedPosX + xOffset, line.alignedPosY); @@ -919,6 +858,18 @@ public class TextNode extends G2DNode implements IDynamicSelectionPainterNode, L protected void renderSelectedHover(Graphics2D g, boolean isSelected, boolean isHovering) { } + public String editText(String text) { + + String error = validator != null ? validator.apply(text) : null; + if (error == null) { + this.text = text; + if (textListener != null) { + textListener.textEditingEnded(); + } + } + return error; + } + /** * Replaces the current selection with the content or inserts * the content at caret. After the insertion the caret @@ -1487,6 +1438,9 @@ public class TextNode extends G2DNode implements IDynamicSelectionPainterNode, L y += line.layout.getDescent() + line.layout.getLeading() + line.layout.getAscent(); Rectangle2D bbox = line.layout.getLogicalHighlightShape(0, lineText.length()).getBounds2D(); + // HighlightShape is not large enough, if font is italic. + Rectangle2D bbox2 = line.layout.getBounds(); + bbox.add(bbox2); bbox.setFrame(bbox.getX(), bbox.getY() + line.drawPosY, bbox.getWidth(), bbox.getHeight()); line.bbox = bbox; } @@ -1867,7 +1821,6 @@ public class TextNode extends G2DNode implements IDynamicSelectionPainterNode, L protected boolean mouseDragged(MouseDragBegin e) { if (isHovering() && (isControlDown(e) || isShiftDown(e)) - && e.context instanceof NodeEventHandler && (dataRVI != null || text != null)) { List trs = new ArrayList<>(2);