/* GNU GENERAL PUBLIC LICENSE Copyright (C) 2006 The Lobo Project This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either verion 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Contact info: lobochief@users.sourceforge.net */ package org.lobobrowser.primary.ext; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.KeyAdapter; import java.awt.event.KeyEvent; import java.awt.event.MouseEvent; import java.awt.event.MouseListener; import java.util.Collection; import java.util.Iterator; import javax.swing.AbstractAction; import javax.swing.JComboBox; import javax.swing.JComponent; import javax.swing.KeyStroke; import javax.swing.event.PopupMenuEvent; import javax.swing.event.PopupMenuListener; public class AddressField extends JComboBox<String> { private static final long serialVersionUID = 3726432852226425553L; private final ComponentSource componentSource; public AddressField(final ComponentSource cs) { this.componentSource = cs; this.setEditable(true); final TextFieldComboBoxEditor editor = new TextFieldComboBoxEditor(); this.setEditor(editor); editor.addKeyListener(new KeyAdapter() { @Override public void keyReleased(final KeyEvent e) { onKeyReleased(e); } @Override public void keyPressed(final KeyEvent e) { onKeyPressed(e); } }); editor.addMouseListener(new MouseListener() { @Override public void mouseReleased(MouseEvent e) { processMouseEvent(e); } @Override public void mousePressed(MouseEvent e) { processMouseEvent(e); } @Override public void mouseExited(MouseEvent e) { processMouseEvent(e); } @Override public void mouseEntered(MouseEvent e) { processMouseEvent(e); } @Override public void mouseClicked(MouseEvent e) { processMouseEvent(e); } }); this.addPopupMenuListener(new PopupMenuListener() { public void popupMenuWillBecomeVisible(final PopupMenuEvent e) { onBeforePopupVisible(); } public void popupMenuWillBecomeInvisible(final PopupMenuEvent e) { } public void popupMenuCanceled(final PopupMenuEvent e) { } }); this.addActionListener(new ActionListener() { public void actionPerformed(final ActionEvent event) { final String cmd = event.getActionCommand(); if ("comboBoxEdited".equals(cmd)) { onEdited(event.getModifiers()); } else if ("comboBoxChanged".equals(cmd)) { } } }); getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW) .put(KeyStroke.getKeyStroke(KeyEvent.VK_L, ComponentSource.CMD_CTRL_KEY_MASK), "edit URL"); getActionMap().put("edit URL", new AbstractAction() { private static final long serialVersionUID = 891701932843814767L; public void actionPerformed(final ActionEvent e) { requestFocus(); getEditor().selectAll(); } }); getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW) .put(KeyStroke.getKeyStroke(KeyEvent.VK_K, ComponentSource.CMD_CTRL_KEY_MASK), "search Keyword"); getActionMap().put("search Keyword", new AbstractAction() { private static final long serialVersionUID = -6652427897850176208L; public void actionPerformed(final ActionEvent e) { requestFocus(); setText("?"); } }); // This needed the first time to set a reasonable popup size. this.onBeforePopupVisible(); } public String getText() { if (this.isEditable()) { return (String) this.getEditor().getItem(); } else { return String.valueOf(this.getSelectedItem()); } } public void setText(final String text) { final JComboBox<String> combo = this; final boolean editable = this.isEditable(); if (editable) { combo.getEditor().setItem(text); } } public void setUrl(final java.net.URL url) { this.setText(url == null ? "" : url.toExternalForm()); } private void onBeforePopupVisible() { if ((comboInvalid || comboHasHeadMatches) && !populatingMatches) { populateCombo(this.getText()); } } private boolean comboInvalid = true; private boolean comboHasHeadMatches = false; private boolean populatingMatches = false; private void populateCombo(final String comboBoxText) { // Expected to be called in GUI thread. this.populatingMatches = true; try { final JComboBox<String> urlComboBox = this; urlComboBox.removeAllItems(); final Collection<String> recentUrls = ComponentSource.getRecentLocations(30); for (final String url : recentUrls) { urlComboBox.addItem(url); } this.setText(comboBoxText); this.comboHasHeadMatches = false; this.comboInvalid = false; } finally { this.populatingMatches = false; } } private void onEdited(final int modifiers) { // if(this.getText().length() != 0) { // this.componentSource.navigateOrSearch(); // } } private void onKeyReleased(final KeyEvent event) { final AddressField urlComboBox = this; final char releasedChar = event.getKeyChar(); if (validPopupChar(releasedChar)) { final String urlText = urlComboBox.getText(); final Collection<String> headMatches = ComponentSource.getPotentialMatches(urlText, 30); if (headMatches.size() == 0) { if (urlComboBox.isPopupVisible()) { urlComboBox.hidePopup(); } } else { populatingMatches = true; try { urlComboBox.removeAllItems(); final Iterator<String> i = headMatches.iterator(); while (i.hasNext()) { final String matchUrl = i.next(); urlComboBox.addItem(matchUrl); } comboHasHeadMatches = true; if (!urlComboBox.isPopupVisible()) { urlComboBox.showPopup(); } urlComboBox.setSelectedItem(null); urlComboBox.setText(urlText); } finally { populatingMatches = false; } } } } private void onKeyPressed(final KeyEvent event) { final AddressField urlComboBox = this; if (event.getKeyCode() == KeyEvent.VK_ENTER) { final String urlText = urlComboBox.getText(); if (urlText.length() != 0) { this.componentSource.navigateOrSearch(); } } } private static boolean validPopupChar(final char ch) { return Character.isLetterOrDigit(ch) || (ch == '.') || (ch == '/'); } }