package skt.swing.search; /** * MySwing: Advanced Swing Utilites * Copyright (C) 2005 Santhosh Kumar T * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. */ import java.awt.Color; import java.awt.Point; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.KeyEvent; import java.awt.event.KeyListener; import javax.swing.AbstractAction; import javax.swing.Action; import javax.swing.JComponent; import javax.swing.JScrollPane; import javax.swing.JTextField; import javax.swing.KeyStroke; import javax.swing.Popup; import javax.swing.PopupFactory; import javax.swing.event.DocumentEvent; import javax.swing.event.DocumentListener; import javax.swing.event.PopupMenuEvent; import javax.swing.event.PopupMenuListener; import javax.swing.text.Position; import org.rr.commons.swing.SwingUtils; /** * @author Santhosh Kumar T * @email santhosh@in.fiorano.com */ public abstract class FindAction extends AbstractAction implements DocumentListener, KeyListener, PopupMenuListener { private FindActionPanel searchPanel = new FindActionPanel(); private Popup popup; protected JTextField searchField = searchPanel.getSearchField(); // component on which search is taking place protected JComponent comp = null; public FindAction() { this(null); } public FindAction(JComponent comp) { super("incremantal-search"); this.comp = comp; int modifiers = KeyEvent.CTRL_MASK; if (!isCaseSensitiveSearch()) { modifiers = modifiers | KeyEvent.SHIFT_MASK; } putValue(Action.ACCELERATOR_KEY, KeyStroke.getKeyStroke('I', modifiers)); searchField.registerKeyboardAction(new ActionListener() { public void actionPerformed(ActionEvent e) { hidePopup(); } }, KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), JComponent.WHEN_FOCUSED); searchPanel.getCloseButton().addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { hidePopup(); } }); } private void hidePopup() { popup.hide(); comp.requestFocus(); } /** * Returns a <code>Popup</code> instance from the * <code>PopupMenuUI</code> that has had <code>show</code> invoked on * it. If the current <code>popup</code> is non-null, * this will invoke <code>dispose</code> of it, and then * <code>show</code> the new one. * <p> * This does NOT fire any events, it is up the caller to dispatch * the necessary events. */ private Popup getPopup(int x, int y) { if(popup == null) { popup = PopupFactory.getSharedInstance().getPopup(comp, searchPanel, x, y); } else { hidePopup(); popup = PopupFactory.getSharedInstance().getPopup(comp, searchPanel, x, y); } popup.show(); return popup; } /*-------------------------------------------------[ ActionListener ]---------------------------------------------------*/ public void actionPerformed(ActionEvent ae) { if (ae.getSource() == searchField) { hidePopup(); } else { if(comp == null) { comp = (JComponent) ae.getSource(); } searchField.removeActionListener(this); searchField.removeKeyListener(this); searchField.getDocument().removeDocumentListener(this); initSearch(ae); searchField.addActionListener(this); searchField.addKeyListener(this); searchField.getDocument().addDocumentListener(this); Point locationOnScreen; JScrollPane surroundingScrollPane = SwingUtils.getSurroundingScrollPane(comp); if(surroundingScrollPane != null) { locationOnScreen = surroundingScrollPane.getLocationOnScreen(); } else { locationOnScreen = comp.getLocationOnScreen(); } getPopup(locationOnScreen.x, locationOnScreen.y); searchField.requestFocus(); } } // can be overridden by subclasses to change initial search text etc. protected void initSearch(ActionEvent ae) { searchField.setText(""); // NOI18N searchField.setForeground(Color.black); } private void changed(Position.Bias bias) { // note: popup.pack() doesn't work for first character insert // popup.setVisible(false); // popup.setVisible(true); searchField.requestFocus(); searchField.setForeground(changed(comp, searchField.getText(), bias) ? Color.black : Color.red); } public boolean isCaseSensitiveSearch() { return searchPanel.isCaseSensitiveSearch(); } // should search for given text and select item and // return true if search is successfull protected abstract boolean changed(JComponent comp, String text, Position.Bias bias); /*-------------------------------------------------[ DocumentListener ]---------------------------------------------------*/ public void insertUpdate(DocumentEvent e) { changed(null); } public void removeUpdate(DocumentEvent e) { changed(null); } public void changedUpdate(DocumentEvent e) { } /*-------------------------------------------------[ KeyListener ]---------------------------------------------------*/ protected boolean shiftDown = false; protected boolean controlDown = false; public void keyPressed(KeyEvent ke) { shiftDown = ke.isShiftDown(); controlDown = ke.isControlDown(); switch (ke.getKeyCode()) { case KeyEvent.VK_UP: changed(Position.Bias.Backward); break; case KeyEvent.VK_DOWN: changed(Position.Bias.Forward); break; } } public void keyTyped(KeyEvent e) { } public void keyReleased(KeyEvent e) { } /*-------------------------------------------------[ PopupMenuListener ]---------------------------------------------------*/ public void popupMenuWillBecomeVisible(PopupMenuEvent e) { } public void popupMenuWillBecomeInvisible(PopupMenuEvent e) { comp.requestFocus(); // comp = null; //favor gc } public void popupMenuCanceled(PopupMenuEvent e) { } }