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

org.jdesktop.swingx.AbstractPatternPanel Maven / Gradle / Ivy

There is a newer version: 1.6.5-1
Show newest version
/*
 * $Id: AbstractPatternPanel.java 3927 2011-02-22 16:34:11Z kleopatra $
 *
 * 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;

import java.awt.Dimension;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.Locale;

import javax.swing.Action;
import javax.swing.ActionMap;
import javax.swing.JCheckBox;
import javax.swing.JLabel;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;

import org.jdesktop.swingx.action.AbstractActionExt;
import org.jdesktop.swingx.action.ActionContainerFactory;
import org.jdesktop.swingx.action.BoundAction;
import org.jdesktop.swingx.plaf.LookAndFeelAddons;
import org.jdesktop.swingx.plaf.UIManagerExt;
import org.jdesktop.swingx.search.PatternModel;

/**
 * Common base class of ui clients.
 * 
 * Implements basic synchronization between PatternModel state and
 * actions bound to it.
 * 
 * 
 * 
 * PENDING: extending JXPanel is a convenience measure, should be extracted
 *   into a dedicated controller.
 * PENDING: should be re-visited when swingx goes binding-aware
 * 
 * @author Jeanette Winzenburg
 */
public abstract class AbstractPatternPanel extends JXPanel {

    public static final String SEARCH_FIELD_LABEL = "searchFieldLabel";
    public static final String SEARCH_FIELD_MNEMONIC = SEARCH_FIELD_LABEL + ".mnemonic";
    public static final String SEARCH_TITLE = "searchTitle";
    public static final String MATCH_ACTION_COMMAND = "match";

    static {
        // Hack to enforce loading of SwingX framework ResourceBundle
        LookAndFeelAddons.getAddon();
    }

    protected JLabel searchLabel;
    protected JTextField searchField;
    protected JCheckBox matchCheck;
    
    protected PatternModel patternModel;
    private ActionContainerFactory actionFactory;


//------------------------ actions

    /**
     * Callback action bound to MATCH_ACTION_COMMAND. 
     */
    public abstract void match();
    
    /** 
     * convenience method for type-cast to AbstractActionExt.
     * 
     * @param key Key to retrieve action
     * @return Action bound to this key
     * @see AbstractActionExt
     */
    protected AbstractActionExt getAction(String key) {
        // PENDING: outside clients might add different types?
        return (AbstractActionExt) getActionMap().get(key);
    }

    /**
     * creates and registers all actions for the default the actionMap.
     */
    protected void initActions() {
        initPatternActions();
        initExecutables();
    }
    
    /**
     * creates and registers all "executable" actions.
     * Meaning: the actions bound to a callback method on this.
     * 
     * PENDING: not quite correctly factored? Name?
     *
     */
    protected void initExecutables() {
        Action execute = createBoundAction(MATCH_ACTION_COMMAND, "match");
        getActionMap().put(JXDialog.EXECUTE_ACTION_COMMAND, 
                execute);
        getActionMap().put(MATCH_ACTION_COMMAND, execute);
        refreshEmptyFromModel();
    }
    
    /**
     * creates actions bound to PatternModel's state.
     */
    protected void initPatternActions() {
        ActionMap map = getActionMap();
        map.put(PatternModel.MATCH_CASE_ACTION_COMMAND, 
                createModelStateAction(PatternModel.MATCH_CASE_ACTION_COMMAND, 
                        "setCaseSensitive", getPatternModel().isCaseSensitive()));
        map.put(PatternModel.MATCH_WRAP_ACTION_COMMAND, 
                createModelStateAction(PatternModel.MATCH_WRAP_ACTION_COMMAND, 
                        "setWrapping", getPatternModel().isWrapping()));
        map.put(PatternModel.MATCH_BACKWARDS_ACTION_COMMAND, 
                createModelStateAction(PatternModel.MATCH_BACKWARDS_ACTION_COMMAND, 
                        "setBackwards", getPatternModel().isBackwards()));
        map.put(PatternModel.MATCH_INCREMENTAL_ACTION_COMMAND, 
                createModelStateAction(PatternModel.MATCH_INCREMENTAL_ACTION_COMMAND, 
                        "setIncremental", getPatternModel().isIncremental()));
    }

    /**
     * Returns a potentially localized value from the UIManager. The given key
     * is prefixed by this component|s UIPREFIX before doing the
     * lookup. The lookup respects this table's current locale
     * property. Returns the key, if no value is found.
     * 
     * @param key the bare key to look up in the UIManager.
     * @return the value mapped to UIPREFIX + key or key if no value is found.
     */
    protected String getUIString(String key) {
        return getUIString(key, getLocale());
    }

    /**
     * Returns a potentially localized value from the UIManager for the 
     * given locale. The given key
     * is prefixed by this component's UIPREFIX before doing the
     * lookup. Returns the key, if no value is found.
     * 
     * @param key the bare key to look up in the UIManager.
     * @param locale the locale use for lookup
     * @return the value mapped to UIPREFIX + key in the given locale,
     *    or key if no value is found.
     */
    protected String getUIString(String key, Locale locale) {
        String text = UIManagerExt.getString(PatternModel.SEARCH_PREFIX + key, locale);
        return text != null ? text : key;
    }


    /**
     * creates, configures and returns a bound state action on a boolean property
     * of the PatternModel.
     * 
     * @param command the actionCommand - same as key to find localizable resources
     * @param methodName the method on the PatternModel to call on item state changed
     * @param initial the initial value of the property
     * @return newly created action
     */
    protected AbstractActionExt createModelStateAction(String command, String methodName, boolean initial) {
        String actionName = getUIString(command);
        BoundAction action = new BoundAction(actionName,
                command);
        action.setStateAction();
        action.registerCallback(getPatternModel(), methodName);
        action.setSelected(initial);
        return action;
    }

    /**
     * creates, configures and returns a bound action to the given method of 
     * this.
     * 
     * @param actionCommand the actionCommand, same as key to find localizable resources
     * @param methodName the method to call an actionPerformed.
     * @return newly created action
     */
    protected AbstractActionExt createBoundAction(String actionCommand, String methodName) {
        String actionName = getUIString(actionCommand);
        BoundAction action = new BoundAction(actionName,
                actionCommand);
        action.registerCallback(this, methodName);
        return action;
    }

//------------------------ dynamic locale support
    

    /**
     * {@inheritDoc} 

* Overridden to update locale-dependent properties. * * @see #updateLocaleState(Locale) */ @Override public void setLocale(Locale l) { updateLocaleState(l); super.setLocale(l); } /** * Updates locale-dependent state. * * Here: updates registered column actions' locale-dependent state. *

* * PENDING: Try better to find all column actions including custom * additions? Or move to columnControl? * * @see #setLocale(Locale) */ protected void updateLocaleState(Locale locale) { for (Object key : getActionMap().allKeys()) { if (key instanceof String) { String keyString = getUIString((String) key, locale); if (!key.equals(keyString)) { getActionMap().get(key).putValue(Action.NAME, keyString); } } } bindSearchLabel(locale); } //---------------------- synch patternModel <--> components /** * called from listening to pattern property of PatternModel. * * This implementation calls match() if the model is in * incremental state. * */ protected void refreshPatternFromModel() { if (getPatternModel().isIncremental()) { match(); } } /** * returns the patternModel. Lazyly creates and registers a * propertyChangeListener if null. * * @return current PatternModel if it exists or newly created * one if it was not initialized before this call */ protected PatternModel getPatternModel() { if (patternModel == null) { patternModel = createPatternModel(); patternModel.addPropertyChangeListener(getPatternModelListener()); } return patternModel; } /** * factory method to create the PatternModel. * Hook for subclasses to install custom models. * * @return newly created PatternModel */ protected PatternModel createPatternModel() { return new PatternModel(); } /** * creates and returns a PropertyChangeListener to the PatternModel. * * NOTE: the patternModel is totally under control of this class - currently * there's no need to keep a reference to the listener. * * @return created and bound to appropriate callback methods * PropertyChangeListener */ protected PropertyChangeListener getPatternModelListener() { return new PropertyChangeListener() { @Override public void propertyChange(PropertyChangeEvent evt) { String property = evt.getPropertyName(); if ("pattern".equals(property)) { refreshPatternFromModel(); } else if ("rawText".equals(property)) { refreshDocumentFromModel(); } else if ("caseSensitive".equals(property)){ getAction(PatternModel.MATCH_CASE_ACTION_COMMAND). setSelected(((Boolean) evt.getNewValue()).booleanValue()); } else if ("wrapping".equals(property)) { getAction(PatternModel.MATCH_WRAP_ACTION_COMMAND). setSelected(((Boolean) evt.getNewValue()).booleanValue()); } else if ("backwards".equals(property)) { getAction(PatternModel.MATCH_BACKWARDS_ACTION_COMMAND). setSelected(((Boolean) evt.getNewValue()).booleanValue()); } else if ("incremental".equals(property)) { getAction(PatternModel.MATCH_INCREMENTAL_ACTION_COMMAND). setSelected(((Boolean) evt.getNewValue()).booleanValue()); } else if ("empty".equals(property)) { refreshEmptyFromModel(); } } }; } /** * called from listening to empty property of PatternModel. * * this implementation synch's the enabled state of the action with * MATCH_ACTION_COMMAND to !empty. * */ protected void refreshEmptyFromModel() { boolean enabled = !getPatternModel().isEmpty(); getAction(MATCH_ACTION_COMMAND).setEnabled(enabled); } /** * callback method from listening to searchField. * */ protected void refreshModelFromDocument() { getPatternModel().setRawText(searchField.getText()); } /** * callback method that updates document from the search field * */ protected void refreshDocumentFromModel() { if (searchField.getText().equals(getPatternModel().getRawText())) return; SwingUtilities.invokeLater(new Runnable() { @Override public void run() { searchField.setText(getPatternModel().getRawText()); } }); } /** * Create DocumentListener for the search field that calls * corresponding callback method whenever the search field contents is being changed * * @return newly created DocumentListener */ protected DocumentListener getSearchFieldListener() { return new DocumentListener() { @Override public void changedUpdate(DocumentEvent ev) { // JW - really?? we've a PlainDoc without Attributes refreshModelFromDocument(); } @Override public void insertUpdate(DocumentEvent ev) { refreshModelFromDocument(); } @Override public void removeUpdate(DocumentEvent ev) { refreshModelFromDocument(); } }; } //-------------------------- config helpers /** * configure and bind components to/from PatternModel */ protected void bind() { bindSearchLabel(getLocale()); searchField.getDocument().addDocumentListener(getSearchFieldListener()); getActionContainerFactory().configureButton(matchCheck, (AbstractActionExt) getActionMap().get(PatternModel.MATCH_CASE_ACTION_COMMAND), null); } /** * Configures the searchLabel. * Here: sets text and mnenomic properties form ui values, * configures as label for searchField. */ protected void bindSearchLabel(Locale locale) { searchLabel.setText(getUIString(SEARCH_FIELD_LABEL, locale)); String mnemonic = getUIString(SEARCH_FIELD_MNEMONIC, locale); if (mnemonic != SEARCH_FIELD_MNEMONIC) { searchLabel.setDisplayedMnemonic(mnemonic.charAt(0)); } searchLabel.setLabelFor(searchField); } /** * @return current ActionContainerFactory. * Will lazily create new factory if it does not exist */ protected ActionContainerFactory getActionContainerFactory() { if (actionFactory == null) { actionFactory = new ActionContainerFactory(null); } return actionFactory; } /** * Initialize all the incorporated components and models */ protected void initComponents() { searchLabel = new JLabel(); searchField = new JTextField(getSearchFieldWidth()) { @Override public Dimension getMaximumSize() { Dimension superMax = super.getMaximumSize(); superMax.height = getPreferredSize().height; return superMax; } }; matchCheck = new JCheckBox(); } /** * @return width in characters of the search field */ protected int getSearchFieldWidth() { return 15; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy