]> gerrit.simantics Code Review - simantics/platform.git/blobdiff - bundles/org.simantics.diagram/src/org/simantics/diagram/elements/TextNode.java
Merge "Counting of pending nodes does not work if null text is saved as """
[simantics/platform.git] / bundles / org.simantics.diagram / src / org / simantics / diagram / elements / TextNode.java
index 85923bc35763923d5d2015178c867d73b84a98a7..bf1bf563b32c9f9e89c2c2355902d2a5b5f1ef02 100644 (file)
@@ -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<Transferable> trs = new ArrayList<>(2);