Sync git svn branch with SVN repository r33189.
[simantics/platform.git] / bundles / org.simantics.diagram / src / org / simantics / diagram / elements / TextNode.java
index 6fb0611f1efb34a99474bda00fcfd0f15bee3941..f948bc053c901b7a1e92201e5c4904fb61be6f6a 100644 (file)
@@ -129,11 +129,6 @@ public class TextNode extends G2DNode implements IDynamicSelectionPainterNode, L
      */\r
     protected String text = null;\r
 \r
-    /**\r
-     * Tells if this node is still pending for real results or not.\r
-     */\r
-    protected boolean pending = false;\r
-\r
     /**\r
      * The font used to render the {@link #text}.\r
      */\r
@@ -180,12 +175,25 @@ public class TextNode extends G2DNode implements IDynamicSelectionPainterNode, L
      */\r
     protected byte verticalAlignment = 3;\r
 \r
-    protected boolean hover = false;\r
-    boolean editable = false;\r
-    boolean showSelection = true;\r
-    \r
-    \r
-    boolean wrapText = true;\r
+    /**\r
+     * Tells if this node is still pending for real results or not.\r
+     */\r
+    protected static final int STATE_PENDING = (1 << 0);\r
+    protected static final int STATE_HOVER   = (1 << 1);\r
+    protected static final int STATE_EDITABLE = (1 << 2);\r
+    protected static final int STATE_SHOW_SELECTION = (1 << 3);\r
+    protected static final int STATE_WRAP_TEXT = (1 << 4);\r
+    protected transient static final int STATE_EDITING = (1 << 5);\r
+    protected transient static final int STATE_VALID = (1 << 6);\r
+    protected transient static final int STATE_X_OFFSET_IS_DIRTY = (1 << 7);\r
+    protected static final int STATE_ALWAYS_ADD_LISTENERS = (1 << 8);\r
+    protected static final int STATE_LISTENERS_ADDED = (1 << 9);\r
+\r
+    /**\r
+     * A combination of all the STATE_ constants defined in this class,\r
+     * e.g. {@link #STATE_PENDING}.\r
+     */\r
+    protected int state = STATE_SHOW_SELECTION | STATE_WRAP_TEXT | STATE_VALID | STATE_X_OFFSET_IS_DIRTY;\r
 \r
     protected RVI dataRVI = null;\r
 \r
@@ -201,11 +209,6 @@ public class TextNode extends G2DNode implements IDynamicSelectionPainterNode, L
     ITextListener textListener;\r
     ITextContentFilter editContentFilter;\r
 \r
-    transient boolean editing = false;\r
-    transient boolean valid = true;\r
-\r
-    private transient boolean xOffsetIsDirty = true;\r
-\r
     /**\r
      * The renderable line structures parsed from {@link #text} by\r
      * {@link #parseLines(String)}, laid out by\r
@@ -248,12 +251,55 @@ public class TextNode extends G2DNode implements IDynamicSelectionPainterNode, L
         super.cleanup();\r
     }\r
 \r
+    protected boolean hasState(int flags) {\r
+        return (state & flags) == flags;\r
+    }\r
+\r
+    protected void setState(int flags) {\r
+        this.state |= flags;\r
+    }\r
+\r
+    protected void setState(int flags, boolean set) {\r
+        if (set)\r
+            this.state |= flags;\r
+        else\r
+            this.state &= ~flags;\r
+    }\r
+\r
+    protected void clearState(int flags) {\r
+        this.state &= ~flags;\r
+    }\r
+\r
+    protected void setListeners(boolean add) {\r
+        if (add)\r
+            addListeners();\r
+        else\r
+            removeListeners();\r
+    }\r
+\r
     protected void addListeners() {\r
-        addEventHandler(this);\r
+        if (!hasState(STATE_LISTENERS_ADDED)) {\r
+            addEventHandler(this);\r
+            setState(STATE_LISTENERS_ADDED);\r
+        }\r
     }\r
 \r
     protected void removeListeners() {\r
-        removeEventHandler(this);\r
+        if (hasState(STATE_LISTENERS_ADDED)) {\r
+            removeEventHandler(this);\r
+            clearState(STATE_LISTENERS_ADDED);\r
+        }\r
+    }\r
+\r
+    /**\r
+     * Set to true to always enable event listening in this TextNode to allow the text node to keep track of hovering, etc. and to allow DnD even when \r
+     * @param force\r
+     */\r
+    public void setForceEventListening(boolean force) {\r
+        setState(STATE_ALWAYS_ADD_LISTENERS, force);\r
+        if (force && !hasState(STATE_EDITABLE)) {\r
+            setListeners(force);\r
+        }\r
     }\r
 \r
     /**\r
@@ -277,11 +323,11 @@ public class TextNode extends G2DNode implements IDynamicSelectionPainterNode, L
      * @return null if no change to edit state was made\r
      */\r
     protected Boolean setEditMode(boolean edit, boolean notify) {\r
-        if (edit && !editable)\r
+        if (edit && !hasState(STATE_EDITABLE))\r
             return null;\r
-        if (editing == edit)\r
+        if (hasState(STATE_EDITING) == edit)\r
             return null;\r
-        this.editing = edit;\r
+        setState(STATE_EDITING);\r
         if (edit) {\r
             caret = text != null ? text.length() : 0;\r
             selectionTail = 0;\r
@@ -298,29 +344,26 @@ public class TextNode extends G2DNode implements IDynamicSelectionPainterNode, L
 \r
     @SyncField({"editable"})\r
     public void setEditable(boolean editable) {\r
-        boolean changed = this.editable != editable;\r
-        this.editable = editable;\r
-        if (editing && !editable)\r
+        boolean changed = hasState(STATE_EDITABLE) != editable;\r
+        setState(STATE_EDITABLE, editable);\r
+        if (hasState(STATE_EDITING) && !editable)\r
             setEditMode(false);\r
-        if (changed) {\r
-            if (editable)\r
-                addListeners();\r
-            else\r
-                removeListeners();\r
+        if (changed && !hasState(STATE_ALWAYS_ADD_LISTENERS)) {\r
+            setListeners(editable);\r
         }\r
     }\r
 \r
     public boolean isEditable() {\r
-        return editable;\r
+        return hasState(STATE_EDITABLE);\r
     }\r
 \r
     public boolean isEditMode() {\r
-        return editing;\r
+        return hasState(STATE_EDITING);\r
     }\r
     \r
     @SyncField({"wrapText"})\r
     public void setWrapText(boolean wrapText) {\r
-        this.wrapText = wrapText;\r
+        setState(STATE_WRAP_TEXT, wrapText);\r
     }\r
     \r
     /**\r
@@ -328,16 +371,16 @@ public class TextNode extends G2DNode implements IDynamicSelectionPainterNode, L
      * the width of the box is fixed\r
      */\r
     public boolean isWrapText() {\r
-        return this.wrapText;\r
+        return hasState(STATE_WRAP_TEXT);\r
     }\r
 \r
     @SyncField({"showSelection"})\r
     public void setShowSelection(boolean showSelection) {\r
-        this.showSelection = showSelection;\r
+        setState(STATE_SHOW_SELECTION, showSelection);\r
     }\r
 \r
     public boolean showsSelection() {\r
-        return showSelection;\r
+        return hasState(STATE_SHOW_SELECTION);\r
     }\r
 \r
     /**\r
@@ -353,7 +396,7 @@ public class TextNode extends G2DNode implements IDynamicSelectionPainterNode, L
         // no value => value\r
         if(this.text == null && text != null) NodeUtil.decreasePending(this);\r
 \r
-        if (editing)\r
+        if (hasState(STATE_EDITING))\r
             return;\r
 \r
         this.text = new String(text != null ? text : "");\r
@@ -399,7 +442,7 @@ public class TextNode extends G2DNode implements IDynamicSelectionPainterNode, L
     @SyncField({"text","caret","selectionTail"})\r
     public void setText(String text) {\r
         //System.out.println("TextNode.setText('" + text + "', " + editing + ")");\r
-        if (editing)\r
+        if (hasState(STATE_EDITING))\r
             return;\r
 \r
         // value => no value\r
@@ -416,9 +459,11 @@ public class TextNode extends G2DNode implements IDynamicSelectionPainterNode, L
 \r
     @SyncField({"pending"})\r
     public void setPending(boolean pending) {\r
-        if(!this.pending && pending) NodeUtil.increasePending(this);\r
-        if(this.pending && !pending) NodeUtil.decreasePending(this);\r
-        this.pending = pending;\r
+        boolean p = hasState(STATE_PENDING);\r
+        if(!p && pending) NodeUtil.increasePending(this);\r
+        if(p && !pending) NodeUtil.decreasePending(this);\r
+        if(p != pending)\r
+            setState(STATE_PENDING, pending);\r
     }\r
 \r
     @SyncField({"fixedWidth"})\r
@@ -448,16 +493,16 @@ public class TextNode extends G2DNode implements IDynamicSelectionPainterNode, L
        }\r
 \r
     public final void synchronizeWrapText(boolean wrap) {\r
-        wrapText = wrap;\r
+        setState(STATE_WRAP_TEXT, wrap);\r
     }\r
 \r
     public boolean isHovering() {\r
-        return hover;\r
+        return hasState(STATE_HOVER);\r
     }\r
 \r
     @SyncField({"hover"})\r
     public void setHover(boolean hover) {\r
-        this.hover = hover;\r
+        setState(STATE_HOVER, hover);\r
         repaint();\r
     }\r
 \r
@@ -564,6 +609,8 @@ public class TextNode extends G2DNode implements IDynamicSelectionPainterNode, L
 \r
         Color color = this.color;\r
         boolean isSelected = NodeUtil.isSelected(this, 1);\r
+        boolean hover = hasState(STATE_HOVER);\r
+        boolean editing = hasState(STATE_EDITING);\r
 \r
         if (!isSelected && hover) {\r
             color = add(color, 120, 120, 120);\r
@@ -634,7 +681,7 @@ public class TextNode extends G2DNode implements IDynamicSelectionPainterNode, L
                                                fieldName.equals("created_by") );\r
         }\r
 \r
-        Color backgroundColor = valid ? this.backgroundColor : Color.red;\r
+        Color backgroundColor = hasState(STATE_VALID) ? this.backgroundColor : Color.red;\r
 \r
         // RENDER\r
         if ( !isPdfField ) {\r
@@ -887,7 +934,7 @@ public class TextNode extends G2DNode implements IDynamicSelectionPainterNode, L
 \r
         if(validator != null) {\r
             String error = validator.apply(text);\r
-            valid = (error == null);\r
+            setState(STATE_VALID, (error == null));\r
         }\r
 \r
         resetCaches();\r
@@ -908,7 +955,7 @@ public class TextNode extends G2DNode implements IDynamicSelectionPainterNode, L
 \r
     @ServerSide\r
     protected void fireTextEditingCancelled() {\r
-        valid = true;\r
+        setState(STATE_VALID);\r
 \r
         if (deactivateEdit()) {\r
             if (textListener != null)\r
@@ -925,9 +972,9 @@ public class TextNode extends G2DNode implements IDynamicSelectionPainterNode, L
 \r
     @ServerSide\r
     public void fireTextEditingEnded() {\r
-        if (!valid) {\r
+        if (!hasState(STATE_VALID)) {\r
             fireTextEditingCancelled();\r
-            valid = true;\r
+            setState(STATE_VALID);\r
             return;\r
         }\r
 \r
@@ -957,13 +1004,13 @@ public class TextNode extends G2DNode implements IDynamicSelectionPainterNode, L
     }\r
 \r
     private void invalidateXOffset() {\r
-        xOffsetIsDirty = true;\r
+        setState(STATE_X_OFFSET_IS_DIRTY);\r
     }\r
 \r
     private void computeEditingXOffset() {\r
 \r
         if(lines == null) return;\r
-        if(!xOffsetIsDirty) return;\r
+        if(!hasState(STATE_X_OFFSET_IS_DIRTY)) return;\r
         if(fixedWidth > 0f) {\r
 \r
             // TODO: implement\r
@@ -979,7 +1026,7 @@ public class TextNode extends G2DNode implements IDynamicSelectionPainterNode, L
 \r
         }\r
 \r
-        xOffsetIsDirty = false;\r
+        clearState(STATE_X_OFFSET_IS_DIRTY);\r
 \r
     }\r
 \r
@@ -1259,7 +1306,7 @@ public class TextNode extends G2DNode implements IDynamicSelectionPainterNode, L
         // Parse & layout (unaligned)\r
         Line[] lines = null;\r
         \r
-        if(wrapText) {\r
+        if(hasState(STATE_WRAP_TEXT)) {\r
             float width = fixedWidth;\r
             if(width <= 0 && targetBounds != null)\r
                 width = (float) (((targetBounds.getWidth() - 2*paddingX)) * scaleRecip);\r
@@ -1545,7 +1592,7 @@ public class TextNode extends G2DNode implements IDynamicSelectionPainterNode, L
 \r
     @Override\r
     protected boolean handleCommand(CommandEvent e) {\r
-        if (!editing)\r
+        if (!hasState(STATE_EDITING))\r
             return false;\r
 \r
         if (Commands.SELECT_ALL.equals(e.command)) {\r
@@ -1557,7 +1604,7 @@ public class TextNode extends G2DNode implements IDynamicSelectionPainterNode, L
 \r
     @Override\r
     protected boolean keyPressed(KeyPressedEvent event) {\r
-        if (!editing)\r
+        if (!hasState(STATE_EDITING))\r
             return false;\r
 \r
         char c = event.character;\r
@@ -1658,7 +1705,7 @@ public class TextNode extends G2DNode implements IDynamicSelectionPainterNode, L
                 case KeyEvent.VK_ESCAPE:\r
                     text = textBeforeEdit;\r
                     resetCaches();\r
-                    editing = false;\r
+                    clearState(STATE_EDITING);\r
                     fireTextEditingCancelled();\r
                     return true;\r
 \r
@@ -1725,7 +1772,7 @@ public class TextNode extends G2DNode implements IDynamicSelectionPainterNode, L
         if (event.button != MouseClickEvent.LEFT_BUTTON)\r
             return false;\r
         \r
-        if (hover) {\r
+        if (hasState(STATE_HOVER)) {\r
                hoverClick++;\r
                if (hoverClick < 2)\r
                        return false;\r
@@ -1734,7 +1781,7 @@ public class TextNode extends G2DNode implements IDynamicSelectionPainterNode, L
             if (ctx == null)\r
                 return false;\r
             IElement e = DiagramNodeUtil.getElement(ctx, this);\r
-            if (!editing) {\r
+            if (!hasState(STATE_EDITING)) {\r
                if (Boolean.TRUE.equals(setEditMode(true))) {\r
                        editActivation = activateEdit(0, e, ctx);\r
                        repaint();\r
@@ -1742,7 +1789,7 @@ public class TextNode extends G2DNode implements IDynamicSelectionPainterNode, L
             } \r
         } else {\r
                hoverClick = 0;\r
-            if (editing) {\r
+            if (hasState(STATE_EDITING)) {\r
                 fireTextEditingEnded();\r
             }\r
         }\r
@@ -1772,13 +1819,13 @@ public class TextNode extends G2DNode implements IDynamicSelectionPainterNode, L
 \r
     @Override\r
     protected boolean mouseButtonPressed(MouseButtonPressedEvent event) {\r
-        if (!editing)\r
+        if (!hasState(STATE_EDITING))\r
             return false;\r
         \r
         Point2D local = controlToLocal( event.controlPosition );\r
         // FIXME: once the event coordinate systems are cleared up, remove this workaround\r
         local = parentToLocal(local);\r
-        if (hover && this.containsLocal(local)) {\r
+        if (hasState(STATE_HOVER) && this.containsLocal(local)) {\r
             setCaret(local, event.isShiftDown());\r
         }\r
         return false;\r
@@ -1787,8 +1834,8 @@ public class TextNode extends G2DNode implements IDynamicSelectionPainterNode, L
     @Override\r
     protected boolean mouseMoved(MouseMovedEvent event) {\r
         boolean hit = hitTest(event, 3.0);\r
-        if (hit != hover) {\r
-            hover = hit;\r
+        if (hit != hasState(STATE_HOVER)) {\r
+            setState(STATE_HOVER, hit);\r
             repaint();\r
         }\r
         return false;\r