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

org.jdesktop.application.ActionManager Maven / Gradle / Ivy

There is a newer version: 1.9.2
Show newest version

/*
 * Copyright (C) 2006 Sun Microsystems, Inc. All rights reserved. Use is
 * subject to license terms.
 */
package org.jdesktop.application;

import java.awt.KeyboardFocusManager;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.WeakHashMap;
import java.util.logging.Logger;
import javax.swing.ActionMap;
import javax.swing.JComponent;

/**
 * The application's {@code ActionManager} provides read-only cached
 * access to {@code ActionMaps} that contain one entry for each method
 * marked with the {@code @Action} annotation in a class.
 * 
 * 
 * @see ApplicationContext#getActionMap(Object)
 * @see ApplicationActionMap
 * @see ApplicationAction
 * @author Hans Muller ([email protected])
 */
public class ActionManager extends AbstractBean {

    private static final Logger logger = Logger.getLogger(ActionManager.class.getName());
    private final ApplicationContext context;
    private final WeakHashMap> actionMaps;
    private ApplicationActionMap globalActionMap = null;

    protected ActionManager(ApplicationContext context) {
        if (context == null) {
            throw new IllegalArgumentException("null context");
        }
        this.context = context;
        actionMaps = new WeakHashMap>();
    }

    protected final ApplicationContext getContext() {
        return context;
    }

    private ApplicationActionMap createActionMapChain(
            Class startClass, Class stopClass, Object actionsObject, ResourceMap resourceMap) {
        // All of the classes from stopClass to startClass, inclusive. 
        List classes = new ArrayList();
        for (Class c = startClass;; c = c.getSuperclass()) {
            classes.add(c);
            if (c.equals(stopClass)) {
                break;
            }
        }
        Collections.reverse(classes);
        // Create the ActionMap chain, one per class
        ApplicationContext ctx = getContext();
        ApplicationActionMap parent = null;
        for (Class cls : classes) {
            ApplicationActionMap appAM = new ApplicationActionMap(ctx, cls, actionsObject, resourceMap);
            appAM.setParent(parent);
            parent = appAM;
        }
        return parent;
    }

    /** 
     * The {@code ActionMap} chain for the entire {@code Application}.
     * 

* Returns an {@code ActionMap} with the {@code @Actions} defined * in the application's {@code Application} subclass, i.e. the * the value of: *

     * ApplicationContext.getInstance().getApplicationClass()
     * 
* The remainder of the chain contains one {@code ActionMap} * for each superclass, up to {@code Application.class}. The * {@code ActionMap.get()} method searches the entire chain, so * logically, the {@code ActionMap} that this method returns contains * all of the application-global actions. *

* The value returned by this method is cached. * * @return the {@code ActionMap} chain for the entire {@code Application}. * @see #getActionMap(Class, Object) * @see ApplicationContext#getActionMap() * @see ApplicationContext#getActionMap(Class, Object) * @see ApplicationContext#getActionMap(Object) */ public ApplicationActionMap getActionMap() { if (globalActionMap == null) { ApplicationContext ctx = getContext(); Object appObject = ctx.getApplication(); Class appClass = ctx.getApplicationClass(); ResourceMap resourceMap = ctx.getResourceMap(); globalActionMap = createActionMapChain(appClass, Application.class, appObject, resourceMap); initProxyActionSupport(); // lazy initialization } return globalActionMap; } private void initProxyActionSupport() { KeyboardFocusManager kfm = KeyboardFocusManager.getCurrentKeyboardFocusManager(); kfm.addPropertyChangeListener(new KeyboardFocusPCL()); } /** * Returns the {@code ApplicationActionMap} chain for the specified * actions class and target object. *

* The specified class can contain methods marked with * the {@code @Action} annotation. Each one will be turned * into an {@link ApplicationAction ApplicationAction} object * and all of them will be added to a single * {@link ApplicationActionMap ApplicationActionMap}. All of the * {@code ApplicationActions} invoke their {@code actionPerformed} * method on the specified {@code actionsObject}. * The parent of the returned {@code ActionMap} is the global * {@code ActionMap} that contains the {@code @Actions} defined * in this application's {@code Application} subclass. * *

* To bind an {@code @Action} to a Swing component, one specifies * the {@code @Action's} name in an expression like this: *

     * ApplicationContext ctx = Application.getInstance(MyApplication.class).getContext();
     * MyActions myActions = new MyActions();
     * myComponent.setAction(ac.getActionMap(myActions).get("myAction"));
     * 
* *

* The value returned by this method is cached. The lifetime of * the cached entry will be the same as the lifetime of the {@code * actionsObject} and the {@code ApplicationActionMap} and {@code * ApplicationActions} that refer to it. In other words, if you * drop all references to the {@code actionsObject}, including * its {@code ApplicationActions} and their {@code * ApplicationActionMaps}, then the cached {@code ActionMap} entry * will be cleared. * * @param actionsClass * @param actionsObject * @see #getActionMap() * @see ApplicationContext#getActionMap() * @see ApplicationContext#getActionMap(Class, Object) * @see ApplicationContext#getActionMap(Object) * @return the {@code ApplicationActionMap} for {@code actionsClass} and {@code actionsObject} */ public ApplicationActionMap getActionMap(Class actionsClass, Object actionsObject) { if (actionsClass == null) { throw new IllegalArgumentException("null actionsClass"); } if (actionsObject == null) { throw new IllegalArgumentException("null actionsObject"); } if (!actionsClass.isAssignableFrom(actionsObject.getClass())) { throw new IllegalArgumentException("actionsObject not instanceof actionsClass"); } synchronized (actionMaps) { WeakReference ref = actionMaps.get(actionsObject); ApplicationActionMap classActionMap = (ref != null) ? ref.get() : null; if ((classActionMap == null) || (classActionMap.getActionsClass() != actionsClass)) { ApplicationContext ctx = getContext(); Class actionsObjectClass = actionsObject.getClass(); ResourceMap resourceMap = ctx.getResourceMap(actionsObjectClass, actionsClass); classActionMap = createActionMapChain(actionsObjectClass, actionsClass, actionsObject, resourceMap); ActionMap lastActionMap = classActionMap; while (lastActionMap.getParent() != null) { lastActionMap = lastActionMap.getParent(); } lastActionMap.setParent(getActionMap()); actionMaps.put(actionsObject, new WeakReference(classActionMap)); } return classActionMap; } } private final class KeyboardFocusPCL implements PropertyChangeListener { private final TextActions textActions; KeyboardFocusPCL() { textActions = new TextActions(getContext()); } @Override public void propertyChange(PropertyChangeEvent e) { if ("permanentFocusOwner".equals(e.getPropertyName())) { JComponent oldOwner = getContext().getFocusOwner(); Object newValue = e.getNewValue(); JComponent newOwner = (newValue instanceof JComponent) ? (JComponent) newValue : null; textActions.updateFocusOwner(oldOwner, newOwner); getContext().setFocusOwner(newOwner); updateAllProxyActions(oldOwner, newOwner); } } } /* For each proxyAction in each ApplicationActionMap, if * the newFocusOwner's ActionMap includes an Action with the same * name then bind the proxyAction to it, otherwise set the proxyAction's * proxyBinding to null. [TBD: synchronize access to actionMaps] */ private void updateAllProxyActions(JComponent oldFocusOwner, JComponent newFocusOwner) { if (newFocusOwner != null) { ActionMap ownerActionMap = newFocusOwner.getActionMap(); if (ownerActionMap != null) { updateProxyActions(getActionMap(), ownerActionMap, newFocusOwner); for (WeakReference appAMRef : actionMaps.values()) { ApplicationActionMap appAM = appAMRef.get(); if (appAM == null) { continue; } updateProxyActions(appAM, ownerActionMap, newFocusOwner); } } } } /* For each proxyAction in appAM: if there's an action with the same * name in the focusOwner's ActionMap, then set the proxyAction's proxy * to the matching Action. In other words: calls to the proxyAction * (actionPerformed) will delegate to the matching Action. */ private void updateProxyActions(ApplicationActionMap appAM, ActionMap ownerActionMap, JComponent focusOwner) { for (ApplicationAction proxyAction : appAM.getProxyActions()) { String proxyActionName = proxyAction.getName(); javax.swing.Action proxy = ownerActionMap.get(proxyActionName); if (proxy != null) { proxyAction.setProxy(proxy); proxyAction.setProxySource(focusOwner); } else { proxyAction.setProxy(null); proxyAction.setProxySource(null); } } } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy