org.openide.awt.ContextAction Maven / Gradle / Ivy
/*
* 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.awt;
import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import java.lang.ref.Reference;
import java.lang.ref.WeakReference;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.function.Supplier;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.Action;
import javax.swing.SwingUtilities;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import org.openide.util.ContextAwareAction;
import org.openide.util.Lookup;
import org.openide.util.Mutex;
import org.openide.util.WeakListeners;
/** A delegate action that is usually associated with a specific lookup and
* listens on certain classes to appear and disappear there.
*/
class ContextAction extends Object
implements Action, ContextAwareAction, ChangeListener, Runnable {
//, Presenter.Menu, Presenter.Popup, Presenter.Toolbar, PropertyChangeListener {
static final Logger LOG = Logger.getLogger(ContextAction.class.getName());
/** type to check */
final Class type;
/** selection mode */
final ContextSelection selectMode;
/** performer to call */
final ContextAction.Performer super T> performer;
/** global lookup to work with */
final ContextManager global;
/** support for listeners */
private PropertyChangeSupport support;
/** was this action enabled or not*/
private boolean previousEnabled;
protected final StatefulMonitor enableMonitor;
/** Constructs new action that is bound to given context and
* listens for changes of ActionMap
in order to delegate
* to right action.
*/
public ContextAction(
ContextAction.Performer super T> performer,
ContextSelection selectMode,
Lookup actionContext,
Class type,
boolean surviveFocusChange, StatefulMonitor enableMonitor
) {
if (performer == null) {
throw new NullPointerException("Has to provide a key!"); // NOI18N
}
this.type = type;
this.selectMode = selectMode;
this.performer = performer;
this.global = ContextManager.findManager(actionContext, surviveFocusChange);
this.enableMonitor = enableMonitor;
if (enableMonitor != null) {
LOG.log(Level.FINE, "Setting enable monitor {0}: {1}", new Object[] {
this, enableMonitor} );
}
}
/** Overrides superclass method, adds delegate description. */
@Override
public String toString() {
return super.toString() + "[type=" + type + ", performer=" + performer + "]"; // NOI18N
}
/** Invoked when an action occurs.
*/
public void actionPerformed(final java.awt.event.ActionEvent e) {
global.actionPerformed(e, performer, type, selectMode);
}
public boolean isEnabled() {
assert EventQueue.isDispatchThread();
boolean r;
if (enableMonitor != null) {
r = fetchEnabledValue();
} else {
r = global.isEnabled(type, selectMode, performer);
}
previousEnabled = r;
return r;
}
private boolean fetchEnabledValue() {
return global.runEnabled(type, selectMode, (all, everything) -> {
Supplier af = () -> (Action)performer.delegate0(everything, all, true);
if (enableMonitor.getType() == Action.class) {
// special case for monitoring the action itself
Action dele = (Action)performer.delegate(everything, all);
// delegate to the action
return enableMonitor.enabled(Collections.singletonList(dele), () -> dele);
} else if (enableMonitor.getType() != type) {
return global.runEnabled(enableMonitor.getType(), selectMode,
(all2, everything2) -> {
// run enable monitor for the other type and the original action
return enableMonitor.enabled(all2, af);
}
);
} else {
return enableMonitor.enabled(all, af);
}
});
}
@Override
public void stateChanged(ChangeEvent e) {
// remap PropertyMonitor change events into EDT
Mutex.EVENT.readAccess(this);
}
@Override
public void run() {
updateStateProperties();
}
@Override
public synchronized void addPropertyChangeListener(PropertyChangeListener listener) {
boolean first = false;
if (support== null) {
support = new PropertyChangeSupport(this);
first = true;
}
support.addPropertyChangeListener(listener);
if (first) {
startListeners();
}
}
@Override
public synchronized void removePropertyChangeListener(PropertyChangeListener listener) {
if( null != support ) {
support.removePropertyChangeListener(listener);
if (!support.hasListeners(null)) {
stopListeners();
support = null;
}
}
}
protected void startListeners() {
performer.startListeners();
global.registerListener(type, this);
if (enableMonitor != null) {
fetchEnabledValue();
enableMonitor.addChangeListener(this);
}
}
protected void stopListeners() {
global.unregisterListener(type, this);
performer.stopListeners();
if (enableMonitor != null) {
enableMonitor.removeChangeListener(this);
}
}
public void putValue(String key, Object o) {
}
public Object getValue(String key) {
if ("enabler".equals(key)) { // NOI18N
// special API to support re-enablement
assert EventQueue.isDispatchThread();
updateState();
} else if (ACTION_COMMAND_KEY.equals(key)) {
Object o = performer.delegate.get(ACTION_COMMAND_KEY);
if (o == null) {
o = performer.delegate.get("key"); // NOI18N
}
if (o != null) {
return o.toString();
}
}
return null;
}
public void setEnabled(boolean b) {
}
void clearState() {
performer.clear();
if (enableMonitor != null) {
enableMonitor.clear();
}
}
/**
* Called from context manager, when the objects watched for in
* Lookup change.
*/
void updateState() {
clearState();
if (!isListening()) {
return;
}
updateStateProperties();
}
void updateStateProperties() {
boolean prev = previousEnabled;
boolean now = isEnabled();
if (prev != now) {
updateEnabledState(now);
}
}
boolean wasEnabled() {
return previousEnabled;
}
protected boolean isListening() {
synchronized (this) {
return support != null;
}
}
protected void firePropertyChange(String property, Boolean old, Boolean current) {
PropertyChangeSupport s;
synchronized (this) {
s = support;
if (s == null) {
return;
}
}
s.firePropertyChange(property, old, current);
}
protected void updateEnabledState(boolean enabled) {
this.previousEnabled = enabled;
firePropertyChange("enabled", !enabled, enabled); // NOI18N
}
/** Clones itself with given context.
*/
public Action createContextAwareInstance(Lookup actionContext) {
return new ContextAction(performer, selectMode, actionContext, type, global.isSurvive(),
enableMonitor == null ? null : enableMonitor.createContextMonitor(actionContext));
}
@Override
public int hashCode() {
return Objects.hash(type, selectMode, performer, enableMonitor);
}
@Override
public boolean equals(Object obj) {
if (obj == this) {
return true;
}
if (obj instanceof ContextAction) {
ContextAction c = (ContextAction)obj;
return type.equals(c.type) &&
selectMode.equals(c.selectMode) &&
performer.equals(c.performer) &&
Objects.equals(enableMonitor, c.enableMonitor);
}
return false;
}
private static final Reference
© 2015 - 2025 Weber Informatics LLC | Privacy Policy