ch.randelshofer.quaqua.QuaquaPasswordFieldUI Maven / Gradle / Ivy
Show all versions of Quaqua Show documentation
/*
* @(#)QuaquaPasswordFieldUI.java
*
* Copyright (c) 2005-2010 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.quaqua;
import ch.randelshofer.quaqua.util.*;
import ch.randelshofer.quaqua.border.BackgroundBorder;
import ch.randelshofer.quaqua.util.Debug;
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.*;
/**
* QuaquaPasswordFieldUI.
*
* @author Werner Randelshofer
* @version $Id: QuaquaPasswordFieldUI.java 361 2010-11-21 11:19:20Z wrandelshofer $
*/
public class QuaquaPasswordFieldUI extends BasicPasswordFieldUI implements VisuallyLayoutable {
private FocusListener focusListener;
//private HierarchyListener hierarchyListener;
private MouseListener popupListener;
/**
* Creates a UI for a JPasswordField.
*
* @param c the JPasswordField
* @return the UI
*/
public static ComponentUI createUI(JComponent c) {
return new QuaquaPasswordFieldUI();
}
@Override
public void installUI(JComponent c) {
super.installUI(c);
QuaquaUtilities.installProperty(c, "opaque", UIManager.get(getPropertyPrefix()+".opaque"));
}
@Override
protected void installDefaults() {
super.installDefaults();
/* Unfortunately we can not set the echo char from the UI. :(
JPasswordField t = (JPasswordField) getComponent();
t.setEchoChar('\u2022');
*/
}
@Override
protected void uninstallDefaults() {
super.uninstallDefaults();
}
@Override
protected void installListeners() {
focusListener = createFocusListener();
if (focusListener != null) {
getComponent().addFocusListener(focusListener);
}
popupListener = createPopupListener();
if (popupListener != null) {
getComponent().addMouseListener(popupListener);
}
QuaquaTextCursorHandler.getInstance().installListeners(getComponent());
super.installListeners();
}
@Override
protected void uninstallListeners() {
if (focusListener != null) {
getComponent().removeFocusListener(focusListener);
focusListener = null;
}
if (popupListener != null) {
getComponent().removeMouseListener(popupListener);
popupListener = null;
}
QuaquaTextCursorHandler.getInstance().uninstallListeners(getComponent());
super.uninstallListeners();
}
protected FocusListener createFocusListener() {
return (FocusListener) UIManager.get(getPropertyPrefix() + ".focusHandler");
}
protected MouseListener createPopupListener() {
return (MouseListener) UIManager.get(getPropertyPrefix()+".popupHandler");
}
/**
* Fetches the EditorKit for the UI.
*
* @param tc the text component for which this UI is installed
* @return the editor capabilities
* @see TextUI#getEditorKit
*/
@Override
public EditorKit getEditorKit(JTextComponent tc) {
return QuaquaTextFieldUI.defaultKit;
}
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();
}
@Override
protected void paintSafely(Graphics g) {
Object oldHints = QuaquaUtilities.beginGraphics((Graphics2D) g);
JTextComponent editor = getComponent();
// Paint the background with the background border or the background color
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());
} else {
if (editor.isOpaque()) {
super.paintBackground(g);
}
}
super.paintSafely(g);
QuaquaUtilities.endGraphics((Graphics2D) g, oldHints);
Debug.paint(g, editor, this);
}
/**
* Paints a background for the view. This will only be
* called if isOpaque() on the associated component is
* true. The default is to paint the background color
* of the component.
*
* @param g the graphics context
*/
@Override
protected void paintBackground(Graphics g) {
// This method is overriden here, to make it do nothing.
// We already paint the background in method paintSafely();
}
@Override
public void propertyChange(PropertyChangeEvent event) {
String name = event.getPropertyName();
if (name.equals("Frame.active")) {
QuaquaUtilities.repaintBorder(getComponent());
} else if (name.equals("JComponent.sizeVariant")) {
QuaquaUtilities.applySizeVariant(getComponent());
} else {
super.propertyChange(event);
}
}
@Override
protected Caret createCaret() {
Window window = SwingUtilities.getWindowAncestor(getComponent());
QuaquaCaret caret = new QuaquaCaret(window, getComponent());
return caret;
}
@Override
protected Highlighter createHighlighter() {
return new QuaquaHighlighter();
}
/**
* Creates a view (PasswordView) for an element.
*
* @param elem the element
* @return the view
*/
@Override
public View create(Element elem) {
return new QuaquaPasswordView(elem);
}
/**
* 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.
*
* 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 name .keyBindings
. The type is expected
* to be JTextComponent.KeyBinding[]
.
*
* @return the keymap
* @see #getKeymapName
* @see javax.swing.text.JTextComponent
*/
@Override
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;
}
@Override
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 Rectangle getVisualBounds(JComponent c, int type, int width, int height) {
Rectangle bounds = new Rectangle(0,0,width,height);
if (type == VisuallyLayoutable.CLIP_BOUNDS) {
return bounds;
}
JTextComponent b = (JTextComponent) c;
if (type == VisuallyLayoutable.COMPONENT_BOUNDS
&& b.getBorder() != null) {
Border border = b.getBorder();
if (border instanceof UIResource) {
InsetsUtil.subtractInto(getVisualMargin(b), bounds);
// FIXME - Should derive this value from border
// FIXME - If we had layout managers that supported baseline alignment,
// we wouldn't have to subtract one here
bounds.height -= 1;
}
} else {
bounds = getVisibleEditorRect();
int baseline = getBaseline(b, width, height);
Rectangle textBounds = Fonts.getPerceivedBounds(b.getText(), b.getFont(), c);
if (bounds == null) {
bounds = textBounds;
bounds.y += baseline;
} else {
bounds.y = baseline + textBounds.y;
bounds.height = textBounds.height;
}
bounds.x += 1;
bounds.width -= 2;
}
return bounds;
}
@Override
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;
}
}