package net.sourceforge.tuned.ui; import java.awt.Component; import java.awt.Rectangle; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.FocusEvent; import java.awt.event.FocusListener; import java.util.regex.Matcher; import java.util.regex.Pattern; import javax.swing.AbstractAction; import javax.swing.BorderFactory; import javax.swing.DefaultListCellRenderer; import javax.swing.JButton; import javax.swing.JComboBox; import javax.swing.JComponent; import javax.swing.JList; import javax.swing.KeyStroke; import javax.swing.border.EmptyBorder; import javax.swing.border.LineBorder; import javax.swing.event.DocumentEvent; import javax.swing.event.DocumentListener; import javax.swing.plaf.basic.BasicComboBoxUI; import javax.swing.plaf.basic.BasicComboPopup; import javax.swing.plaf.basic.ComboPopup; import javax.swing.text.JTextComponent; import net.miginfocom.swing.MigLayout; import net.sourceforge.filebot.ResourceManager; public class SelectButtonTextField extends JComponent { private SelectButton selectButton = new SelectButton(); private JComboBox editor = new JComboBox(); public SelectButtonTextField() { selectButton.addActionListener(textFieldFocusOnClick); editor.setBorder(BorderFactory.createMatteBorder(1, 0, 1, 1, ((LineBorder) selectButton.getBorder()).getLineColor())); setLayout(new MigLayout("nogrid, fill")); add(selectButton, "h pref!, w pref!, sizegroupy this"); add(editor, "gap 0, w 195px!, sizegroupy this"); editor.setRenderer(new CompletionCellRenderer()); editor.setUI(new TextFieldComboBoxUI()); TunedUtilities.putActionForKeystroke(this, KeyStroke.getKeyStroke("ctrl UP"), new SpinClientAction(-1)); TunedUtilities.putActionForKeystroke(this, KeyStroke.getKeyStroke("ctrl DOWN"), new SpinClientAction(1)); } public String getText() { return ((TextFieldComboBoxUI) editor.getUI()).getEditor().getText(); } public JComboBox getEditor() { return editor; } public SelectButton getSelectButton() { return selectButton; } private final ActionListener textFieldFocusOnClick = new ActionListener() { public void actionPerformed(ActionEvent e) { getEditor().requestFocus(); } }; private class SpinClientAction extends AbstractAction { private int spin; public SpinClientAction(int spin) { this.spin = spin; } public void actionPerformed(ActionEvent e) { selectButton.spinValue(spin); } } private class CompletionCellRenderer extends DefaultListCellRenderer { @Override public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) { super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus); setBorder(new EmptyBorder(1, 4, 1, 4)); String highlightText = SelectButtonTextField.this.getText().substring(0, ((TextFieldComboBoxUI) editor.getUI()).getEditor().getSelectionStart()); // highlight the matching sequence Matcher matcher = Pattern.compile(highlightText, Pattern.LITERAL | Pattern.CASE_INSENSITIVE).matcher(value.toString()); // use no-break, because we really don't want line-wrapping in our table cells StringBuffer htmlText = new StringBuffer(""); if (matcher.find()) { matcher.appendReplacement(htmlText, "$0"); } matcher.appendTail(htmlText); htmlText.append(""); setText(htmlText.toString()); return this; } } private class TextFieldComboBoxUI extends BasicComboBoxUI { @Override protected JButton createArrowButton() { return new JButton(ResourceManager.getIcon("action.list")); } @Override public void configureArrowButton() { super.configureArrowButton(); arrowButton.setContentAreaFilled(false); arrowButton.setFocusable(false); } @Override protected void configureEditor() { JTextComponent editor = getEditor(); editor.setEnabled(comboBox.isEnabled()); editor.setFocusable(comboBox.isFocusable()); editor.setFont(comboBox.getFont()); editor.setBorder(BorderFactory.createEmptyBorder(0, 3, 0, 3)); editor.addFocusListener(createFocusListener()); editor.getDocument().addDocumentListener(new DocumentListener() { @Override public void changedUpdate(DocumentEvent e) { popup.getList().repaint(); } @Override public void insertUpdate(DocumentEvent e) { popup.getList().repaint(); } @Override public void removeUpdate(DocumentEvent e) { popup.getList().repaint(); } }); } public JTextComponent getEditor() { return (JTextComponent) editor; } @Override protected ComboPopup createPopup() { return new BasicComboPopup(comboBox) { @Override public void show(Component invoker, int x, int y) { super.show(invoker, x - selectButton.getWidth(), y); } @Override protected Rectangle computePopupBounds(int px, int py, int pw, int ph) { Rectangle bounds = super.computePopupBounds(px, py, pw, ph); bounds.width += selectButton.getWidth(); return bounds; } }; } @Override protected FocusListener createFocusListener() { return new FocusHandler() { /** * Prevent action events from being fired on focusLost. */ @Override public void focusLost(FocusEvent e) { if (isPopupVisible(comboBox)) { setPopupVisible(comboBox, false); } } }; } } }