/*
 * @(#)PDFTextFieldUI.java  1.5  2007-08-06
 *
 * Copyright (c) 2004-2006 Werner Randelshofer, Immensee, Switzerland.
 * All rights reserved.
 *
 * You may not use, copy or modify this file, except in compliance with the
 * license agreement you entered into with Werner Randelshofer.
 * For details see accompanying license terms.
 */

package ch.randelshofer.pdf.plaf;

import ch.randelshofer.gui.border.BackgroundBorder;
import java.awt.*;
import java.awt.event.*;
import java.beans.*;

import javax.swing.*;
import javax.swing.plaf.*;
import javax.swing.plaf.basic.*;
import javax.swing.text.*;
import javax.swing.border.*;

/**
 * PDFTextFieldUI.
 *
 * @author  Werner Randelshofer
 * @version 1.6 2007-08-06 Select all text when the user tabs into the field. 
 * <br>1.5 2007-07-27 Added 2 pixels to the preferred width.
 * <br>1.4 2006-04-24 Fixed .opaque UIManager property. Added support for
 * .popupHandler UIManager property.
 * <br>1.2 2005-07-17 Adapted to changes in interface VisuallyLayoutable.
 * <br>1.1.1 2005-06-26 Fixed NullPointerException in method getVisualBounds.
 * <br>1.1 2005-02-27 Set to non-opaque. Support for margin added.
 * <br>1.0  December 1, 2004  Created.
 */
public class PDFTextFieldUI extends BasicTextFieldUI {
    boolean oldDragState = false;
    private FocusListener focusListener;
    private MouseListener popupListener;
    //private HierarchyListener hierarchyListener;
    
    /**
     * Preferred spacing between text fiels and other components.
     * /
     * private final static Insets regularSpacing = new Insets(10,10,10,10);
     * private final static Insets smallSpacing = new Insets(8,8,8,8);
     * private final static Insets miniSpacing = new Insets(8,8,8,8);
     */
    public static ComponentUI createUI(JComponent jcomponent) {
        return new PDFTextFieldUI();
    }
    
    @Override
    public void installUI(JComponent c) {
        super.installUI(c);
        PDFLookAndFeel plaf = PDFLookAndFeel.getInstance();
         PDFLookAndFeel.installColorsAndFont(c, "TextField.background", "TextField.foreground", "TextField.font");
	PDFLookAndFeel.installProperty(c, "opaque", plaf.getBoolean(getPropertyPrefix()+".opaque"));
    }
    @Override
    protected void installDefaults() {
        if (! PDFUtilities.isHeadless()) {
            oldDragState = getComponent().getDragEnabled();
           getComponent().setDragEnabled(true);
        }
       // super.installDefaults();
        
        String prefix = getPropertyPrefix();
        JTextComponent editor = getComponent();
        
        PDFLookAndFeel plaf = PDFLookAndFeel.getInstance();
        
        Font f = editor.getFont();
        if ((f == null) || (f instanceof UIResource)) {
            editor.setFont(plaf.getFont(prefix + ".font"));
        }

        Color bg = editor.getBackground();
        if ((bg == null) || (bg instanceof UIResource)) {
            editor.setBackground(plaf.getColor(prefix + ".background"));
        }
        
        Color fg = editor.getForeground();
        if ((fg == null) || (fg instanceof UIResource)) {
            editor.setForeground(plaf.getColor(prefix + ".foreground"));
        }

        Color color = editor.getCaretColor();
        if ((color == null) || (color instanceof UIResource)) {
            editor.setCaretColor(plaf.getColor(prefix + ".caretForeground"));
        }

        Color s = editor.getSelectionColor();
        if ((s == null) || (s instanceof UIResource)) {
            editor.setSelectionColor(plaf.getColor(prefix + ".selectionBackground"));
        }

        Color sfg = editor.getSelectedTextColor();
        if ((sfg == null) || (sfg instanceof UIResource)) {
            editor.setSelectedTextColor(plaf.getColor(prefix + ".selectionForeground"));
        }

        Color dfg = editor.getDisabledTextColor();
        if ((dfg == null) || (dfg instanceof UIResource)) {
            editor.setDisabledTextColor(plaf.getColor(prefix + ".inactiveForeground"));
        }

        Border b = editor.getBorder();
        if ((b == null) || (b instanceof UIResource)) {
            editor.setBorder(plaf.getBorder(prefix + ".border"));
        }

        Insets margin = editor.getMargin();
        if (margin == null || margin instanceof UIResource) {
            editor.setMargin(plaf.getInsets(prefix + ".margin"));
        }
    }
    
    protected void uninstallDefaults() {
        if (PDFUtilities.isHeadless()) {
            getComponent().setDragEnabled(oldDragState);
        }
        super.uninstallDefaults();
    }
    
    protected void installListeners() {
        focusListener = createFocusListener();
        if (focusListener != null) {
            getComponent().addFocusListener(focusListener);
        }
        popupListener = createPopupListener();
        if (popupListener != null) {
            getComponent().addMouseListener(popupListener);
        }
        super.installListeners();
    }
    
    protected void uninstallListeners() {
        if (focusListener != null) {
            getComponent().removeFocusListener(focusListener);
            focusListener = null;
        }
        if (popupListener != null) {
            getComponent().removeMouseListener(popupListener);
            popupListener = null;
        }
        super.uninstallListeners();
    }
    
    protected FocusListener createFocusListener() {
            return (FocusListener) PDFTextFieldFocusHandler.getInstance();
    }
    protected MouseListener createPopupListener() {
        return (MouseListener) UIManager.get(getPropertyPrefix()+".popupHandler");
    }
    
    public Insets getVisualMargin(JTextComponent tc) {
        Insets margin = (Insets) tc.getClientProperty("Quaqua.Component.visualMargin");
        if (margin == null) {
            margin = UIManager.getInsets("Component.visualMargin");
        }
        return (margin == null) ? new Insets(0, 0, 0 ,0) : (Insets) margin.clone();
    }
    
    
    protected void paintSafely(Graphics g) {
        Object oldHints = PDFUtilities.beginGraphics((Graphics2D) g);
        JTextComponent editor = getComponent();
        
        // Fill the background
        Border border = editor.getBorder();
        if (border != null && border instanceof BackgroundBorder) {
            Border bb = ((BackgroundBorder) border).getBackgroundBorder();
            bb.paintBorder(editor, g, 0, 0, editor.getWidth(), editor.getHeight());
            
        }
        super.paintSafely(g);
        PDFUtilities.endGraphics((Graphics2D) g, oldHints);
    }
    
    public void propertyChange(PropertyChangeEvent event) {
        String name = event.getPropertyName();
        if (name.equals("Frame.active")) {
            PDFUtilities.repaintBorder(getComponent());
       }
            super.propertyChange(event);
    }
    
    protected Caret createCaret() {
        Window window = SwingUtilities.getWindowAncestor(getComponent());
        PDFCaret caret = new PDFCaret(window, getComponent());
        return caret;
    }
    
    protected Highlighter createHighlighter() {
        return new PDFHighlighter();
    }
    /**
     * Creates the keymap to use for the text component, and installs
     * any necessary bindings into it.  By default, the keymap is
     * shared between all instances of this type of TextUI. The
     * keymap has the name defined by the getKeymapName method.  If the
     * keymap is not found, then DEFAULT_KEYMAP from JTextComponent is used.
     * <p>
     * The set of bindings used to create the keymap is fetched
     * from the UIManager using a key formed by combining the
     * {@link #getPropertyPrefix} method
     * and the string <code>.keyBindings</code>.  The type is expected
     * to be <code>JTextComponent.KeyBinding[]</code>.
     *
     * @return the keymap
     * @see #getKeymapName
     * @see javax.swing.text.JTextComponent
     */
    protected Keymap createKeymap() {
        String nm = getKeymapName();
        Keymap map = JTextComponent.getKeymap(nm);
        if (map == null) {
            Keymap parent = JTextComponent.getKeymap(JTextComponent.DEFAULT_KEYMAP);
            map = JTextComponent.addKeymap(nm, parent);
            String prefix = getPropertyPrefix();
            Object o = UIManager.get(prefix + ".keyBindings");
            if ((o != null) && (o instanceof JTextComponent.KeyBinding[])) {
                JTextComponent.KeyBinding[] bindings = (JTextComponent.KeyBinding[]) o;
                JTextComponent.loadKeymap(map, bindings, getComponent().getActions());
            }
        }
        return map;
    }
    
    public int getBaseline(JComponent c, int width, int height) {
        JTextComponent textComponent = (JTextComponent) c;
        View rootView = textComponent.getUI().getRootView(textComponent);
        if (rootView.getViewCount() > 0) {
            Insets insets = textComponent.getInsets();
            int h = height - insets.top - insets.bottom;
            int y = insets.top;
            View fieldView = rootView.getView(0);
            int vspan = (int)fieldView.getPreferredSpan(View.Y_AXIS);
            if (height != vspan) {
                int slop = h - vspan;
                y += slop / 2;
            }
            FontMetrics fm = textComponent.getFontMetrics(
                    textComponent.getFont());
            y += fm.getAscent();
            return y;
        }
        return -1;
    }
    
    
    public Dimension getPreferredSize(JComponent c) {
        // The following code has been derived from BasicTextUI.
	Document doc = ((JTextComponent) c).getDocument();
	Insets i = c.getInsets();
	Dimension d = c.getSize();
        View rootView = getRootView((JTextComponent) c);

	if (doc instanceof AbstractDocument) {
	    ((AbstractDocument)doc).readLock();
	}
	try {
	    if ((d.width > (i.left + i.right)) && (d.height > (i.top + i.bottom))) {
		rootView.setSize(d.width - i.left - i.right, d.height - i.top - i.bottom);
	    }
            else if (d.width == 0 && d.height == 0) {
                // Probably haven't been layed out yet, force some sort of
                // initial sizing.
                rootView.setSize(Integer.MAX_VALUE, Integer.MAX_VALUE);
            }
	    d.width = (int) Math.min((long) rootView.getPreferredSpan(View.X_AXIS) +
				     (long) i.left + (long) i.right, Integer.MAX_VALUE);
	    d.height = (int) Math.min((long) rootView.getPreferredSpan(View.Y_AXIS) +
				      (long) i.top + (long) i.bottom, Integer.MAX_VALUE);
	} finally {
	    if (doc instanceof AbstractDocument) {
		((AbstractDocument)doc).readUnlock();
	    }
	}
        
        // Fix: The preferred width is always two pixels too small
        // on a Mac. 
        d.width += 2;
        
	return d;
    }
}
