org.openide.util.actions.CookieAction Maven / Gradle / Ivy
The newest version!
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.openide.util.actions;
import org.openide.nodes.Node;
import org.openide.nodes.NodeAdapter;
import org.openide.util.Lookup;
import org.openide.util.Mutex;
import java.beans.PropertyChangeEvent;
import java.lang.ref.*;
import java.util.*;
/** Not the preferred solution anymore, rather use
* Actions.context.
* To replace your action
*
* layer definition use more delarative way:
*
* <file name="action-pkg-ClassName.instance">
* <attr name="type" stringvalue="org.netbeans.api.actions.Openable"/>
* <attr name="delegate" methodvalue="org.openide.awt.Action.inject"/>
* <attr name="selectionType" stringvalue="ANY"/>
* <attr name="injectable" stringvalue="pkg.YourClass"/>
* <attr name="displayName" bundlevalue="your.pkg.Bundle#key"/>
* <attr name="iconBase" stringvalue="your/pkg/YourImage.png"/>
* <!-- if desired: <attr name="noIconInMenu" boolvalue="false"/> -->
* </file>
*
*
* @author Petr Hamernik, Jaroslav Tulach, Dafe Simonek, Jesse Glick
*/
public abstract class CookieAction extends NodeAction {
/** name of property with cookies for this action */
private static final String PROP_COOKIES = "cookies"; // NOI18N
/** Action will be enabled if there are one or more selected nodes
* and there is exactly one node which supports the given cookies. */
public static final int MODE_ONE = 0x01;
/** Action will be enabled if there are several selected nodes
* and some of them (at least one, but not all)
* support the given cookies. */
public static final int MODE_SOME = 0x02;
/** Action will be enabled if there are one or more selected nodes
* and all of them support the given cookies. */
public static final int MODE_ALL = 0x04;
/** Action will be enabled if there is exactly one selected node
* and it supports the given cookies. */
public static final int MODE_EXACTLY_ONE = 0x08;
/** Action will be enabled if there are one or more selected nodes
* and any of them (one, all, or some) support the given cookies. */
public static final int MODE_ANY = 0x07;
// [PENDING] 0x06 should suffice, yes? --jglick
private static final long serialVersionUID = 6031319415908298424L;
private CookiesChangeListener listener = new CookiesChangeListener(this);
/** Get the mode of the action: how strict it should be about
* cookie support.
* @return the mode of the action. Possible values are disjunctions of the MODE_XXX
* constants. */
protected abstract int mode();
/** Get the cookies that this action requires. The cookies are disjunctive, i.e. a node
* must support AT LEAST ONE of the cookies specified by this method.
*
* @return a list of cookies
*/
protected abstract Class>[] cookieClasses(); // might not extend Node.Cookie; seems to work with Lookup
/** Getter for cookies.
* @return the set of cookies for this
*/
private Class>[] getCookies() {
Class[] ret = (Class[]) getProperty(PROP_COOKIES);
if (ret != null) {
return ret;
}
ret = cookieClasses();
putProperty(PROP_COOKIES, ret);
return ret;
}
/** Test for enablement based on the cookies of selected nodes.
* Generally subclasses should not override this except for strange
* purposes, and then only calling the super method and adding a check.
* Just use {@link #cookieClasses} and {@link #mode} to specify
* the enablement logic.
* @param activatedNodes the set of activated nodes
* @return true
to enable
*/
protected boolean enable(Node[] activatedNodes) {
if (activatedNodes.length == 0) {
return false;
}
// sets new nodes to cookie change listener
listener.setNodes(activatedNodes);
// perform enable / disable logic
return doEnable(activatedNodes);
}
/** Implements ContextAwareAction
interface method. */
public javax.swing.Action createContextAwareInstance(Lookup actionContext) {
return new CookieDelegateAction(this, actionContext);
}
/** Helper, actually performs enable / disable logic */
boolean doEnable(Node[] activatedNodes) {
int supported = resolveSupported(activatedNodes);
if (supported == 0) {
return false;
}
int mode = mode();
return
// [PENDING] shouldn't MODE_ONE also say: && supported == 1? --jglick
((mode & MODE_ONE) != 0) || (((mode & MODE_ALL) != 0) && (supported == activatedNodes.length)) ||
(((mode & MODE_EXACTLY_ONE) != 0) && (activatedNodes.length == 1)) ||
(((mode & MODE_SOME) != 0) && (supported < activatedNodes.length));
}
/**
* Implementation of the above method.
*
* @param activatedNodes gives array of actually activated nodes.
* @return number of supported classes
*/
private int resolveSupported(Node[] activatedNodes) {
int ret = 0;
Class>[] cookies = getCookies();
for (Node n : activatedNodes) {
for (Class> cookie : cookies) {
// test for supported cookies
@SuppressWarnings("unchecked")
Lookup.Template> templ = new Lookup.Template<>(cookie);
if (n.getLookup().lookupItem(templ) != null) {
ret++;
break;
}
}
}
return ret;
}
/** Tracks changes of cookie classes in currently selected nodes
*/
private static final class CookiesChangeListener extends NodeAdapter {
/** our weak listener */
private org.openide.nodes.NodeListener listener;
/** The nodes we are currently listening */
private volatile List> nodes;
/** the associated action */
private Reference action;
/** Constructor - asociates with given cookie action
*/
public CookiesChangeListener(CookieAction a) {
listener = org.openide.nodes.NodeOp.weakNodeListener(this, null);
action = new WeakReference(a);
}
/** Sets the nodes to work on */
void setNodes(Node[] newNodes) {
// detach old nodes
List> nodes2 = this.nodes;
if (nodes2 != null) {
detachListeners(nodes2);
}
nodes = null;
// attach to new nodes
if (newNodes != null) {
List> tmp = new ArrayList>(newNodes.length);
for (int i = 0; i < newNodes.length; i++)
tmp.add(new WeakReference(newNodes[i]));
attachListeners(tmp);
this.nodes = tmp;
}
}
/** Removes itself as a listener from given nodes */
void detachListeners(List> nodes) {
if (nodes == null) {
return;
}
Iterator> it = nodes.iterator();
while (it.hasNext()) {
Node node = it.next().get();
if (node != null) {
node.removeNodeListener(listener);
}
}
}
/** Attach itself as a listener to the given nodes */
void attachListeners(List> nodes) {
Iterator> it = nodes.iterator();
while (it.hasNext()) {
Node node = it.next().get();
if (node != null) {
node.addNodeListener(listener);
}
}
}
/** Reacts to the cookie classes change -
* calls enable on asociated action */
public void propertyChange(PropertyChangeEvent ev) {
// filter only cookie classes changes
if (!Node.PROP_COOKIE.equals(ev.getPropertyName())) {
return;
}
// find asociated action
final CookieAction a = action.get();
if (a == null) {
return;
}
List> _nodes = this.nodes;
if (_nodes != null) {
ArrayList nonNullNodes = new ArrayList(_nodes.size());
Iterator> it = _nodes.iterator();
while (it.hasNext()) {
Node node = it.next().get();
if (node != null) {
nonNullNodes.add(node);
} else {
// If there is really a selection, it should not have been collected.
return;
}
}
final Node[] nodes2 = new Node[nonNullNodes.size()];
nonNullNodes.toArray(nodes2);
Mutex.EVENT.writeAccess(
new Runnable() {
public void run() {
a.setEnabled(a.enable(nodes2));
}
}
);
}
}
protected void finalize() {
detachListeners(nodes);
}
}
// end of CookiesChangeListener
/** A delegate action that is usually associated with a specific lookup and
* extract the nodes it operates on from it. Otherwise it delegates to the
* regular NodeAction.
*/
static final class CookieDelegateAction extends org.openide.util.actions.NodeAction.DelegateAction
implements org.openide.nodes.NodeListener, Runnable {
/** our weak listener */
private org.openide.nodes.NodeListener listener;
/** The nodes we are currently listening */
private List> nodes;
public CookieDelegateAction(CookieAction a, Lookup actionContext) {
super(a, actionContext);
listener = org.openide.nodes.NodeOp.weakNodeListener(this, null);
setNodes(nodes());
}
public void resultChanged(org.openide.util.LookupEvent ev) {
setNodes(nodes());
superResultChanged(ev);
}
private void superResultChanged(org.openide.util.LookupEvent ev) {
super.resultChanged(ev);
}
public void childrenAdded(org.openide.nodes.NodeMemberEvent ev) {
}
public void childrenRemoved(org.openide.nodes.NodeMemberEvent ev) {
}
public void childrenReordered(org.openide.nodes.NodeReorderEvent ev) {
}
public void nodeDestroyed(org.openide.nodes.NodeEvent ev) {
}
public void propertyChange(PropertyChangeEvent ev) {
// filter only cookie classes changes
if (!Node.PROP_COOKIE.equals(ev.getPropertyName())) {
return;
}
// find asociated action
Mutex.EVENT.readAccess(this);
}
public void run() {
superResultChanged(null);
}
/** Attach itself as a listener own nodes */
private void setNodes(org.openide.nodes.Node[] newNodes) {
// detach listeners from old nodes
detachListeners(nodes);
// attach to new nodes
if (newNodes != null) {
nodes = new ArrayList>(newNodes.length);
for (int i = 0; i < newNodes.length; i++)
nodes.add(new WeakReference(newNodes[i]));
}
// attach listeners to new nodes
attachListeners(nodes);
}
/** Removes itself as a listener from given nodes */
private void detachListeners(List> nodes) {
if (nodes != null) {
Iterator> it = nodes.iterator();
while (it.hasNext()) {
Node node = it.next().get();
if (node != null) {
node.removeNodeListener(listener);
}
}
}
}
/** Attach itself as a listener to the given nodes */
private void attachListeners(List> nodes) {
if (nodes != null) {
Iterator> it = nodes.iterator();
while (it.hasNext()) {
Node node = it.next().get();
if (node != null) {
node.addNodeListener(listener);
}
}
}
}
protected void finalize() {
detachListeners(nodes);
}
}
// end of CookieDelegateAction
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy