All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.jdesktop.swingx.search.SearchFactory Maven / Gradle / Ivy

The newest version!
/*
 * $Id$
 *
 * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle,
 * Santa Clara, California 95054, U.S.A. All rights reserved.
 *
 * 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.
 * 
 * You should have received a copy of the GNU Lesser 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
 */
package org.jdesktop.swingx.search;

import java.awt.Component;
import java.awt.Container;
import java.awt.Dialog;
import java.awt.Frame;
import java.awt.KeyboardFocusManager;
import java.awt.Point;
import java.awt.Window;
import java.awt.event.ActionEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.lang.ref.WeakReference;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;

import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.JComponent;
import javax.swing.JOptionPane;
import javax.swing.JToolBar;
import javax.swing.KeyStroke;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;

import org.jdesktop.swingx.JXDialog;
import org.jdesktop.swingx.JXFindBar;
import org.jdesktop.swingx.JXFindPanel;
import org.jdesktop.swingx.JXFrame;
import org.jdesktop.swingx.JXRootPane;
import org.jdesktop.swingx.plaf.LookAndFeelAddons;
import org.jdesktop.swingx.plaf.UIDependent;
import org.jdesktop.swingx.util.Utilities;

/**
 * Factory to create, configure and show application consistent
 * search and find widgets.
 * 
 * Typically a shared JXFindBar is used for incremental search, while
 * a shared JXFindPanel is used for batch search. This implementation 
 * 
 * 
    *
  • JXFindBar - adds and shows it in the target's toplevel container's * toolbar (assuming a JXRootPane) *
  • JXFindPanel - creates a JXDialog, adds and shows the findPanel in the * Dialog *
* * * PENDING: JW - update (?) views/wiring on focus change. Started brute force - * stop searching. This looks extreme confusing for findBars added to ToolBars * which are empty except for the findbar. Weird problem if triggered from * menu - find widget disappears after having been shown for an instance. * Where's the focus? * * * PENDING: add methods to return JXSearchPanels (for use by PatternMatchers). * * @author Jeanette Winzenburg */ public class SearchFactory implements UIDependent { private static class LaFListener implements PropertyChangeListener { private final WeakReference ref; public LaFListener(SearchFactory sf) { this.ref = new WeakReference(sf); } /** * {@inheritDoc} */ @Override public void propertyChange(PropertyChangeEvent evt) { SearchFactory sf = ref.get(); if (sf == null) { UIManager.removePropertyChangeListener(this); } else if ("lookAndFeel".equals(evt.getPropertyName())) { sf.updateUI(); } } } // PENDING: rename methods to batch/incremental instead of dialog/toolbar static { // Hack to enforce loading of SwingX framework ResourceBundle LookAndFeelAddons.getAddon(); } private static SearchFactory searchFactory; /** the shared find widget for batch-find. */ protected JXFindPanel findPanel; /** the shared find widget for incremental-find. */ protected JXFindBar findBar; /** this is a temporary hack: need to remove the useSearchHighlighter property. */ protected JComponent lastFindBarTarget; private boolean useFindBar; private Point lastFindDialogLocation; private FindRemover findRemover; /** * Returns the shared SearchFactory. * * @return the shared SearchFactory */ public static SearchFactory getInstance() { if (searchFactory == null) { searchFactory = new SearchFactory(); } return searchFactory; } /** * Sets the shared SearchFactory. * * @param factory */ public static void setInstance(SearchFactory factory) { searchFactory = factory; } public SearchFactory() { UIManager.addPropertyChangeListener(new LaFListener(this)); } /** * Returns a common Keystroke for triggering * a search. Tries to be OS-specific.

* * PENDING: this should be done in the LF and the * keyStroke looked up in the UIManager. * * @return the keyStroke to register with a findAction. */ public KeyStroke getSearchAccelerator() { // JW: this should be handled by the LF! // get the accelerator mnemonic from the UIManager String findMnemonic = "F"; KeyStroke findStroke = Utilities.stringToKey("D-" + findMnemonic); // fallback for sandbox (this should be handled in Utilities instead!) if (findStroke == null) { findStroke = KeyStroke.getKeyStroke("control F"); } return findStroke; } /** * Returns decision about using a batch- vs. incremental-find for the * searchable. This implementation returns the useFindBar property directly. * * @param target - the component associated with the searchable * @param searchable - the object to search. * @return true if a incremental-find should be used, false otherwise. */ public boolean isUseFindBar(JComponent target, Searchable searchable) { return useFindBar; } /** * Sets the default search type to incremental or batch, for a * true/false boolean. The default value is false (== batch). * * @param incremental a boolean to indicate the default search * type, true for incremental and false for batch. */ public void setUseFindBar(boolean incremental) { if (incremental == useFindBar) return; this.useFindBar = incremental; getFindRemover().endSearching(); } /** * Shows an appropriate find widget targeted at the searchable. * Opens a batch-find or incremental-find * widget based on the return value of isUseFindBar. * * @param target - the component associated with the searchable * @param searchable - the object to search. * * @see #isUseFindBar(JComponent, Searchable) * @see #setUseFindBar(boolean) */ public void showFindInput(JComponent target, Searchable searchable) { if (isUseFindBar(target, searchable)) { showFindBar(target, searchable); } else { showFindDialog(target, searchable); } } //------------------------- incremental search /** * Show a incremental-find widget targeted at the searchable. * * This implementation uses a JXFindBar and inserts it into the * target's toplevel container toolbar. * * PENDING: Nothing shown if there is no toolbar found. * * @param target - the component associated with the searchable * @param searchable - the object to search. */ public void showFindBar(JComponent target, Searchable searchable) { if (target == null) return; if (findBar == null) { findBar = getSharedFindBar(); } else { releaseFindBar(); } Window topLevel = SwingUtilities.getWindowAncestor(target); if (topLevel instanceof JXFrame) { JXRootPane rootPane = ((JXFrame) topLevel).getRootPaneExt(); JToolBar toolBar = rootPane.getToolBar(); if (toolBar == null) { toolBar = new JToolBar(); rootPane.setToolBar(toolBar); } toolBar.add(findBar, 0); rootPane.revalidate(); KeyboardFocusManager.getCurrentKeyboardFocusManager().focusNextComponent(findBar); } lastFindBarTarget = target; findBar.setLocale(target.getLocale()); target.putClientProperty(AbstractSearchable.MATCH_HIGHLIGHTER, Boolean.TRUE); getSharedFindBar().setSearchable(searchable); installFindRemover(target, findBar); } /** * Returns the shared JXFindBar. Creates and configures on * first call. * * @return the shared JXFindBar */ public JXFindBar getSharedFindBar() { if (findBar == null) { findBar = createFindBar(); configureSharedFindBar(); } return findBar; } /** * Factory method to create a JXFindBar. * * @return the JXFindBar */ public JXFindBar createFindBar() { return new JXFindBar(); } protected void installFindRemover(Container target, Container findWidget) { if (target != null) { getFindRemover().addTarget(target); } getFindRemover().addTarget(findWidget); } private FindRemover getFindRemover() { if (findRemover == null) { findRemover = new FindRemover(); } return findRemover; } /** * convenience method to remove a component from its parent * and revalidate the parent */ protected void removeFromParent(JComponent component) { Container oldParent = component.getParent(); if (oldParent != null) { oldParent.remove(component); if (oldParent instanceof JComponent) { ((JComponent) oldParent).revalidate(); } else { // not sure... never have non-j comps oldParent.invalidate(); oldParent.validate(); } } } protected void stopSearching() { if (findPanel != null) { lastFindDialogLocation = hideSharedFindPanel(false); findPanel.setSearchable(null); } if (findBar != null) { releaseFindBar(); } } /** * Pre: findbar != null. */ protected void releaseFindBar() { findBar.setSearchable(null); if (lastFindBarTarget != null) { lastFindBarTarget.putClientProperty(AbstractSearchable.MATCH_HIGHLIGHTER, Boolean.FALSE); lastFindBarTarget = null; } removeFromParent(findBar); } /** * Configures the shared FindBar. This method is * called once after creation of the shared FindBar. * Subclasses can override to add configuration code.

* * Here: registers a custom action to remove the * findbar from its ancestor container. * * PRE: findBar != null. * */ protected void configureSharedFindBar() { Action removeAction = new AbstractAction() { @Override public void actionPerformed(ActionEvent e) { removeFromParent(findBar); // stopSearching(); // releaseFindBar(); } }; findBar.getActionMap().put(JXDialog.CLOSE_ACTION_COMMAND, removeAction); } //------------------------ batch search /** * Show a batch-find widget targeted at the given Searchable. * * This implementation uses a shared JXFindPanel contained * JXDialog. * * @param target - * the component associated with the searchable * @param searchable - * the object to search. */ public void showFindDialog(JComponent target, Searchable searchable) { Window frame = null; //JOptionPane.getRootFrame(); if (target != null) { target.putClientProperty(AbstractSearchable.MATCH_HIGHLIGHTER, Boolean.FALSE); frame = SwingUtilities.getWindowAncestor(target); // if (window instanceof Frame) { // frame = (Frame) window; // } } JXDialog topLevel = getDialogForSharedFindPanel(); JXDialog findDialog; if ((topLevel != null) && (topLevel.getOwner().equals(frame))) { findDialog = topLevel; // JW: #635-swingx - quick hack to update title to current locale ... // findDialog.setTitle(getSharedFindPanel().getName()); KeyboardFocusManager.getCurrentKeyboardFocusManager().focusNextComponent(findDialog); } else { Point location = hideSharedFindPanel(true); if (frame instanceof Frame) { findDialog = new JXDialog((Frame) frame, getSharedFindPanel()); } else if (frame instanceof Dialog) { // fix #215-swingx: had problems with secondary modal dialogs. findDialog = new JXDialog((Dialog) frame, getSharedFindPanel()); } else { findDialog = new JXDialog(JOptionPane.getRootFrame(), getSharedFindPanel()); } // RJO: shouldn't we avoid overloaded useage like this in a JSR296 world? swap getName() for getTitle() here? // findDialog.setTitle(getSharedFindPanel().getName()); // JW: don't - this will stay on top of all applications! // findDialog.setAlwaysOnTop(true); findDialog.pack(); if (location == null) { findDialog.setLocationRelativeTo(frame); } else { findDialog.setLocation(location); } } if (target != null) { findDialog.setLocale(target.getLocale()); } getSharedFindPanel().setSearchable(searchable); installFindRemover(target, findDialog); findDialog.setVisible(true); } /** * Returns the shared JXFindPanel. Lazyly creates and configures on * first call. * * @return the shared JXFindPanel */ public JXFindPanel getSharedFindPanel() { if (findPanel == null) { findPanel = createFindPanel(); configureSharedFindPanel(); } else { // JW: temporary hack around #718-swingx // no longer needed with cleanup of hideSharedFindPanel // if (findPanel.getParent() == null) { // SwingUtilities.updateComponentTreeUI(findPanel); // } } return findPanel; } /** * Factory method to create a JXFindPanel. * * @return JXFindPanel */ public JXFindPanel createFindPanel() { return new JXFindPanel(); } /** * Configures the shared FindPanel. This method is * called once after creation of the shared FindPanel. * Subclasses can override to add configuration code.

* * Here: no-op * PRE: findPanel != null. * */ protected void configureSharedFindPanel() { } private JXDialog getDialogForSharedFindPanel() { if (findPanel == null) return null; Window window = SwingUtilities.getWindowAncestor(findPanel); return (window instanceof JXDialog) ? (JXDialog) window : null; } /** * Hides the findPanel's toplevel window and returns its location. * If the dispose is true, the findPanel is removed from its parent * and the toplevel window is disposed. * * @param dispose boolean to indicate whether the findPanels toplevel * window should be disposed. * @return the location of the window if visible, or the last known * location. */ protected Point hideSharedFindPanel(boolean dispose) { if (findPanel == null) return null; Window window = SwingUtilities.getWindowAncestor(findPanel); Point location = lastFindDialogLocation; if (window != null) { // PENDING JW: can't remember why it it removed always? if (window.isVisible()) { location = window.getLocationOnScreen(); window.setVisible(false); } if (dispose) { findPanel.getParent().remove(findPanel); window.dispose(); } } return location; } public class FindRemover implements PropertyChangeListener { KeyboardFocusManager focusManager; Set targets; public FindRemover() { updateManager(); } public void addTarget(Container target) { getTargets().add(target); } public void removeTarget(Container target) { getTargets().remove(target); } private Set getTargets() { if (targets == null) { targets = new HashSet(); } return targets; } private void updateManager() { if (focusManager != null) { focusManager.removePropertyChangeListener("permanentFocusOwner", this); } this.focusManager = KeyboardFocusManager.getCurrentKeyboardFocusManager(); focusManager.addPropertyChangeListener("permanentFocusOwner", this); } @Override public void propertyChange(PropertyChangeEvent ev) { Component c = focusManager.getPermanentFocusOwner(); if (c == null) return; for (Iterator iter = getTargets().iterator(); iter.hasNext();) { Container element = iter.next(); if ((element == c) || (SwingUtilities.isDescendingFrom(c, element))) { return; } } endSearching(); } public void endSearching() { getTargets().clear(); stopSearching(); } } /** * {@inheritDoc} */ @Override public void updateUI() { if (findBar != null) { SwingUtilities.updateComponentTreeUI(findBar); } if (findPanel != null) { SwingUtilities.updateComponentTreeUI(findPanel); } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy