org.jdesktop.swingx.plaf.SearchFieldUI Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of swingx-all Show documentation
Show all versions of swingx-all Show documentation
A Maven project to aggregate all modules into a single artifact.
package org.jdesktop.swingx.plaf;
import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.Insets;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import javax.swing.Icon;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import javax.swing.plaf.TextUI;
import javax.swing.plaf.UIResource;
import javax.swing.text.Document;
import org.jdesktop.swingx.JXSearchField;
import org.jdesktop.swingx.JXSearchField.LayoutStyle;
import org.jdesktop.swingx.prompt.BuddySupport;
import org.jdesktop.swingx.search.NativeSearchFieldSupport;
/**
* The default {@link JXSearchField} UI delegate.
*
* @author Peter Weishapl
*
*/
public class SearchFieldUI extends BuddyTextFieldUI {
/**
* The search field that we're a UI delegate for. Initialized by the
* installUI
method, and reset to null by
* uninstallUI
.
*
* @see #installUI
* @see #uninstallUI
*/
protected JXSearchField searchField;
private Handler handler;
public static final Insets NO_INSETS = new Insets(0, 0, 0, 0);
public SearchFieldUI(TextUI delegate) {
super(delegate);
}
private Handler getHandler() {
if (handler == null) {
handler = new Handler();
}
return handler;
}
/**
* Calls {@link #installDefaults()}, adds the search, clear and popup button
* to the search field and registers a {@link PropertyChangeListener} ad
* {@link DocumentListener} and an {@link ActionListener} on the popup
* button.
*/
@Override
public void installUI(JComponent c) {
searchField = (JXSearchField) c;
super.installUI(c);
installDefaults();
layoutButtons();
configureListeners();
}
private void configureListeners() {
if (isNativeSearchField()) {
popupButton().removeActionListener(getHandler());
searchField.removePropertyChangeListener(getHandler());
} else {
popupButton().addActionListener(getHandler());
searchField.addPropertyChangeListener(getHandler());
}
// add support for instant search mode in any case.
searchField.getDocument().addDocumentListener(getHandler());
}
private boolean isNativeSearchField() {
return NativeSearchFieldSupport.isNativeSearchField(searchField);
}
@Override
protected BuddyLayoutAndBorder createBuddyLayoutAndBorder() {
return new BuddyLayoutAndBorder() {
/**
* This does nothing, if the search field is rendered natively on
* Leopard.
*/
@Override
protected void replaceBorderIfNecessary() {
if (!isNativeSearchField()) {
super.replaceBorderIfNecessary();
}
}
/**
* Return zero, when the search field is rendered natively on
* Leopard, to make painting work correctly.
*/
@Override
public Dimension preferredLayoutSize(Container parent) {
if (isNativeSearchField()) {
return new Dimension();
} else {
return super.preferredLayoutSize(parent);
}
}
/**
* Prevent 'jumping' when text is entered: Include the clear button,
* when layout style is Mac. When layout style is Vista: Take the
* clear button's preferred width if its either greater than the
* search button's pref. width or greater than the popup button's
* pref. width when a popup menu is installed and not using a
* seperate popup button.
*/
@Override
public Insets getBorderInsets(Component c) {
Insets insets = super.getBorderInsets(c);
if (searchField != null && !isNativeSearchField()) {
if (isMacLayoutStyle()) {
if (!clearButton().isVisible()) {
insets.right += clearButton().getPreferredSize().width;
}
} else {
JButton refButton = popupButton();
if (searchField.getFindPopupMenu() == null
^ searchField.isUseSeperatePopupButton()) {
refButton = searchButton();
}
int clearWidth = clearButton().getPreferredSize().width;
int refWidth = refButton.getPreferredSize().width;
int overSize = clearButton().isVisible() ? refWidth
- clearWidth : clearWidth - refWidth;
if (overSize > 0) {
insets.right += overSize;
}
}
}
return insets;
}
};
}
private void layoutButtons() {
BuddySupport.removeAll(searchField);
if (isNativeSearchField()) {
return;
}
if (isMacLayoutStyle()) {
BuddySupport.addLeft(searchButton(), searchField);
} else {
BuddySupport.addRight(searchButton(), searchField);
}
BuddySupport.addRight(clearButton(), searchField);
if (usingSeperatePopupButton()) {
BuddySupport.addRight(BuddySupport.createGap(getPopupOffset()),
searchField);
}
if (usingSeperatePopupButton() || !isMacLayoutStyle()) {
BuddySupport.addRight(popupButton(), searchField);
} else {
BuddySupport.addLeft(popupButton(), searchField);
}
}
private boolean isMacLayoutStyle() {
return searchField.getLayoutStyle() == LayoutStyle.MAC;
}
/**
* Initialize the search fields various properties based on the
* corresponding "SearchField.*" properties from defaults table. The
* {@link JXSearchField}s layout is set to the value returned by
* createLayout
. Also calls {@link #replaceBorderIfNecessary()}
* and {@link #updateButtons()}. This method is called by
* {@link #installUI(JComponent)}.
*
* @see #installUI
* @see #createLayout
* @see JXSearchField#customSetUIProperty(String, Object)
*/
protected void installDefaults() {
if (isNativeSearchField()) {
return;
}
if (UIManager.getBoolean("SearchField.useSeperatePopupButton")) {
searchField.customSetUIProperty("useSeperatePopupButton",
Boolean.TRUE);
} else {
searchField.customSetUIProperty("useSeperatePopupButton",
Boolean.FALSE);
}
searchField.customSetUIProperty("layoutStyle", UIManager
.get("SearchField.layoutStyle"));
searchField.customSetUIProperty("promptFontStyle", UIManager
.get("SearchField.promptFontStyle"));
if (shouldReplaceResource(searchField.getOuterMargin())) {
searchField.setOuterMargin(UIManager
.getInsets("SearchField.buttonMargin"));
}
updateButtons();
if (shouldReplaceResource(clearButton().getIcon())) {
clearButton().setIcon(UIManager.getIcon("SearchField.clearIcon"));
}
if (shouldReplaceResource(clearButton().getPressedIcon())) {
clearButton().setPressedIcon(
UIManager.getIcon("SearchField.clearPressedIcon"));
}
if (shouldReplaceResource(clearButton().getRolloverIcon())) {
clearButton().setRolloverIcon(
UIManager.getIcon("SearchField.clearRolloverIcon"));
}
searchButton().setIcon(
getNewIcon(searchButton().getIcon(), "SearchField.icon"));
popupButton().setIcon(
getNewIcon(popupButton().getIcon(), "SearchField.popupIcon"));
popupButton().setRolloverIcon(
getNewIcon(popupButton().getRolloverIcon(),
"SearchField.popupRolloverIcon"));
popupButton().setPressedIcon(
getNewIcon(popupButton().getPressedIcon(),
"SearchField.popupPressedIcon"));
}
/**
* Removes all installed listeners, the layout and resets the search field
* original border and removes all children.
*/
@Override
public void uninstallUI(JComponent c) {
super.uninstallUI(c);
searchField.removePropertyChangeListener(getHandler());
searchField.getDocument().removeDocumentListener(getHandler());
popupButton().removeActionListener(getHandler());
searchField.setLayout(null);
searchField.removeAll();
searchField = null;
}
/**
* Returns true if o
is null
or of instance
* {@link UIResource}.
*
* @param o an object
* @return true if o
is null
or of instance
* {@link UIResource}
*/
protected boolean shouldReplaceResource(Object o) {
return o == null || o instanceof UIResource;
}
/**
* Convience method for only replacing icons if they have not been
* customized by the user. Returns the icon from the defaults table
* belonging to resKey
, if
* {@link #shouldReplaceResource(Object)} with the icon
as a
* parameter returns true
. Otherwise returns icon
.
*
* @param icon the current icon
* @param resKey the resource key identifying the default icon
* @return the new icon
*/
protected Icon getNewIcon(Icon icon, String resKey) {
Icon uiIcon = UIManager.getIcon(resKey);
if (shouldReplaceResource(icon)) {
return uiIcon;
}
return icon;
}
/**
* Convienence method.
*
* @see JXSearchField#getCancelButton()
* @return the clear button
*/
protected final JButton clearButton() {
return searchField.getCancelButton();
}
/**
* Convienence method.
*
* @see JXSearchField#getFindButton()
* @return the search button
*/
protected final JButton searchButton() {
return searchField.getFindButton();
}
/**
* Convienence method.
*
* @see JXSearchField#getPopupButton()
* @return the popup button
*/
protected final JButton popupButton() {
return searchField.getPopupButton();
}
/**
* Returns true
if
* {@link JXSearchField#isUseSeperatePopupButton()} is true
and
* a search popup menu has been set.
*
* @return the popup button is used in addition to the search button
*/
public boolean usingSeperatePopupButton() {
return searchField.isUseSeperatePopupButton()
&& searchField.getFindPopupMenu() != null;
}
/**
* Returns the number of pixels between the popup button and the clear (or
* search) button as specified in the default table by
* 'SearchField.popupOffset'. Returns 0 if
* {@link #usingSeperatePopupButton()} returns false
*
* @return number of pixels between the popup button and the clear (or
* search) button
*/
protected int getPopupOffset() {
if (usingSeperatePopupButton()) {
return UIManager.getInt("SearchField.popupOffset");
}
return 0;
}
/**
* Sets the visibility of the search, clear and popup buttons depending on
* the search mode, layout stye, search text, search popup menu and the use
* of a seperate popup button. Also resets the search buttons pressed and
* rollover icons if the search field is in regular search mode or clears
* the icons when the search field is in instant search mode.
*/
protected void updateButtons() {
clearButton().setVisible(
(!searchField.isRegularSearchMode() || searchField
.isMacLayoutStyle())
&& hasText());
boolean clearNotHere = (searchField.isMacLayoutStyle() || !clearButton()
.isVisible());
searchButton()
.setVisible(
(searchField.getFindPopupMenu() == null || usingSeperatePopupButton())
&& clearNotHere);
popupButton().setVisible(
searchField.getFindPopupMenu() != null
&& (clearNotHere || usingSeperatePopupButton()));
if (searchField.isRegularSearchMode()) {
searchButton().setRolloverIcon(
getNewIcon(searchButton().getRolloverIcon(),
"SearchField.rolloverIcon"));
searchButton().setPressedIcon(
getNewIcon(searchButton().getPressedIcon(),
"SearchField.pressedIcon"));
} else {
// no action, therefore no rollover icon.
if (shouldReplaceResource(searchButton().getRolloverIcon())) {
searchButton().setRolloverIcon(null);
}
if (shouldReplaceResource(searchButton().getPressedIcon())) {
searchButton().setPressedIcon(null);
}
}
}
private boolean hasText() {
return searchField.getText() != null
&& searchField.getText().length() > 0;
}
class Handler implements PropertyChangeListener, ActionListener,
DocumentListener {
@Override
public void propertyChange(PropertyChangeEvent evt) {
String prop = evt.getPropertyName();
Object src = evt.getSource();
if (src.equals(searchField)) {
if ("findPopupMenu".equals(prop) || "searchMode".equals(prop)
|| "useSeperatePopupButton".equals(prop)
|| "searchMode".equals(prop)
|| "layoutStyle".equals(prop)) {
layoutButtons();
updateButtons();
} else if ("document".equals(prop)) {
Document doc = (Document) evt.getOldValue();
if (doc != null) {
doc.removeDocumentListener(this);
}
doc = (Document) evt.getNewValue();
if (doc != null) {
doc.addDocumentListener(this);
}
}
}
}
/**
* Shows the search popup menu, if installed.
*/
@Override
public void actionPerformed(ActionEvent e) {
if (searchField.getFindPopupMenu() != null) {
Component src = SearchFieldAddon.SEARCH_FIELD_SOURCE
.equals(UIManager.getString("SearchField.popupSource")) ? searchField
: (Component) e.getSource();
Rectangle r = SwingUtilities.getLocalBounds(src);
int popupWidth = searchField.getFindPopupMenu()
.getPreferredSize().width;
int x = searchField.isVistaLayoutStyle()
|| usingSeperatePopupButton() ? r.x + r.width
- popupWidth : r.x;
searchField.getFindPopupMenu().show(src, x, r.y + r.height);
}
}
@Override
public void changedUpdate(DocumentEvent e) {
update();
}
@Override
public void insertUpdate(DocumentEvent e) {
update();
}
@Override
public void removeUpdate(DocumentEvent e) {
update();
}
/**
* Called when the search text changes. Calls
* {@link JXSearchField#postActionEvent()} In instant search mode or
* starts the search field instant search timer if the instant search
* delay is greater 0.
*/
private void update() {
if (searchField.isInstantSearchMode()) {
searchField.getInstantSearchTimer().stop();
// only use timer when delay greater 0.
if (searchField.getInstantSearchDelay() > 0) {
searchField.getInstantSearchTimer().setInitialDelay(
searchField.getInstantSearchDelay());
searchField.getInstantSearchTimer().start();
} else {
searchField.postActionEvent();
}
}
updateButtons();
}
}
}