org.openide.actions.UndoRedoAction 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.actions;
import java.awt.EventQueue;
import java.awt.HeadlessException;
import java.awt.event.ActionEvent;
import javax.swing.Action;
import org.openide.awt.UndoRedo;
import org.openide.util.HelpCtx;
import org.openide.util.LookupEvent;
import org.openide.util.NbBundle;
import org.openide.windows.TopComponent;
import org.openide.windows.TopComponent.Registry;
import org.openide.windows.WindowManager;
import java.beans.*;
import java.util.Map;
import java.util.MissingResourceException;
import java.util.logging.Logger;
import java.util.logging.Level;
import javax.swing.AbstractAction;
import javax.swing.JOptionPane;
import javax.swing.UIManager;
import javax.swing.event.*;
import javax.swing.undo.*;
import org.openide.util.ContextAwareAction;
import org.openide.util.Exceptions;
import org.openide.util.ImageUtilities;
import org.openide.util.Lookup;
import org.openide.util.LookupListener;
import org.openide.util.Utilities;
import org.openide.util.WeakListeners;
/** Context aware undo and redo actions.
*
* @author Jaroslav Tulach
*/
final class UndoRedoAction extends AbstractAction
implements ContextAwareAction, PropertyChangeListener, ChangeListener, LookupListener, Runnable, HelpCtx.Provider {
private static final Logger LOG = Logger.getLogger(UndoRedoAction.class.getName());
/** last edit */
private UndoRedo last = UndoRedo.NONE;
private final boolean doUndo;
private final Lookup.Result result;
private final boolean fallback;
private PropertyChangeListener weakPCL;
private ChangeListener weakCL;
private LookupListener weakLL;
UndoRedoAction(Lookup context, boolean doUndo, boolean fallback) {
this.doUndo = doUndo;
this.fallback = fallback;
this.result = context.lookupResult(UndoRedo.Provider.class);
}
@Override
public String toString() {
return super.toString() + "[undo=" + doUndo + ", fallback: " + fallback + "]"; // NOI18N
}
public static Action create(Map,?> map) {
if (Boolean.TRUE.equals(map.get("redo"))) { // NOI18N
return new UndoRedoAction(Utilities.actionsGlobalContext(), false, true);
}
if (Boolean.TRUE.equals(map.get("undo"))) { // NOI18N
return new UndoRedoAction(Utilities.actionsGlobalContext(), true, true);
}
throw new IllegalStateException();
}
@Override
public boolean isEnabled() {
initializeUndoRedo();
return super.isEnabled();
}
void initializeUndoRedo() {
assert EventQueue.isDispatchThread();
if (weakLL != null) {
return;
}
String res;
if (doUndo) {
res = "org/openide/resources/actions/undo.gif"; // NOI18N
} else {
res = "org/openide/resources/actions/redo.gif"; // NOI18N
}
putValue("iconBase", res); // NOI18N
putValue(SMALL_ICON, ImageUtilities.loadImageIcon(res, true));
if (fallback) {
Registry r = WindowManager.getDefault().getRegistry();
weakPCL = WeakListeners.propertyChange(this, r);
r.addPropertyChangeListener(weakPCL);
}
weakCL = WeakListeners.change(this, null);
weakLL = WeakListeners.create(LookupListener.class, this, result);
result.addLookupListener(weakLL);
last = UndoRedo.NONE;
run();
}
@Override
public void run() {
if (!EventQueue.isDispatchThread()) {
EventQueue.invokeLater(this);
return;
}
UndoRedo ur = getUndoRedo();
last.removeChangeListener(weakCL);
if (doUndo) {
setEnabled(ur.canUndo());
} else {
setEnabled(ur.canRedo());
}
putValue(NAME, getName());
last = ur;
last.addChangeListener(weakCL);
}
private UndoRedo getUndoRedo() {
assert EventQueue.isDispatchThread();
for (UndoRedo.Provider provider : result.allInstances()) {
UndoRedo ur = provider.getUndoRedo();
if (ur != null) {
return ur;
}
}
if (fallback) {
TopComponent el = WindowManager.getDefault().getRegistry().getActivated();
if (el != null) {
UndoRedo ur = el.getUndoRedo();
if (ur != null) {
return ur;
}
}
}
return UndoRedo.NONE;
}
private String getName() {
assert EventQueue.isDispatchThread();
//#40823 related. AbstractUndoableEdit prepends "Undo/Redo" strings before the custom text,
// resulting in repetitive text in UndoAction/RedoAction. attempt to remove the AbstractUndoableEdit text
// keeping our text because it has mnemonics.
String undo = doUndo ? getUndoRedo().getUndoPresentationName() : getUndoRedo().getRedoPresentationName();
LOG.log (Level.FINE, doUndo ? "getUndoRedo().getUndoPresentationName() returns {0}" :
"getUndoRedo().getRedoPresentationName() returns {0}", undo);
if ((undo != null) && (getDefaultSwingText() != null) && undo.startsWith(getDefaultSwingText())) {
undo = undo.substring(getDefaultSwingText().length()).trim();
}
LOG.log (Level.FINE, "Name adapted by SWING_DEFAULT_LABEL is {0}", undo);
String presentationName = null;
if (undo == null || undo.trim ().length () == 0) {
presentationName = NbBundle.getMessage(UndoRedoAction.class, doUndo ? "UndoSimple" : "RedoSimple");
} else {
presentationName = NbBundle.getMessage(UndoRedoAction.class, doUndo ? "UndoWithParameter" : "RedoWithParameter", undo);
}
LOG.log (Level.FINE, "Result name is {0}", presentationName);
return presentationName;
}
@Override
public HelpCtx getHelpCtx() {
return new HelpCtx(UndoRedoAction.class);
}
@Override
public void actionPerformed(ActionEvent ev) {
UndoRedo undoRedo = getUndoRedo();
if (doUndo) try {
if (undoRedo.canUndo()) {
undoRedo.undo();
}
} catch (CannotUndoException ex) {
cannotUndoRedo(ex);
} else try {
if (undoRedo.canRedo()) {
undoRedo.redo();
}
} catch (CannotRedoException ex) {
cannotUndoRedo(ex);
}
run();
}
static void cannotUndoRedo(RuntimeException ex) throws MissingResourceException, HeadlessException {
if (ex.getMessage() != null) {
JOptionPane.showMessageDialog(
WindowManager.getDefault().getMainWindow(),
ex.getMessage(),
NbBundle.getMessage(UndoRedoAction.class, ex instanceof CannotUndoException ? "LBL_CannotUndo" : "LBL_CannotRedo"),
JOptionPane.ERROR_MESSAGE);
}
}
@Override
public void propertyChange(PropertyChangeEvent ev) {
if (TopComponent.Registry.PROP_ACTIVATED.equals(ev.getPropertyName())) {
run();
}
}
@Override
public void stateChanged(ChangeEvent ev) {
run();
}
@Override
public void resultChanged(LookupEvent ev) {
run();
}
@Override
public Action createContextAwareInstance(Lookup actionContext) {
return new UndoRedoAction(actionContext, doUndo, false);
}
private String getDefaultSwingText() {
return doUndo ? UIManager.getString("AbstractUndoableEdit.undoText") : //NOI18N
UIManager.getString("AbstractUndoableEdit.redoText"); //NOI18N
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy