swingx-master/swingx-core/src/main/java/org/jdesktop/swingx/plaf/PromptTextFieldUI.java
package org.jdesktop.swingx.plaf;

import static javax.swing.BorderFactory.createEmptyBorder;

import java.awt.Insets;
import java.awt.Rectangle;

import javax.swing.JTextField;
import javax.swing.border.Border;
import javax.swing.plaf.TextUI;
import javax.swing.text.JTextComponent;

import org.jdesktop.swingx.search.NativeSearchFieldSupport;
import org.jdesktop.swingx.util.OS;

/**
 * {@link PromptTextUI} implementation for rendering prompts on
 * {@link JTextField}s and uses a {@link JTextField} as a prompt component.
 * 
 * @author Peter Weishapl <petw@gmx.net>
 * 
 */
public class PromptTextFieldUI extends PromptTextUI {
    /**
     * Creates a new {@link PromptTextFieldUI}.
     * 
     * @param delegate
     */
    public PromptTextFieldUI(TextUI delegate) {
        super(delegate);
    }

    /**
     * Overrides {@link #getPromptComponent(JTextComponent)} to additionally
     * update {@link JTextField} specific properties.
     */
    @Override
    public JTextComponent getPromptComponent(JTextComponent txt) {
        LabelField lbl = (LabelField) super.getPromptComponent(txt);
        JTextField txtField = (JTextField) txt;

        lbl.setHorizontalAlignment(txtField.getHorizontalAlignment());
        lbl.setColumns(txtField.getColumns());

        // Make search field in Leopard paint focused border.
        lbl.hasFocus = txtField.hasFocus()
                && NativeSearchFieldSupport.isNativeSearchField(txtField);

        // leopard client properties. see
        // http://developer.apple.com/technotes/tn2007/tn2196.html#JTEXTFIELD_VARIANT
        NativeSearchFieldSupport.setSearchField(lbl, NativeSearchFieldSupport
                .isSearchField(txtField));
        NativeSearchFieldSupport.setFindPopupMenu(lbl, NativeSearchFieldSupport
                .getFindPopupMenu(txtField));

        // here we need to copy the border again for Mac OS X, because the above
        // calls may have replaced it.
        Border b = txt.getBorder();

        if (b == null) {
            lbl.setBorder(txt.getBorder());
        } else {
            Insets insets = b.getBorderInsets(txt);
            lbl.setBorder(
                    createEmptyBorder(insets.top, insets.left, insets.bottom, insets.right));
        }
        //		lbl.setBorder(txtField.getBorder());

        // buddy support: not needed, because BuddyLayoutAndBorder queries
        // original text field
        // BuddySupport.setOuterMargin(lbl,
        // BuddySupport.getOuterMargin(txtField));
        // BuddySupport.setLeft(lbl, BuddySupport.getLeft(txtField));
        // BuddySupport.setRight(lbl, BuddySupport.getRight(txtField));

        return lbl;
    }

    /**
     * Returns a shared {@link JTextField}.
     */
    @Override
    protected JTextComponent createPromptComponent() {
        return new LabelField();
    }

    private static final class LabelField extends JTextField {
        boolean hasFocus;

        @Override
        public boolean hasFocus() {
            return hasFocus;
        }

        /**
         * {@inheritDoc} <p>
         * 
         * Overridden to not automatically de/register itself from/to the ToolTipManager.
         * As rendering component it is not considered to be active in any way, so the
         * manager must not listen.
         */
        @Override
        public void setToolTipText(String text) {
            putClientProperty(TOOL_TIP_TEXT_KEY, text);
        }

        /**
         * Overridden for performance reasons.
         * See the <a href="#override">Implementation Note</a>
         * for more information.
         *
         * @since 1.5
         */
        @Override
        public void invalidate() {}

        /**
         * Overridden for performance reasons.
         * See the <a href="#override">Implementation Note</a>
         * for more information.
         */
        @Override
        public void validate() {}

        /**
         * Overridden for performance reasons.
         * See the <a href="#override">Implementation Note</a>
         * for more information.
         */
        @Override
        public void revalidate() {}

        /**
         * Overridden for performance reasons.
         * See the <a href="#override">Implementation Note</a>
         * for more information.
         */
        @Override
        public void repaint(long tm, int x, int y, int width, int height) {}

        /**
         * Overridden for performance reasons.
         * See the <a href="#override">Implementation Note</a>
         * for more information.
         */
        @Override
        public void repaint(Rectangle r) { }

        /**
         * Overridden for performance reasons.
         * See the <a href="#override">Implementation Note</a>
         * for more information.
         *
         * @since 1.5
         */
        @Override
        public void repaint() {
        }

        /**
         * Overridden for performance reasons.
         * See the <a href="#override">Implementation Note</a>
         * for more information.
         */
        @Override
        protected void firePropertyChange(String propertyName, Object oldValue, Object newValue) {
        	if (OS.isMacOSX()) {
        		super.firePropertyChange(propertyName, oldValue, newValue);
        	} else {
        		// Strings get interned...
        		if ("document".equals(propertyName)) {
        			super.firePropertyChange(propertyName, oldValue, newValue);
        		}
        	}
        }

        /**
         * Overridden for performance reasons.
         * See the <a href="#override">Implementation Note</a>
         * for more information.
         */
        @Override
        public void firePropertyChange(String propertyName, boolean oldValue, boolean newValue) {
        	if (OS.isMacOSX()) {
        		super.firePropertyChange(propertyName, oldValue, newValue);
        	}
        }
    }
}
