org.openide.awt.AlwaysEnabledAction 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.Image;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.net.URL;
import java.util.Collection;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.prefs.BackingStoreException;
import java.util.prefs.PreferenceChangeEvent;
import java.util.prefs.PreferenceChangeListener;
import java.util.prefs.Preferences;
import javax.swing.AbstractAction;
import javax.swing.AbstractButton;
import javax.swing.Action;
import javax.swing.Icon;
import javax.swing.JCheckBoxMenuItem;
import javax.swing.JMenuItem;
import javax.swing.JToggleButton;
import org.openide.util.ContextAwareAction;
import org.openide.util.ImageUtilities;
import org.openide.util.Lookup;
import org.openide.util.LookupEvent;
import org.openide.util.LookupListener;
import org.openide.util.NbPreferences;
import org.openide.util.Utilities;
import org.openide.util.WeakSet;
import org.openide.util.WeakListeners;
import org.openide.util.actions.Presenter;
import org.openide.util.actions.ActionInvoker;
/** Lazily initialized always enabled action
*
* @author Jaroslav Tulach <[email protected]>
*/
class AlwaysEnabledAction extends AbstractAction
implements PropertyChangeListener, ContextAwareAction {
// -J-Dorg.openide.awt.AlwaysEnabledAction.level=FINE
private static final Logger LOG = Logger.getLogger(AlwaysEnabledAction.class.getName());
private static final String PREFERENCES_NODE = "preferencesNode"; // NOI18N
private static final String PREFERENCES_KEY = "preferencesKey"; // NOI18N
private static final String PREFERENCES_DEFAULT = "preferencesDefault"; // NOI18N
static AlwaysEnabledAction create(Map m) {
return (m.containsKey(PREFERENCES_KEY)) ? new CheckBox(m) : new AlwaysEnabledAction(m);
}
final Map map;
private final AlwaysEnabledAction parent;
private PropertyChangeListener weakL;
ActionListener delegate;
final Lookup context;
final Object equals;
public AlwaysEnabledAction(Map m) {
super();
this.map = m;
this.context = null;
this.equals = this;
parent = null;
}
AlwaysEnabledAction(Map m, AlwaysEnabledAction parent, Lookup context, Object equals) {
super();
this.map = m;
this.parent = parent;
this.context = context;
this.equals = equals;
}
private static ActionListener bindToContext(ActionListener a, Lookup context) {
if (context != null) {
if (a instanceof ContextAwareAction) {
return ((ContextAwareAction)a).createContextAwareInstance(context);
}
}
return a;
}
protected ActionListener getDelegate() {
if (delegate == null) {
ActionListener al;
if (parent == null) {
Object listener = map.get("delegate"); // NOI18N
if (!(listener instanceof ActionListener)) {
throw new NullPointerException("No 'delegate' in " + map);
}
al = (ActionListener) listener;
} else {
al = parent.getDelegate();
}
delegate = bindToContext(al, context);
if (delegate instanceof Action) {
Action actionDelegate = (Action) delegate;
if (weakL == null) {
weakL = WeakListeners.propertyChange(this, actionDelegate);
}
actionDelegate.addPropertyChangeListener(weakL);
// Ensure display names and other properties are in sync or propagate them
syncActionDelegateProperty(Action.NAME, actionDelegate);
}
}
return delegate;
}
private void syncActionDelegateProperty(String propertyName, Action actionDelegate) {
Object value = extractCommonAttribute(map, propertyName);
Object delegateValue = actionDelegate.getValue(propertyName);
if (value != null) {
if (delegateValue == null) {
actionDelegate.putValue(propertyName, value);
} else {
if (!delegateValue.equals(value)) { // Values differ
LOG.log(Level.FINE, "Value of property \"{0}\" of AlwaysEnabledAction " +
"is \"{1}\" but delegate {2} has \"{3}\"",
new Object[] {propertyName, value, delegate, delegateValue});
}
}
} // else either both values are null or
// this has null and delegate has non-null which is probably fine (declarer does not care)
}
@Override
public boolean isEnabled() {
// assert EventQueue.isDispatchThread();
if (delegate instanceof Action) {
return ((Action)delegate).isEnabled();
}
return true;
}
public void actionPerformed(final ActionEvent e) {
assert EventQueue.isDispatchThread();
if (getDelegate() instanceof Action) {
if (!((Action)getDelegate()).isEnabled()) {
Utilities.disabledActionBeep();
// Do not fire newValue == null (see #165838)
firePropertyChange("enabled", null, isEnabled()); // NOI18N
return;
}
}
boolean async = Boolean.TRUE.equals(map.get("asynchronous")); // NOI18N
Runnable ar = new Runnable() {
public void run() {
getDelegate().actionPerformed(e);
}
};
ActionInvoker.invokeAction(this, e, async, ar);
}
@Override
public Object getValue(String name) {
if (delegate instanceof Action) {
Object ret = ((Action)delegate).getValue(name);
if (ret != null) {
return ret;
}
if (
"iconBase".equals(name) && // NOI18N
((Action)delegate).getValue(Action.SMALL_ICON) != null
) {
return null;
}
}
Object o = extractCommonAttribute(map, name);
// cf. #137709 JG18:
return o != null ? o : super.getValue(name);
}
static final Object extractCommonAttribute(Map fo, String name) {
try {
if (Action.NAME.equals(name)) {
String actionName = (String) fo.get("displayName"); // NOI18N
// NOI18N
//return Actions.cutAmpersand(actionName);
return actionName;
}
if (Action.MNEMONIC_KEY.equals(name)) {
String actionName = (String) fo.get("displayName"); // NOI18N
if( null == actionName )
return null;
// NOI18N
int position = Mnemonics.findMnemonicAmpersand(actionName);
if (position == -1) {
return null;
} else {
// #167996: copied from AbstractButton.setMnemonic
int vk = (int) actionName.charAt(position + 1);
if(vk >= 'a' && vk <='z') { //NOI18N
vk -= ('a' - 'A'); //NOI18N
}
return vk;
}
}
if (Action.SMALL_ICON.equals(name)) {
Object icon = fo == null ? null : fo.get("iconBase"); // NOI18N
if (icon instanceof Icon) {
return (Icon) icon;
}
if (icon instanceof URL) {
icon = Toolkit.getDefaultToolkit().getImage((URL)icon);
}
if (icon instanceof Image) {
return ImageUtilities.image2Icon((Image)icon);
}
if (icon instanceof String) {
return ImageUtilities.loadImageIcon((String)icon, true);
}
}
if ("iconBase".equals(name)) { // NOI18N
return fo == null ? null : fo.get("iconBase"); // NOI18N
}
if ("noIconInMenu".equals(name)) { // NOI18N
return fo == null ? null : fo.get("noIconInMenu"); // NOI18N
}
// Delegate query to other properties to "fo" ignoring special properties
if (!"delegate".equals(name) && !"instanceCreate".equals(name)) {
return fo == null ? null : fo.get(name);
}
} catch (RuntimeException x) { // noted in #172103
LOG.log(Level.WARNING, "Could not get action attribute " + name, x);
}
return null;
}
@Override
public int hashCode() {
if (equals == this) {
return super.hashCode();
}
return equals.hashCode();
}
@Override
public boolean equals(Object obj) {
if (obj == this) {
return true;
}
if (obj instanceof AlwaysEnabledAction) {
final AlwaysEnabledAction other = (AlwaysEnabledAction) obj;
if (this == this.equals && other == other.equals) {
return (this == other);
}
if (this.equals.equals(other.equals)) {
return true;
}
}
return false;
}
@Override
public String toString() {
return "AlwaysEnabledAction[" + getValue(Action.NAME) + "]"; // NOI18N
}
public void propertyChange(PropertyChangeEvent evt) {
if (evt.getSource() == delegate) {
firePropertyChange(evt.getPropertyName(), evt.getOldValue(), evt.getNewValue());
}
}
public Action createContextAwareInstance(Lookup actionContext) {
return new AlwaysEnabledAction(map, this, actionContext, equals);
}
static final class CheckBox extends AlwaysEnabledAction
implements Presenter.Menu, Presenter.Popup, Presenter.Toolbar, PreferenceChangeListener, LookupListener
{
private static final long serialVersionUID = 1L;
private static final ActionListener EMPTY = new ActionListener() {
public void actionPerformed(ActionEvent ae) {
// Do nothing
}
};
private JCheckBoxMenuItem menuItem;
private JCheckBoxMenuItem popupItem;
private WeakSet toolbarItems;
private Preferences preferencesNode;
private Lookup.Result preferencesNodeResult;
private boolean prefsListening;
CheckBox(Map m) {
super(m);
}
CheckBox(Map m, AlwaysEnabledAction parent, Lookup context, Object equals) {
super(m, parent, context, equals);
}
@Override
public void actionPerformed(ActionEvent e) {
// Toggle state in preferences
togglePreferencesSelected();
super.actionPerformed(e);
}
public JMenuItem getMenuPresenter() {
if (menuItem == null) {
menuItem = new JCheckBoxMenuItem();
menuItem.setSelected(isPreferencesSelected());
Actions.connect(menuItem, this, false);
}
return menuItem;
}
public JMenuItem getPopupPresenter() {
if (popupItem == null) {
popupItem = new JCheckBoxMenuItem();
popupItem.setSelected(isPreferencesSelected());
Actions.connect(popupItem, this, true);
}
return popupItem;
}
public AbstractButton getToolbarPresenter() {
if(toolbarItems == null) {
toolbarItems = new WeakSet(4);
}
AbstractButton b = new DefaultIconToggleButton();
toolbarItems.add(b);
b.setSelected(isPreferencesSelected());
Actions.connect(b, this);
return b;
}
public void preferenceChange(PreferenceChangeEvent pce) {
updateItemsSelected();
}
@Override
protected ActionListener getDelegate() {
return EMPTY;
}
@Override
public Action createContextAwareInstance(Lookup actionContext) {
return new CheckBox(map, this, actionContext, equals);
}
boolean isPreferencesSelected() {
String key = (String) getValue(PREFERENCES_KEY);
Preferences prefs = prefs();
boolean value;
if (key != null && prefs != null) {
Object defaultValue = getValue(PREFERENCES_DEFAULT);
value = prefs.getBoolean(key, defaultValue instanceof Boolean ? (Boolean) defaultValue : false);
synchronized (this) {
if (!prefsListening) {
prefsListening = true;
prefs.addPreferenceChangeListener(this);
}
}
} else {
value = false;
}
return value;
}
private void updateItemsSelected() {
boolean selected = isPreferencesSelected();
if (menuItem != null) {
menuItem.setSelected(selected);
}
if (popupItem != null) {
popupItem.setSelected(selected);
}
if (toolbarItems != null) {
for(AbstractButton b : toolbarItems) {
b.setSelected(selected);
}
}
}
private synchronized Preferences prefs() {
if (preferencesNode == null) {
Object prefsNodeOrLookup = getValue(PREFERENCES_NODE);
if (prefsNodeOrLookup instanceof String) {
String nodeName = (String) prefsNodeOrLookup;
if (nodeName.startsWith("system:")) {
preferencesNode = Preferences.systemRoot();
if (preferencesNode != null) {
nodeName = nodeName.substring("system:".length());
try {
preferencesNode = preferencesNode.nodeExists(nodeName) ? preferencesNode.node(nodeName) : null;
} catch (BackingStoreException ex) {
preferencesNode = null;
}
}
} else if (nodeName.startsWith("user:")) {
preferencesNode = Preferences.userRoot();
if (preferencesNode != null) {
nodeName = nodeName.substring("user:".length());
try {
preferencesNode = preferencesNode.nodeExists(nodeName) ? preferencesNode.node(nodeName) : null;
} catch (BackingStoreException ex) {
preferencesNode = null;
}
}
} else {
preferencesNode = NbPreferences.root();
if (preferencesNode != null) {
try {
preferencesNode = preferencesNode.nodeExists(nodeName) ? preferencesNode.node(nodeName) : null;
} catch (BackingStoreException ex) {
preferencesNode = null;
}
}
}
} else if (prefsNodeOrLookup instanceof Preferences) {
preferencesNode = (Preferences) prefsNodeOrLookup;
} else if (prefsNodeOrLookup instanceof Lookup) {
Lookup prefsLookup = (Lookup) prefsNodeOrLookup;
preferencesNodeResult = prefsLookup.lookupResult(Preferences.class);
Collection extends Preferences> instances = preferencesNodeResult.allInstances();
if (instances.size() > 0) {
preferencesNode = instances.iterator().next();
preferencesNodeResult.addLookupListener(this);
}
return prefsLookup.lookup(Preferences.class);
} else {
preferencesNode = null;
}
}
return preferencesNode;
}
public void resultChanged(LookupEvent ev) {
preferencesNode = null;
preferencesNodeResult = null;
updateItemsSelected();
}
private void togglePreferencesSelected() {
String key = (String) getValue(PREFERENCES_KEY);
Preferences prefs = prefs();
if (key != null && prefs != null) {
Object defaultValue = getValue(PREFERENCES_DEFAULT);
prefs.putBoolean(key, !prefs.getBoolean(key, defaultValue instanceof Boolean ? (Boolean) defaultValue : false));
}
}
}
/**
* A button that provides a default icon when no text and no custom icon have been set.
* Copied from Toolbar.java and made a toggle button.
*/
static class DefaultIconToggleButton extends JToggleButton {
private Icon unknownIcon;
@Override
public Icon getIcon() {
Icon retValue = super.getIcon();
if( null == retValue && (null == getText() || getText().isEmpty()) ) {
if (unknownIcon == null) {
unknownIcon = ImageUtilities.loadImageIcon("org/openide/awt/resources/unknown.gif", false); //NOI18N
//unknownIcon = ImageUtilities.loadImageIcon("org/openide/loaders/unknown.gif", false); //NOI18N
}
retValue = unknownIcon;
}
return retValue;
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy