package com.onaro.sanscreen.client.view.search; import java.awt.Color; import java.awt.Font; import java.awt.FontMetrics; import java.awt.Graphics; import java.awt.KeyboardFocusManager; import java.awt.event.ActionEvent; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import javax.swing.AbstractAction; import javax.swing.AbstractButton; import javax.swing.Action; import javax.swing.JButton; import javax.swing.JTextField; import javax.swing.border.EmptyBorder; import javax.swing.event.DocumentEvent; import javax.swing.event.DocumentListener; import org.apache.commons.lang3.StringUtils; import com.jidesoft.swing.LabeledTextField; import com.onaro.sanscreen.client.Messages; import com.onaro.sanscreen.client.Resources; /** * The search field component, sub-classing the Jide LabeledTextField. */ class SearchField extends LabeledTextField { private static final long serialVersionUID = 1L; private final SearchTextListener searchTextListener; private static final String HINT_TEXT = Messages.INSTANCE.getSearchFieldHintText(); private AbstractAction clearAction; private AbstractAction searchingAction; private MouseAdapter activateListener; public SearchField(SearchTextListener searchTextListener) { if (searchTextListener == null) { throw new IllegalArgumentException("searchTextListener must not be null"); //$NON-NLS-1$ } this.searchTextListener = searchTextListener; setIcon(Resources.INSTANCE.getSearchSmallIcon()); initSize(); initListeners(); } @Override protected AbstractButton createButton() { // The button needs to be stripped down: JButton button = new JButton(); button.setBorderPainted(false); button.setContentAreaFilled(false); button.setFocusable(false); // strip button of border to keep size at minimum: button.setBorder(new EmptyBorder(0,0,0,0)); return button; } @Override protected JTextField createTextField() { // Text field should have hint text when empty: JTextField textField = new JTextField() { private static final long serialVersionUID = 1L; private final Font hintFont = getFont().deriveFont(Font.ITALIC); @Override public void paint(Graphics g) { super.paint(g); if (getText().length() == 0) { FontMetrics fm = getFontMetrics(hintFont); g.setFont(hintFont); g.setColor(Color.LIGHT_GRAY); int y = getInsets().top + fm.getAscent() + fm.getLeading(); int x = getInsets().left; g.drawString(HINT_TEXT, x, y); } } @Override public boolean requestFocusInWindow() { if (!isEnabled()) { activate(); } return super.requestFocusInWindow(); } }; return textField; } private void initListeners() { getTextField().getDocument().addDocumentListener(new DocumentListener() { @Override public void insertUpdate(DocumentEvent e) { // Initiate a search if text added: searchTextListener.updateSearch(); } @Override public void removeUpdate(DocumentEvent e) { // If no text left, remove any button icon if (getTextField().getText().length() == 0) { getButton().setIcon(null); } searchTextListener.updateSearch(); } @Override public void changedUpdate(DocumentEvent e) {} }); } private void initSize() { // Looks like it's a hack, but a very reliable means of fixing a text field's size (doing it the "right" way // is actually more of a hack): JTextField field = getTextField(); field.setText("This is a typical search item length"); //$NON-NLS-1$ field.setPreferredSize(getTextField().getPreferredSize()); field.setMaximumSize(getTextField().getPreferredSize()); field.setMinimumSize(getTextField().getPreferredSize()); field.setText(null); // A similar means of fixing the button's size: AbstractButton button = getButton(); button.setIcon(Resources.INSTANCE.getCloseSelectedIcon()); button.setPreferredSize(getButton().getPreferredSize()); button.setMaximumSize(getButton().getPreferredSize()); button.setMinimumSize(getButton().getPreferredSize()); button.setIcon(null); // Fix overall size: setPreferredSize(getPreferredSize()); setMaximumSize(getPreferredSize()); } /** * Clears field of text and icon/clear action, and "re-activates". */ public void clear() { getTextField().setText(null); // No icon or action now: getButton().setAction(null); activate(); } /** * A search has concluded and is displayed. The field needs to now show and enable the close/clear button. */ public void markComplete() { getButton().setAction(getClearAction()); } /** * The search is in process. Display the field button with a non-active "processing" icon. */ public void markSearching() { getButton().setAction(getSearchingAction()); } /** * Makes the search field inactive: if contains any text, makes field appear disabled; A mouse-click re-activates. */ public void deactivate() { if (StringUtils.isEmpty(getTextField().getText())) { return; } if (getTextField().hasFocus()) { KeyboardFocusManager.getCurrentKeyboardFocusManager().clearGlobalFocusOwner(); } getTextField().setEnabled(false); // Make transparent, so we don't display the fields disabled background color: getTextField().setOpaque(false); if (activateListener == null) { activateListener = new MouseAdapter() { @Override public void mousePressed(MouseEvent e) { activate(); // Remove this listener -- no longer needed until next deactivate: getTextField().removeMouseListener(activateListener); } }; } getTextField().addMouseListener(activateListener); } private Action getClearAction() { if (clearAction == null) { clearAction = new AbstractAction() { private static final long serialVersionUID = 1L; @Override public void actionPerformed(ActionEvent e) { clear(); } }; clearAction.putValue(AbstractAction.LARGE_ICON_KEY, Resources.INSTANCE.getCloseSelectedIcon()); } return clearAction; } private Action getSearchingAction() { if (searchingAction == null) { searchingAction = new AbstractAction() { private static final long serialVersionUID = 1L; @Override public void actionPerformed(ActionEvent e) {} }; searchingAction.putValue(AbstractAction.LARGE_ICON_KEY, Resources.INSTANCE.getAnimatedRefreshIcon()); } return searchingAction; } private void activate() { getTextField().setEnabled(true); // Can now use the enabled background color getTextField().setOpaque(true); getTextField().requestFocusInWindow(); int searchTextLength = getTextField().getText().length(); if (searchTextLength > 0) { searchTextListener.updateSearch(); } } }