]> gerrit.simantics Code Review - simantics/platform.git/blobdiff - bundles/org.simantics.spreadsheet.ui/src/org/simantics/spreadsheet/ui/TrackedText.java
Fixed all line endings of the repository
[simantics/platform.git] / bundles / org.simantics.spreadsheet.ui / src / org / simantics / spreadsheet / ui / TrackedText.java
index 168efa16e04b5d5785d5fa79847beb6d777ac2b5..ef1cd6ab1d075c93dfde58c65e8f711fd9abe762 100644 (file)
-/*******************************************************************************\r
- * Copyright (c) 2007, 2010 Association for Decentralized Information Management\r
- * in Industry THTH ry.\r
- * All rights reserved. This program and the accompanying materials\r
- * are made available under the terms of the Eclipse Public License v1.0\r
- * which accompanies this distribution, and is available at\r
- * http://www.eclipse.org/legal/epl-v10.html\r
- *\r
- * Contributors:\r
- *     VTT Technical Research Centre of Finland - initial API and implementation\r
- *******************************************************************************/\r
-package org.simantics.spreadsheet.ui;\r
-\r
-import java.awt.Color;\r
-import java.awt.event.ActionEvent;\r
-import java.awt.event.ActionListener;\r
-import java.awt.event.ComponentEvent;\r
-import java.awt.event.ComponentListener;\r
-import java.awt.event.ContainerEvent;\r
-import java.awt.event.ContainerListener;\r
-import java.awt.event.FocusEvent;\r
-import java.awt.event.FocusListener;\r
-import java.awt.event.HierarchyBoundsListener;\r
-import java.awt.event.HierarchyEvent;\r
-import java.awt.event.HierarchyListener;\r
-import java.awt.event.InputMethodEvent;\r
-import java.awt.event.InputMethodListener;\r
-import java.awt.event.KeyEvent;\r
-import java.awt.event.KeyListener;\r
-import java.awt.event.MouseEvent;\r
-import java.awt.event.MouseListener;\r
-import java.awt.event.MouseMotionListener;\r
-import java.awt.event.MouseWheelEvent;\r
-import java.awt.event.MouseWheelListener;\r
-import java.beans.PropertyChangeEvent;\r
-import java.beans.PropertyChangeListener;\r
-import java.beans.PropertyVetoException;\r
-import java.beans.VetoableChangeListener;\r
-\r
-import javax.swing.JTextField;\r
-import javax.swing.event.AncestorEvent;\r
-import javax.swing.event.AncestorListener;\r
-import javax.swing.event.CaretEvent;\r
-import javax.swing.event.CaretListener;\r
-\r
-import org.eclipse.core.runtime.Assert;\r
-import org.eclipse.core.runtime.ListenerList;\r
-import org.eclipse.jface.dialogs.IInputValidator;\r
-\r
-/**\r
- * This is a TrackedTest SWT Text-widget 'decorator'.\r
- * \r
- * The widget has 2 main states: editing and inactive.\r
- * \r
- * It implements the necessary listeners to achieve the text widget behaviour\r
- * needed by Simantics. User notification about modifications is provided via\r
- * {@link TrackedModifyListener}.\r
- * \r
- * Examples:\r
- * \r
- * <pre>\r
- * // #1: create new Text internally, use TrackedModifylistener\r
- * TrackedText trackedText = new TrackedText(parentComposite, style); \r
- * trackedText.addModifyListener(new TrackedModifyListener() {\r
- *     public void modifyText(TrackedModifyEvent e) {\r
- *         // text was modified, do something.\r
- *     }\r
- * });\r
- * \r
- * // #2: create new Text internally, define the colors for text states.\r
- * TrackedText trackedText = new TrackedText(text, &lt;instance of ITrackedColorProvider&gt;); \r
- * </pre>\r
- * \r
- * @author Tuukka Lehtonen\r
- */\r
-public class TrackedText {\r
-    \r
-    private static final boolean  EVENT_DEBUG = false;\r
-    \r
-    private static final int      EDITING                 = 1 << 0;\r
-    private static final int      MODIFIED_DURING_EDITING = 1 << 1;\r
-\r
-    /**\r
-     * Used to tell whether or not a mouseDown has occured after a focusGained\r
-     * event to be able to select the whole text field when it is pressed for\r
-     * the first time while the widget holds focus.\r
-     */\r
-    private static final int      MOUSE_DOWN_FIRST_TIME   = 1 << 2;\r
-    private static final int      MOUSE_INSIDE_CONTROL    = 1 << 3;\r
-\r
-    private int                   state;\r
-\r
-    private int                   caretPositionBeforeEdit;\r
-\r
-    private String                textBeforeEdit;\r
-\r
-    private JTextField            text;\r
-\r
-    private CompositeListener     listener;\r
-\r
-    private ListenerList          modifyListeners;\r
-\r
-    private IInputValidator       validator;\r
-\r
-    private ITrackedColorProvider colorProvider;\r
-\r
-    private class DefaultColorProvider implements ITrackedColorProvider {\r
-        \r
-        private Color editingColor = new Color(255, 255, 255);\r
-        \r
-//        private Color highlightColor = new Color(text.getDisplay(), 254, 255, 197);\r
-//        private Color inactiveColor = new Color(text.getDisplay(), 245, 246, 190);\r
-//        private Color invalidInputColor = new Color(text.getDisplay(), 255, 128, 128);\r
-\r
-        @Override\r
-        public Color getEditingBackground() {\r
-            return editingColor;\r
-        }\r
-\r
-        @Override\r
-        public Color getHoverBackground() {\r
-            return null;\r
-//            return highlightColor;\r
-        }\r
-\r
-        @Override\r
-        public Color getInactiveBackground() {\r
-            return null;\r
-//            return inactiveColor;\r
-        }\r
-\r
-        @Override\r
-        public Color getInvalidBackground() {\r
-            return null;\r
-//            return invalidInputColor;\r
-        }\r
-        \r
-        void dispose() {\r
-//            highlightColor.dispose();\r
-//            inactiveColor.dispose();\r
-//            invalidInputColor.dispose();\r
-        }\r
-    };\r
-    \r
-//    /**\r
-//     * A composite of many UI listeners for creating the functionality of this\r
-//     * class.\r
-//     */\r
-//    private class CompositeListener\r
-//    implements ModifyListener, DisposeListener, KeyListener, MouseTrackListener,\r
-//            MouseListener, FocusListener\r
-//    {\r
-//        // Keyboard/editing events come in the following order:\r
-//        //   1. keyPressed\r
-//        //   2. verifyText\r
-//        //   3. modifyText\r
-//        //   4. keyReleased\r
-//        \r
-//        public void modifyText(ModifyEvent e) {\r
-//            //System.out.println("modifyText: " + e);\r
-//            setModified(true);\r
-//            \r
-////            String valid = isTextValid();\r
-////            if (valid != null) {\r
-////                setBackground(colorProvider.getInvalidBackground());\r
-////            } else {\r
-////                if (isEditing())\r
-////                    setBackground(colorProvider.getEditingBackground());\r
-////                else\r
-////                    setBackground(colorProvider.getInactiveBackground());\r
-////            }\r
-//        }\r
-//\r
-//        public void widgetDisposed(DisposeEvent e) {\r
-//            getWidget().removeModifyListener(this);\r
-//        }\r
-//        \r
-//        private boolean isMultiLine() {\r
-//            return false;\r
-////            return (text.getStyle() & SWT.MULTI) != 0;\r
-//        }\r
-//        \r
-//        private boolean hasMultiLineCommitModifier(KeyEvent e) {\r
-//            return (e.stateMask & SWT.CTRL) != 0;\r
-//        }\r
-//\r
-//        public void keyPressed(KeyEvent e) {\r
-//            //System.out.println("keyPressed: " + e);\r
-//            if (!isEditing()) {\r
-//                // ESC, ENTER & keypad ENTER must not start editing \r
-//                if (e.keyCode == SWT.ESC)\r
-//                    return;\r
-//\r
-//                if (!isMultiLine()) {\r
-//                    if (e.keyCode == SWT.F2 || e.keyCode == SWT.CR || e.keyCode == SWT.KEYPAD_CR) {\r
-//                        startEdit(true);\r
-//                    } else if (e.character != '\0') {\r
-//                        startEdit(false);\r
-//                    }\r
-//                } else {\r
-//                    // In multi-line mode, TAB must not start editing!\r
-//                    if (e.keyCode == SWT.F2) {\r
-//                        startEdit(true);\r
-//                    } else if (e.keyCode == SWT.CR || e.keyCode == SWT.KEYPAD_CR) {\r
-//                        if (hasMultiLineCommitModifier(e)) {\r
-//                            e.doit = false;\r
-//                        } else {\r
-//                            startEdit(false);\r
-//                        }\r
-//                    } else if (e.keyCode == SWT.TAB) {\r
-//                        text.traverse(((e.stateMask & SWT.SHIFT) != 0) ? SWT.TRAVERSE_TAB_PREVIOUS : SWT.TRAVERSE_TAB_NEXT);\r
-//                        e.doit = false;\r
-//                    } else if (e.character != '\0') {\r
-//                        startEdit(false);\r
-//                    }\r
-//                }\r
-//            } else {\r
-//                // ESC reverts any changes made during this edit\r
-//                if (e.keyCode == SWT.ESC) {\r
-//                    revertEdit();\r
-//                }\r
-//                if (!isMultiLine()) {\r
-//                    if (e.keyCode == SWT.CR || e.keyCode == SWT.KEYPAD_CR) {\r
-//                        applyEdit();\r
-//                    }\r
-//                } else {\r
-//                    if (e.keyCode == SWT.CR || e.keyCode == SWT.KEYPAD_CR) {\r
-//                        if (hasMultiLineCommitModifier(e)) {\r
-//                            applyEdit();\r
-//                            e.doit = false;\r
-//                        }\r
-//                    }\r
-//                }\r
-//            }\r
-//        }\r
-//\r
-//        public void keyReleased(KeyEvent e) {\r
-//            //System.out.println("keyReleased: " + e);\r
-//        }\r
-//\r
-//        public void mouseEnter(MouseEvent e) {\r
-//            //System.out.println("mouseEnter");\r
-//            if (!isEditing()) {\r
-//                setBackground(colorProvider.getHoverBackground());\r
-//            }\r
-//            setMouseInsideControl(true);\r
-//        }\r
-//\r
-//        public void mouseExit(MouseEvent e) {\r
-//            //System.out.println("mouseExit");\r
-//            if (!isEditing()) {\r
-//                setBackground(colorProvider.getInactiveBackground());\r
-//            }\r
-//            setMouseInsideControl(false);\r
-//        }\r
-//\r
-//        public void mouseHover(MouseEvent e) {\r
-//            //System.out.println("mouseHover");\r
-//            setMouseInsideControl(true);\r
-//        }\r
-//\r
-//        public void mouseDoubleClick(MouseEvent e) {\r
-//            //System.out.println("mouseDoubleClick: " + e);\r
-//            if (e.button == 1) {\r
-//                getWidget().selectAll();\r
-//            }\r
-//        }\r
-//\r
-//        public void mouseDown(MouseEvent e) {\r
-//            //System.out.println("mouseDown: " + e);\r
-//            if (!isEditing()) {\r
-//                // In reality we should never get here, since focusGained\r
-//                // always comes before mouseDown, but let's keep this\r
-//                // fallback just to be safe.\r
-//                if (e.button == 1) {\r
-//                    startEdit(true);\r
-//                }\r
-//            } else {\r
-//                if (e.button == 1 && (state & MOUSE_DOWN_FIRST_TIME) != 0) {\r
-//                    getWidget().selectAll();\r
-//                    state &= ~MOUSE_DOWN_FIRST_TIME;\r
-//                }\r
-//            }\r
-//        }\r
-//\r
-//        public void mouseUp(MouseEvent e) {\r
-//        }\r
-//\r
-//        public void focusGained(FocusEvent e) {\r
-//            //System.out.println("focusGained");\r
-//            if (!isEditing()) {\r
-//                if (!isMultiLine()) {\r
-//                    // Always start edit on single line texts when focus is gained\r
-//                    startEdit(true);\r
-//                }\r
-//            }\r
-//        }\r
-//\r
-//        public void focusLost(FocusEvent e) {\r
-//            //System.out.println("focusLost");\r
-//            if (isEditing()) {\r
-//                applyEdit();\r
-//            }\r
-//        }\r
-//    }\r
-    \r
-    public TrackedText(JTextField text) {\r
-        Assert.isNotNull(text);\r
-        this.state = 0;\r
-        this.text = text;\r
-        this.colorProvider = new DefaultColorProvider();\r
-\r
-        initialize();\r
-    }\r
-    \r
-    public TrackedText(JTextField text, ITrackedColorProvider colorProvider) {\r
-        Assert.isNotNull(text, "text must not be null");\r
-        Assert.isNotNull(colorProvider, "colorProvider must not be null");\r
-        this.state = 0;\r
-        this.text = text;\r
-        this.colorProvider = colorProvider;\r
-\r
-        initialize();\r
-    }\r
-\r
-    private class CompositeListener implements ActionListener, CaretListener, AncestorListener, ComponentListener, ContainerListener, FocusListener, HierarchyBoundsListener, HierarchyListener,\r
-        InputMethodListener, KeyListener, MouseListener, MouseMotionListener, MouseWheelListener, PropertyChangeListener, VetoableChangeListener {\r
-\r
-        @Override\r
-        public void actionPerformed(ActionEvent arg0) {\r
-            if(EVENT_DEBUG) System.out.println("actionPerformed " + arg0);\r
-        }\r
-\r
-        @Override\r
-        public void caretUpdate(CaretEvent arg0) {\r
-            if(EVENT_DEBUG) System.out.println("caretUpdate " + arg0);\r
-        }\r
-\r
-        @Override\r
-        public void ancestorAdded(AncestorEvent arg0) {\r
-            if(EVENT_DEBUG) System.out.println("ancestorAdded " + arg0);\r
-        }\r
-\r
-        @Override\r
-        public void ancestorMoved(AncestorEvent arg0) {\r
-            if(EVENT_DEBUG) System.out.println("ancestorMoved " + arg0);\r
-        }\r
-\r
-        @Override\r
-        public void ancestorRemoved(AncestorEvent arg0) {\r
-            if(EVENT_DEBUG) System.out.println("ancestorRemoved " + arg0);\r
-        }\r
-\r
-        @Override\r
-        public void componentHidden(ComponentEvent arg0) {\r
-            if(EVENT_DEBUG) System.out.println("componentHidden " + arg0);\r
-        }\r
-\r
-        @Override\r
-        public void componentMoved(ComponentEvent arg0) {\r
-            if(EVENT_DEBUG) System.out.println("componentMoved " + arg0);\r
-        }\r
-\r
-        @Override\r
-        public void componentResized(ComponentEvent arg0) {\r
-            if(EVENT_DEBUG) System.out.println("componentResized " + arg0);\r
-        }\r
-\r
-        @Override\r
-        public void componentShown(ComponentEvent arg0) {\r
-            if(EVENT_DEBUG) System.out.println("componentShown " + arg0);\r
-        }\r
-\r
-        @Override\r
-        public void componentAdded(ContainerEvent arg0) {\r
-            if(EVENT_DEBUG) System.out.println("componentAdded " + arg0);\r
-        }\r
-\r
-        @Override\r
-        public void componentRemoved(ContainerEvent arg0) {\r
-            if(EVENT_DEBUG) System.out.println("componentRemoved " + arg0);\r
-        }\r
-\r
-        @Override\r
-        public void focusGained(FocusEvent arg0) {\r
-            if(EVENT_DEBUG) System.out.println("focusGained " + arg0);\r
-            if(!isEditing())\r
-                startEdit(false);\r
-        }\r
-\r
-        @Override\r
-        public void focusLost(FocusEvent arg0) {\r
-            if(EVENT_DEBUG) System.out.println("focusLost " + arg0);\r
-        }\r
-\r
-        @Override\r
-        public void ancestorMoved(HierarchyEvent arg0) {\r
-            if(EVENT_DEBUG) System.out.println("ancestorMoved " + arg0);\r
-        }\r
-\r
-        @Override\r
-        public void ancestorResized(HierarchyEvent arg0) {\r
-            if(EVENT_DEBUG) System.out.println("ancestorResized " + arg0);\r
-        }\r
-\r
-        @Override\r
-        public void hierarchyChanged(HierarchyEvent arg0) {\r
-            if(EVENT_DEBUG) System.out.println("hierarchyChanged " + arg0);\r
-        }\r
-\r
-        @Override\r
-        public void caretPositionChanged(InputMethodEvent arg0) {\r
-            if(EVENT_DEBUG) System.out.println("caretPositionChanged " + arg0);\r
-        }\r
-\r
-        @Override\r
-        public void inputMethodTextChanged(InputMethodEvent arg0) {\r
-            if(EVENT_DEBUG) System.out.println("inputMethodTextChanged " + arg0);\r
-        }\r
-\r
-        @Override\r
-        public void keyPressed(KeyEvent arg0) {\r
-            if(EVENT_DEBUG) System.out.println("keyPressed " + arg0);\r
-            if(arg0.getKeyCode() == KeyEvent.VK_ESCAPE) {\r
-                revertEdit();\r
-            }\r
-            if(arg0.getKeyCode() == KeyEvent.VK_ENTER) {\r
-                applyEdit();\r
-            }\r
-        }\r
-\r
-        @Override\r
-        public void keyReleased(KeyEvent arg0) {\r
-            if(EVENT_DEBUG) System.out.println("keyReleased " + arg0);\r
-            setModified(true);\r
-            if(!isEditing())\r
-                startEdit(false);\r
-        }\r
-\r
-        @Override\r
-        public void keyTyped(KeyEvent arg0) {\r
-            if(EVENT_DEBUG) System.out.println("keyTyped " + arg0);\r
-        }\r
-\r
-        @Override\r
-        public void mouseClicked(MouseEvent arg0) {\r
-            if(EVENT_DEBUG) System.out.println("mouseClicked " + arg0);\r
-        }\r
-\r
-        @Override\r
-        public void mouseEntered(MouseEvent arg0) {\r
-            if(EVENT_DEBUG) System.out.println("mouseEntered " + arg0);\r
-        }\r
-\r
-        @Override\r
-        public void mouseExited(MouseEvent arg0) {\r
-            if(EVENT_DEBUG) System.out.println("mouseExited " + arg0);\r
-        }\r
-\r
-        @Override\r
-        public void mousePressed(MouseEvent arg0) {\r
-            if(EVENT_DEBUG) System.out.println("mousePressed " + arg0);\r
-        }\r
-\r
-        @Override\r
-        public void mouseReleased(MouseEvent arg0) {\r
-            if(EVENT_DEBUG) System.out.println("mouseReleased " + arg0);\r
-        }\r
-\r
-        @Override\r
-        public void mouseDragged(MouseEvent arg0) {\r
-            if(EVENT_DEBUG) System.out.println("mouseDragged " + arg0);\r
-        }\r
-\r
-        @Override\r
-        public void mouseMoved(MouseEvent arg0) {\r
-            if(EVENT_DEBUG) System.out.println("mouseMoved " + arg0);\r
-        }\r
-\r
-        @Override\r
-        public void mouseWheelMoved(MouseWheelEvent arg0) {\r
-            if(EVENT_DEBUG) System.out.println("mouseWheelMoved " + arg0);\r
-        }\r
-\r
-        @Override\r
-        public void propertyChange(PropertyChangeEvent arg0) {\r
-            if(EVENT_DEBUG) System.out.println("propertyChange " + arg0);\r
-        }\r
-\r
-        @Override\r
-        public void vetoableChange(PropertyChangeEvent arg0) throws PropertyVetoException {\r
-            if(EVENT_DEBUG) System.out.println("vetoableChange " + arg0);\r
-        }\r
-        \r
-    };\r
-    \r
-    /**\r
-     * Common initialization. Assumes that text is already created.\r
-     */\r
-    private void initialize() {\r
-        Assert.isNotNull(text);\r
-        \r
-//        text.setBackground(colorProvider.getInactiveBackground());\r
-//        text.setDoubleClickEnabled(false);\r
-        \r
-        listener = new CompositeListener();\r
-\r
-        text.addActionListener(listener);\r
-        text.addCaretListener(listener);\r
-        text.addAncestorListener(listener);\r
-        text.addComponentListener(listener);\r
-        text.addContainerListener(listener);\r
-        text.addFocusListener(listener);\r
-        text.addHierarchyBoundsListener(listener);\r
-        text.addHierarchyListener(listener);\r
-        text.addInputMethodListener(listener);\r
-        text.addKeyListener(listener);\r
-        text.addMouseListener(listener);\r
-        text.addMouseMotionListener(listener);\r
-        text.addMouseWheelListener(listener);\r
-        text.addPropertyChangeListener(listener);\r
-        text.addVetoableChangeListener(listener);\r
-        \r
-    }\r
-    \r
-    private void startEdit(boolean selectAll) {\r
-        if (isEditing()) {\r
-            // Print some debug incase we end are in an invalid state\r
-            System.out.println("TrackedText: BUG: startEdit called when in editing state");\r
-        }\r
-        //System.out.println("start edit: selectall=" + selectAll + ", text=" + text.getText() + ", caretpos=" + caretPositionBeforeEdit);\r
-\r
-        // Backup text-field data for reverting purposes\r
-        caretPositionBeforeEdit = text.getCaretPosition();\r
-        textBeforeEdit = text.getText();\r
-\r
-        // Signal editing state\r
-        setBackground(colorProvider.getEditingBackground());\r
-        \r
-        if (selectAll) {\r
-            text.selectAll();\r
-        }\r
-        state |= EDITING | MOUSE_DOWN_FIRST_TIME;\r
-    }\r
-\r
-    private void applyEdit() {\r
-        try {\r
-            if (isTextValid() != null) {\r
-                text.setText(textBeforeEdit);\r
-            } else if (isModified() && !text.getText().equals(textBeforeEdit)) {\r
-                //System.out.println("apply");\r
-                if (modifyListeners != null) {\r
-                    TrackedModifyEvent event = new TrackedModifyEvent(text, text.getText());\r
-                    for (Object o : modifyListeners.getListeners()) {\r
-                        ((TrackedModifyListener) o).modifyText(event);\r
-                    }\r
-                }\r
-            }\r
-        } finally {\r
-            endEdit();\r
-        }\r
-    }\r
-    \r
-    private void endEdit() {\r
-        if (!isEditing()) {\r
-            // Print some debug incase we end are in an invalid state\r
-            //ExceptionUtils.logError(new Exception("BUG: endEdit called when not in editing state"));\r
-        }\r
-        setBackground(isMouseInsideControl() ? colorProvider.getHoverBackground() : colorProvider.getInactiveBackground());\r
-        //System.out.println("endEdit: " + text.getText() + ", caret: " + text.getCaretLocation() + ", selection: " + text.getSelection());\r
-        // Always move the caret to the end of the string\r
-        text.setCaretPosition(text.getText().length());\r
-        state &= ~(EDITING | MOUSE_DOWN_FIRST_TIME);\r
-        setModified(false);\r
-    }\r
-\r
-    private void revertEdit() {\r
-        if (!isEditing()) {\r
-            // Print some debug incase we end are in an invalid state\r
-            //ExceptionUtils.logError(new Exception("BUG: revertEdit called when not in editing state"));\r
-            System.out.println("BUG: revertEdit called when not in editing state");\r
-        }\r
-        text.setText(textBeforeEdit);\r
-        text.setCaretPosition(caretPositionBeforeEdit);\r
-        setBackground(isMouseInsideControl() ? colorProvider.getHoverBackground() : colorProvider.getInactiveBackground());\r
-        state &= ~(EDITING | MOUSE_DOWN_FIRST_TIME);\r
-        setModified(false);\r
-    }\r
-    \r
-    private boolean isEditing() {\r
-        return (state & EDITING) != 0;\r
-    }\r
-    \r
-    private void setModified(boolean modified) {\r
-        if (modified) {\r
-            state |= MODIFIED_DURING_EDITING;\r
-        } else {\r
-            state &= ~MODIFIED_DURING_EDITING;\r
-        }\r
-    }\r
-    \r
-    private boolean isMouseInsideControl() {\r
-        return (state & MOUSE_INSIDE_CONTROL) != 0;\r
-    }\r
-    \r
-    private void setMouseInsideControl(boolean inside) {\r
-        if (inside)\r
-            state |= MOUSE_INSIDE_CONTROL;\r
-        else\r
-            state &= ~MOUSE_INSIDE_CONTROL;\r
-    }\r
-    \r
-    private boolean isModified() {\r
-        return (state & MODIFIED_DURING_EDITING) != 0;\r
-    }\r
-    \r
-    public void setEditable(boolean editable) {\r
-        if (editable) {\r
-            text.setEditable(true);\r
-            setBackground(isMouseInsideControl() ? colorProvider.getHoverBackground() : colorProvider.getInactiveBackground());\r
-        } else {\r
-            text.setEditable(false);\r
-            text.setBackground(null);\r
-        }\r
-    }\r
-    \r
-    public void setText(String text) {\r
-        this.text.setText(text);\r
-    }\r
-    \r
-    public void setTextWithoutNotify(String text) {\r
-//        this.text.removeModifyListener(listener);\r
-        setText(text);\r
-//        this.text.addModifyListener(listener);\r
-    }\r
-\r
-    public JTextField getWidget() {\r
-        return text;\r
-    }\r
-    \r
-    public synchronized void addModifyListener(TrackedModifyListener listener) {\r
-        if (modifyListeners == null) {\r
-            modifyListeners = new ListenerList(ListenerList.IDENTITY);\r
-        }\r
-        modifyListeners.add(listener);\r
-    }\r
-    \r
-    public synchronized void removeModifyListener(TrackedModifyListener listener) {\r
-        if (modifyListeners == null)\r
-            return;\r
-        modifyListeners.remove(listener);\r
-    }\r
-    \r
-    public void setInputValidator(IInputValidator validator) {\r
-        if (validator != this.validator) {\r
-            this.validator = validator;\r
-        }\r
-    }\r
-    \r
-    private String isTextValid() {\r
-        if (validator != null) {\r
-            return validator.isValid(getWidget().getText());\r
-        }\r
-        return null;\r
-    }\r
-    \r
-    public void setColorProvider(ITrackedColorProvider provider) {\r
-        Assert.isNotNull(provider);\r
-        this.colorProvider = provider;\r
-    }\r
-    \r
-    private void setBackground(Color background) {\r
-        if (!text.isEditable()) {\r
-            // Do not alter background when the widget is not editable.\r
-            return;\r
-        }\r
-        text.setBackground(background);\r
-    }\r
-    \r
-}\r
+/*******************************************************************************
+ * 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.spreadsheet.ui;
+
+import java.awt.Color;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.ComponentEvent;
+import java.awt.event.ComponentListener;
+import java.awt.event.ContainerEvent;
+import java.awt.event.ContainerListener;
+import java.awt.event.FocusEvent;
+import java.awt.event.FocusListener;
+import java.awt.event.HierarchyBoundsListener;
+import java.awt.event.HierarchyEvent;
+import java.awt.event.HierarchyListener;
+import java.awt.event.InputMethodEvent;
+import java.awt.event.InputMethodListener;
+import java.awt.event.KeyEvent;
+import java.awt.event.KeyListener;
+import java.awt.event.MouseEvent;
+import java.awt.event.MouseListener;
+import java.awt.event.MouseMotionListener;
+import java.awt.event.MouseWheelEvent;
+import java.awt.event.MouseWheelListener;
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+import java.beans.PropertyVetoException;
+import java.beans.VetoableChangeListener;
+
+import javax.swing.JTextField;
+import javax.swing.event.AncestorEvent;
+import javax.swing.event.AncestorListener;
+import javax.swing.event.CaretEvent;
+import javax.swing.event.CaretListener;
+
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.core.runtime.ListenerList;
+import org.eclipse.jface.dialogs.IInputValidator;
+
+/**
+ * This is a TrackedTest SWT Text-widget 'decorator'.
+ * 
+ * The widget has 2 main states: editing and inactive.
+ * 
+ * It implements the necessary listeners to achieve the text widget behaviour
+ * needed by Simantics. User notification about modifications is provided via
+ * {@link TrackedModifyListener}.
+ * 
+ * Examples:
+ * 
+ * <pre>
+ * // #1: create new Text internally, use TrackedModifylistener
+ * TrackedText trackedText = new TrackedText(parentComposite, style); 
+ * trackedText.addModifyListener(new TrackedModifyListener() {
+ *     public void modifyText(TrackedModifyEvent e) {
+ *         // text was modified, do something.
+ *     }
+ * });
+ * 
+ * // #2: create new Text internally, define the colors for text states.
+ * TrackedText trackedText = new TrackedText(text, &lt;instance of ITrackedColorProvider&gt;); 
+ * </pre>
+ * 
+ * @author Tuukka Lehtonen
+ */
+public class TrackedText {
+    
+    private static final boolean  EVENT_DEBUG = false;
+    
+    private static final int      EDITING                 = 1 << 0;
+    private static final int      MODIFIED_DURING_EDITING = 1 << 1;
+
+    /**
+     * Used to tell whether or not a mouseDown has occured after a focusGained
+     * event to be able to select the whole text field when it is pressed for
+     * the first time while the widget holds focus.
+     */
+    private static final int      MOUSE_DOWN_FIRST_TIME   = 1 << 2;
+    private static final int      MOUSE_INSIDE_CONTROL    = 1 << 3;
+
+    private int                   state;
+
+    private int                   caretPositionBeforeEdit;
+
+    private String                textBeforeEdit;
+
+    private JTextField            text;
+
+    private CompositeListener     listener;
+
+    private ListenerList          modifyListeners;
+
+    private IInputValidator       validator;
+
+    private ITrackedColorProvider colorProvider;
+
+    private class DefaultColorProvider implements ITrackedColorProvider {
+        
+        private Color editingColor = new Color(255, 255, 255);
+        
+//        private Color highlightColor = new Color(text.getDisplay(), 254, 255, 197);
+//        private Color inactiveColor = new Color(text.getDisplay(), 245, 246, 190);
+//        private Color invalidInputColor = new Color(text.getDisplay(), 255, 128, 128);
+
+        @Override
+        public Color getEditingBackground() {
+            return editingColor;
+        }
+
+        @Override
+        public Color getHoverBackground() {
+            return null;
+//            return highlightColor;
+        }
+
+        @Override
+        public Color getInactiveBackground() {
+            return null;
+//            return inactiveColor;
+        }
+
+        @Override
+        public Color getInvalidBackground() {
+            return null;
+//            return invalidInputColor;
+        }
+        
+        void dispose() {
+//            highlightColor.dispose();
+//            inactiveColor.dispose();
+//            invalidInputColor.dispose();
+        }
+    };
+    
+//    /**
+//     * A composite of many UI listeners for creating the functionality of this
+//     * class.
+//     */
+//    private class CompositeListener
+//    implements ModifyListener, DisposeListener, KeyListener, MouseTrackListener,
+//            MouseListener, FocusListener
+//    {
+//        // Keyboard/editing events come in the following order:
+//        //   1. keyPressed
+//        //   2. verifyText
+//        //   3. modifyText
+//        //   4. keyReleased
+//        
+//        public void modifyText(ModifyEvent e) {
+//            //System.out.println("modifyText: " + e);
+//            setModified(true);
+//            
+////            String valid = isTextValid();
+////            if (valid != null) {
+////                setBackground(colorProvider.getInvalidBackground());
+////            } else {
+////                if (isEditing())
+////                    setBackground(colorProvider.getEditingBackground());
+////                else
+////                    setBackground(colorProvider.getInactiveBackground());
+////            }
+//        }
+//
+//        public void widgetDisposed(DisposeEvent e) {
+//            getWidget().removeModifyListener(this);
+//        }
+//        
+//        private boolean isMultiLine() {
+//            return false;
+////            return (text.getStyle() & SWT.MULTI) != 0;
+//        }
+//        
+//        private boolean hasMultiLineCommitModifier(KeyEvent e) {
+//            return (e.stateMask & SWT.CTRL) != 0;
+//        }
+//
+//        public void keyPressed(KeyEvent e) {
+//            //System.out.println("keyPressed: " + e);
+//            if (!isEditing()) {
+//                // ESC, ENTER & keypad ENTER must not start editing 
+//                if (e.keyCode == SWT.ESC)
+//                    return;
+//
+//                if (!isMultiLine()) {
+//                    if (e.keyCode == SWT.F2 || e.keyCode == SWT.CR || e.keyCode == SWT.KEYPAD_CR) {
+//                        startEdit(true);
+//                    } else if (e.character != '\0') {
+//                        startEdit(false);
+//                    }
+//                } else {
+//                    // In multi-line mode, TAB must not start editing!
+//                    if (e.keyCode == SWT.F2) {
+//                        startEdit(true);
+//                    } else if (e.keyCode == SWT.CR || e.keyCode == SWT.KEYPAD_CR) {
+//                        if (hasMultiLineCommitModifier(e)) {
+//                            e.doit = false;
+//                        } else {
+//                            startEdit(false);
+//                        }
+//                    } else if (e.keyCode == SWT.TAB) {
+//                        text.traverse(((e.stateMask & SWT.SHIFT) != 0) ? SWT.TRAVERSE_TAB_PREVIOUS : SWT.TRAVERSE_TAB_NEXT);
+//                        e.doit = false;
+//                    } else if (e.character != '\0') {
+//                        startEdit(false);
+//                    }
+//                }
+//            } else {
+//                // ESC reverts any changes made during this edit
+//                if (e.keyCode == SWT.ESC) {
+//                    revertEdit();
+//                }
+//                if (!isMultiLine()) {
+//                    if (e.keyCode == SWT.CR || e.keyCode == SWT.KEYPAD_CR) {
+//                        applyEdit();
+//                    }
+//                } else {
+//                    if (e.keyCode == SWT.CR || e.keyCode == SWT.KEYPAD_CR) {
+//                        if (hasMultiLineCommitModifier(e)) {
+//                            applyEdit();
+//                            e.doit = false;
+//                        }
+//                    }
+//                }
+//            }
+//        }
+//
+//        public void keyReleased(KeyEvent e) {
+//            //System.out.println("keyReleased: " + e);
+//        }
+//
+//        public void mouseEnter(MouseEvent e) {
+//            //System.out.println("mouseEnter");
+//            if (!isEditing()) {
+//                setBackground(colorProvider.getHoverBackground());
+//            }
+//            setMouseInsideControl(true);
+//        }
+//
+//        public void mouseExit(MouseEvent e) {
+//            //System.out.println("mouseExit");
+//            if (!isEditing()) {
+//                setBackground(colorProvider.getInactiveBackground());
+//            }
+//            setMouseInsideControl(false);
+//        }
+//
+//        public void mouseHover(MouseEvent e) {
+//            //System.out.println("mouseHover");
+//            setMouseInsideControl(true);
+//        }
+//
+//        public void mouseDoubleClick(MouseEvent e) {
+//            //System.out.println("mouseDoubleClick: " + e);
+//            if (e.button == 1) {
+//                getWidget().selectAll();
+//            }
+//        }
+//
+//        public void mouseDown(MouseEvent e) {
+//            //System.out.println("mouseDown: " + e);
+//            if (!isEditing()) {
+//                // In reality we should never get here, since focusGained
+//                // always comes before mouseDown, but let's keep this
+//                // fallback just to be safe.
+//                if (e.button == 1) {
+//                    startEdit(true);
+//                }
+//            } else {
+//                if (e.button == 1 && (state & MOUSE_DOWN_FIRST_TIME) != 0) {
+//                    getWidget().selectAll();
+//                    state &= ~MOUSE_DOWN_FIRST_TIME;
+//                }
+//            }
+//        }
+//
+//        public void mouseUp(MouseEvent e) {
+//        }
+//
+//        public void focusGained(FocusEvent e) {
+//            //System.out.println("focusGained");
+//            if (!isEditing()) {
+//                if (!isMultiLine()) {
+//                    // Always start edit on single line texts when focus is gained
+//                    startEdit(true);
+//                }
+//            }
+//        }
+//
+//        public void focusLost(FocusEvent e) {
+//            //System.out.println("focusLost");
+//            if (isEditing()) {
+//                applyEdit();
+//            }
+//        }
+//    }
+    
+    public TrackedText(JTextField text) {
+        Assert.isNotNull(text);
+        this.state = 0;
+        this.text = text;
+        this.colorProvider = new DefaultColorProvider();
+
+        initialize();
+    }
+    
+    public TrackedText(JTextField text, ITrackedColorProvider colorProvider) {
+        Assert.isNotNull(text, "text must not be null");
+        Assert.isNotNull(colorProvider, "colorProvider must not be null");
+        this.state = 0;
+        this.text = text;
+        this.colorProvider = colorProvider;
+
+        initialize();
+    }
+
+    private class CompositeListener implements ActionListener, CaretListener, AncestorListener, ComponentListener, ContainerListener, FocusListener, HierarchyBoundsListener, HierarchyListener,
+        InputMethodListener, KeyListener, MouseListener, MouseMotionListener, MouseWheelListener, PropertyChangeListener, VetoableChangeListener {
+
+        @Override
+        public void actionPerformed(ActionEvent arg0) {
+            if(EVENT_DEBUG) System.out.println("actionPerformed " + arg0);
+        }
+
+        @Override
+        public void caretUpdate(CaretEvent arg0) {
+            if(EVENT_DEBUG) System.out.println("caretUpdate " + arg0);
+        }
+
+        @Override
+        public void ancestorAdded(AncestorEvent arg0) {
+            if(EVENT_DEBUG) System.out.println("ancestorAdded " + arg0);
+        }
+
+        @Override
+        public void ancestorMoved(AncestorEvent arg0) {
+            if(EVENT_DEBUG) System.out.println("ancestorMoved " + arg0);
+        }
+
+        @Override
+        public void ancestorRemoved(AncestorEvent arg0) {
+            if(EVENT_DEBUG) System.out.println("ancestorRemoved " + arg0);
+        }
+
+        @Override
+        public void componentHidden(ComponentEvent arg0) {
+            if(EVENT_DEBUG) System.out.println("componentHidden " + arg0);
+        }
+
+        @Override
+        public void componentMoved(ComponentEvent arg0) {
+            if(EVENT_DEBUG) System.out.println("componentMoved " + arg0);
+        }
+
+        @Override
+        public void componentResized(ComponentEvent arg0) {
+            if(EVENT_DEBUG) System.out.println("componentResized " + arg0);
+        }
+
+        @Override
+        public void componentShown(ComponentEvent arg0) {
+            if(EVENT_DEBUG) System.out.println("componentShown " + arg0);
+        }
+
+        @Override
+        public void componentAdded(ContainerEvent arg0) {
+            if(EVENT_DEBUG) System.out.println("componentAdded " + arg0);
+        }
+
+        @Override
+        public void componentRemoved(ContainerEvent arg0) {
+            if(EVENT_DEBUG) System.out.println("componentRemoved " + arg0);
+        }
+
+        @Override
+        public void focusGained(FocusEvent arg0) {
+            if(EVENT_DEBUG) System.out.println("focusGained " + arg0);
+            if(!isEditing())
+                startEdit(false);
+        }
+
+        @Override
+        public void focusLost(FocusEvent arg0) {
+            if(EVENT_DEBUG) System.out.println("focusLost " + arg0);
+        }
+
+        @Override
+        public void ancestorMoved(HierarchyEvent arg0) {
+            if(EVENT_DEBUG) System.out.println("ancestorMoved " + arg0);
+        }
+
+        @Override
+        public void ancestorResized(HierarchyEvent arg0) {
+            if(EVENT_DEBUG) System.out.println("ancestorResized " + arg0);
+        }
+
+        @Override
+        public void hierarchyChanged(HierarchyEvent arg0) {
+            if(EVENT_DEBUG) System.out.println("hierarchyChanged " + arg0);
+        }
+
+        @Override
+        public void caretPositionChanged(InputMethodEvent arg0) {
+            if(EVENT_DEBUG) System.out.println("caretPositionChanged " + arg0);
+        }
+
+        @Override
+        public void inputMethodTextChanged(InputMethodEvent arg0) {
+            if(EVENT_DEBUG) System.out.println("inputMethodTextChanged " + arg0);
+        }
+
+        @Override
+        public void keyPressed(KeyEvent arg0) {
+            if(EVENT_DEBUG) System.out.println("keyPressed " + arg0);
+            if(arg0.getKeyCode() == KeyEvent.VK_ESCAPE) {
+                revertEdit();
+            }
+            if(arg0.getKeyCode() == KeyEvent.VK_ENTER) {
+                applyEdit();
+            }
+        }
+
+        @Override
+        public void keyReleased(KeyEvent arg0) {
+            if(EVENT_DEBUG) System.out.println("keyReleased " + arg0);
+            setModified(true);
+            if(!isEditing())
+                startEdit(false);
+        }
+
+        @Override
+        public void keyTyped(KeyEvent arg0) {
+            if(EVENT_DEBUG) System.out.println("keyTyped " + arg0);
+        }
+
+        @Override
+        public void mouseClicked(MouseEvent arg0) {
+            if(EVENT_DEBUG) System.out.println("mouseClicked " + arg0);
+        }
+
+        @Override
+        public void mouseEntered(MouseEvent arg0) {
+            if(EVENT_DEBUG) System.out.println("mouseEntered " + arg0);
+        }
+
+        @Override
+        public void mouseExited(MouseEvent arg0) {
+            if(EVENT_DEBUG) System.out.println("mouseExited " + arg0);
+        }
+
+        @Override
+        public void mousePressed(MouseEvent arg0) {
+            if(EVENT_DEBUG) System.out.println("mousePressed " + arg0);
+        }
+
+        @Override
+        public void mouseReleased(MouseEvent arg0) {
+            if(EVENT_DEBUG) System.out.println("mouseReleased " + arg0);
+        }
+
+        @Override
+        public void mouseDragged(MouseEvent arg0) {
+            if(EVENT_DEBUG) System.out.println("mouseDragged " + arg0);
+        }
+
+        @Override
+        public void mouseMoved(MouseEvent arg0) {
+            if(EVENT_DEBUG) System.out.println("mouseMoved " + arg0);
+        }
+
+        @Override
+        public void mouseWheelMoved(MouseWheelEvent arg0) {
+            if(EVENT_DEBUG) System.out.println("mouseWheelMoved " + arg0);
+        }
+
+        @Override
+        public void propertyChange(PropertyChangeEvent arg0) {
+            if(EVENT_DEBUG) System.out.println("propertyChange " + arg0);
+        }
+
+        @Override
+        public void vetoableChange(PropertyChangeEvent arg0) throws PropertyVetoException {
+            if(EVENT_DEBUG) System.out.println("vetoableChange " + arg0);
+        }
+        
+    };
+    
+    /**
+     * Common initialization. Assumes that text is already created.
+     */
+    private void initialize() {
+        Assert.isNotNull(text);
+        
+//        text.setBackground(colorProvider.getInactiveBackground());
+//        text.setDoubleClickEnabled(false);
+        
+        listener = new CompositeListener();
+
+        text.addActionListener(listener);
+        text.addCaretListener(listener);
+        text.addAncestorListener(listener);
+        text.addComponentListener(listener);
+        text.addContainerListener(listener);
+        text.addFocusListener(listener);
+        text.addHierarchyBoundsListener(listener);
+        text.addHierarchyListener(listener);
+        text.addInputMethodListener(listener);
+        text.addKeyListener(listener);
+        text.addMouseListener(listener);
+        text.addMouseMotionListener(listener);
+        text.addMouseWheelListener(listener);
+        text.addPropertyChangeListener(listener);
+        text.addVetoableChangeListener(listener);
+        
+    }
+    
+    private void startEdit(boolean selectAll) {
+        if (isEditing()) {
+            // Print some debug incase we end are in an invalid state
+            System.out.println("TrackedText: BUG: startEdit called when in editing state");
+        }
+        //System.out.println("start edit: selectall=" + selectAll + ", text=" + text.getText() + ", caretpos=" + caretPositionBeforeEdit);
+
+        // Backup text-field data for reverting purposes
+        caretPositionBeforeEdit = text.getCaretPosition();
+        textBeforeEdit = text.getText();
+
+        // Signal editing state
+        setBackground(colorProvider.getEditingBackground());
+        
+        if (selectAll) {
+            text.selectAll();
+        }
+        state |= EDITING | MOUSE_DOWN_FIRST_TIME;
+    }
+
+    private void applyEdit() {
+        try {
+            if (isTextValid() != null) {
+                text.setText(textBeforeEdit);
+            } else if (isModified() && !text.getText().equals(textBeforeEdit)) {
+                //System.out.println("apply");
+                if (modifyListeners != null) {
+                    TrackedModifyEvent event = new TrackedModifyEvent(text, text.getText());
+                    for (Object o : modifyListeners.getListeners()) {
+                        ((TrackedModifyListener) o).modifyText(event);
+                    }
+                }
+            }
+        } finally {
+            endEdit();
+        }
+    }
+    
+    private void endEdit() {
+        if (!isEditing()) {
+            // Print some debug incase we end are in an invalid state
+            //ExceptionUtils.logError(new Exception("BUG: endEdit called when not in editing state"));
+        }
+        setBackground(isMouseInsideControl() ? colorProvider.getHoverBackground() : colorProvider.getInactiveBackground());
+        //System.out.println("endEdit: " + text.getText() + ", caret: " + text.getCaretLocation() + ", selection: " + text.getSelection());
+        // Always move the caret to the end of the string
+        text.setCaretPosition(text.getText().length());
+        state &= ~(EDITING | MOUSE_DOWN_FIRST_TIME);
+        setModified(false);
+    }
+
+    private void revertEdit() {
+        if (!isEditing()) {
+            // Print some debug incase we end are in an invalid state
+            //ExceptionUtils.logError(new Exception("BUG: revertEdit called when not in editing state"));
+            System.out.println("BUG: revertEdit called when not in editing state");
+        }
+        text.setText(textBeforeEdit);
+        text.setCaretPosition(caretPositionBeforeEdit);
+        setBackground(isMouseInsideControl() ? colorProvider.getHoverBackground() : colorProvider.getInactiveBackground());
+        state &= ~(EDITING | MOUSE_DOWN_FIRST_TIME);
+        setModified(false);
+    }
+    
+    private boolean isEditing() {
+        return (state & EDITING) != 0;
+    }
+    
+    private void setModified(boolean modified) {
+        if (modified) {
+            state |= MODIFIED_DURING_EDITING;
+        } else {
+            state &= ~MODIFIED_DURING_EDITING;
+        }
+    }
+    
+    private boolean isMouseInsideControl() {
+        return (state & MOUSE_INSIDE_CONTROL) != 0;
+    }
+    
+    private void setMouseInsideControl(boolean inside) {
+        if (inside)
+            state |= MOUSE_INSIDE_CONTROL;
+        else
+            state &= ~MOUSE_INSIDE_CONTROL;
+    }
+    
+    private boolean isModified() {
+        return (state & MODIFIED_DURING_EDITING) != 0;
+    }
+    
+    public void setEditable(boolean editable) {
+        if (editable) {
+            text.setEditable(true);
+            setBackground(isMouseInsideControl() ? colorProvider.getHoverBackground() : colorProvider.getInactiveBackground());
+        } else {
+            text.setEditable(false);
+            text.setBackground(null);
+        }
+    }
+    
+    public void setText(String text) {
+        this.text.setText(text);
+    }
+    
+    public void setTextWithoutNotify(String text) {
+//        this.text.removeModifyListener(listener);
+        setText(text);
+//        this.text.addModifyListener(listener);
+    }
+
+    public JTextField getWidget() {
+        return text;
+    }
+    
+    public synchronized void addModifyListener(TrackedModifyListener listener) {
+        if (modifyListeners == null) {
+            modifyListeners = new ListenerList(ListenerList.IDENTITY);
+        }
+        modifyListeners.add(listener);
+    }
+    
+    public synchronized void removeModifyListener(TrackedModifyListener listener) {
+        if (modifyListeners == null)
+            return;
+        modifyListeners.remove(listener);
+    }
+    
+    public void setInputValidator(IInputValidator validator) {
+        if (validator != this.validator) {
+            this.validator = validator;
+        }
+    }
+    
+    private String isTextValid() {
+        if (validator != null) {
+            return validator.isValid(getWidget().getText());
+        }
+        return null;
+    }
+    
+    public void setColorProvider(ITrackedColorProvider provider) {
+        Assert.isNotNull(provider);
+        this.colorProvider = provider;
+    }
+    
+    private void setBackground(Color background) {
+        if (!text.isEditable()) {
+            // Do not alter background when the widget is not editable.
+            return;
+        }
+        text.setBackground(background);
+    }
+    
+}