diff -r 1db5ce9ba37d core.ui/src/org/netbeans/core/ui/resources/layer.xml --- a/core.ui/src/org/netbeans/core/ui/resources/layer.xml Thu Feb 18 12:56:39 2010 +0100 +++ b/core.ui/src/org/netbeans/core/ui/resources/layer.xml Thu Feb 18 19:22:19 2010 +0100 @@ -84,13 +84,19 @@ - + + + + - + + + + diff -r 1db5ce9ba37d openide.actions/manifest.mf --- a/openide.actions/manifest.mf Thu Feb 18 12:56:39 2010 +0100 +++ b/openide.actions/manifest.mf Thu Feb 18 19:22:19 2010 +0100 @@ -2,5 +2,5 @@ OpenIDE-Module: org.openide.actions OpenIDE-Module-Localizing-Bundle: org/openide/actions/Bundle.properties AutoUpdate-Essential-Module: true -OpenIDE-Module-Specification-Version: 6.14 +OpenIDE-Module-Specification-Version: 6.15 diff -r 1db5ce9ba37d openide.actions/src/org/openide/actions/RedoAction.java --- a/openide.actions/src/org/openide/actions/RedoAction.java Thu Feb 18 12:56:39 2010 +0100 +++ b/openide.actions/src/org/openide/actions/RedoAction.java Thu Feb 18 19:22:19 2010 +0100 @@ -40,22 +40,26 @@ */ package org.openide.actions; +import javax.swing.Action; import org.openide.awt.UndoRedo; import org.openide.util.HelpCtx; +import org.openide.util.Lookup; import org.openide.util.NbBundle; import org.openide.util.actions.CallableSystemAction; import javax.swing.UIManager; import javax.swing.undo.CannotRedoException; +import org.openide.util.ContextAwareAction; import org.openide.util.Exceptions; -/** Redo an edit. +/** Redo an edit. Since version 6.15 this class +* implements {@link ContextAwareAction}. * * @see UndoAction * @author Ian Formanek, Jaroslav Tulach */ -public class RedoAction extends CallableSystemAction { +public class RedoAction extends CallableSystemAction implements ContextAwareAction { private static String SWING_DEFAULT_LABEL = UIManager.getString("AbstractUndoableEdit.redoText"); //NOI18N @Override @@ -109,4 +113,9 @@ protected boolean asynchronous() { return false; } + + @Override + public Action createContextAwareInstance(Lookup actionContext) { + return new UndoRedoAction(actionContext, false, false); + } } diff -r 1db5ce9ba37d openide.actions/src/org/openide/actions/UndoAction.java --- a/openide.actions/src/org/openide/actions/UndoAction.java Thu Feb 18 12:56:39 2010 +0100 +++ b/openide.actions/src/org/openide/actions/UndoAction.java Thu Feb 18 19:22:19 2010 +0100 @@ -40,8 +40,10 @@ */ package org.openide.actions; +import javax.swing.Action; import org.openide.awt.UndoRedo; import org.openide.util.HelpCtx; +import org.openide.util.Lookup; import org.openide.util.NbBundle; import org.openide.util.actions.CallableSystemAction; import org.openide.windows.TopComponent; @@ -56,15 +58,18 @@ import javax.swing.UIManager; import javax.swing.event.*; import javax.swing.undo.*; +import org.openide.util.ContextAwareAction; import org.openide.util.Exceptions; -/** Undo an edit. +/** Undo an edit. Since version 6.15 this class +* implements {@link ContextAwareAction}. * * @see UndoRedo * @author Ian Formanek, Jaroslav Tulach */ -public class UndoAction extends CallableSystemAction { +public class UndoAction extends CallableSystemAction +implements ContextAwareAction { /** initialized listener */ private static Listener listener; @@ -188,6 +193,11 @@ return false; } + @Override + public Action createContextAwareInstance(Lookup actionContext) { + return new UndoRedoAction(actionContext, true, false); + } + /** Listener on changes of selected workspace element and * its changes. */ diff -r 1db5ce9ba37d openide.actions/src/org/openide/actions/UndoRedoAction.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/openide.actions/src/org/openide/actions/UndoRedoAction.java Thu Feb 18 19:22:19 2010 +0100 @@ -0,0 +1,255 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 1997-2009 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common + * Development and Distribution License("CDDL") (collectively, the + * "License"). You may not use this file except in compliance with the + * License. You can obtain a copy of the License at + * http://www.netbeans.org/cddl-gplv2.html + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the + * specific language governing permissions and limitations under the + * License. When distributing the software, include this License Header + * Notice in each file and include the License file at + * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the GPL Version 2 section of the License file that + * accompanied this code. If applicable, add the following below the + * License Header, with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * Contributor(s): + * + * The Original Software is NetBeans. The Initial Developer of the Original + * Software is Sun Microsystems, Inc. Portions Copyright 1997-2008 Sun + * Microsystems, Inc. All Rights Reserved. + * + * If you wish your version of this file to be governed by only the CDDL + * or only the GPL Version 2, indicate your decision by adding + * "[Contributor] elects to include this software in this distribution + * under the [CDDL or GPL Version 2] license." If you do not indicate a + * single choice of license, a recipient has the option to distribute + * your version of this file under either the CDDL, the GPL Version 2 or + * to extend the choice of license to its licensees as provided above. + * However, if you add GPL Version 2 code and therefore, elected the GPL + * Version 2 license, then the option applies only if the new code is + * made subject to such option by the copyright holder. + */ +package org.openide.actions; + +import java.awt.EventQueue; +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.logging.Logger; +import java.util.logging.Level; +import javax.swing.AbstractAction; + +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); + } + + 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 = getUndoRedo().getUndoPresentationName(); + LOG.log (Level.FINE, "getUndoRedo().getUndoPresentationName() 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" : "UndoSimple", 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) { + Exceptions.printStackTrace(ex); + } else try { + if (undoRedo.canRedo()) { + undoRedo.redo(); + } + } catch (CannotRedoException ex) { + Exceptions.printStackTrace(ex); + } + run(); + } + + @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 + } +} diff -r 1db5ce9ba37d openide.actions/test/unit/src/org/openide/actions/UndoRedoActionTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/openide.actions/test/unit/src/org/openide/actions/UndoRedoActionTest.java Thu Feb 18 19:22:19 2010 +0100 @@ -0,0 +1,224 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2010 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common + * Development and Distribution License("CDDL") (collectively, the + * "License"). You may not use this file except in compliance with the + * License. You can obtain a copy of the License at + * http://www.netbeans.org/cddl-gplv2.html + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the + * specific language governing permissions and limitations under the + * License. When distributing the software, include this License Header + * Notice in each file and include the License file at + * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the GPL Version 2 section of the License file that + * accompanied this code. If applicable, add the following below the + * License Header, with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * If you wish your version of this file to be governed by only the CDDL + * or only the GPL Version 2, indicate your decision by adding + * "[Contributor] elects to include this software in this distribution + * under the [CDDL or GPL Version 2] license." If you do not indicate a + * single choice of license, a recipient has the option to distribute + * your version of this file under either the CDDL, the GPL Version 2 or + * to extend the choice of license to its licensees as provided above. + * However, if you add GPL Version 2 code and therefore, elected the GPL + * Version 2 license, then the option applies only if the new code is + * made subject to such option by the copyright holder. + * + * Contributor(s): + * + * Portions Copyrighted 2010 Sun Microsystems, Inc. + */ + +package org.openide.actions; + +import java.awt.event.ActionEvent; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; +import javax.swing.Action; +import javax.swing.event.UndoableEditEvent; +import javax.swing.undo.CannotRedoException; +import javax.swing.undo.CannotUndoException; +import javax.swing.undo.UndoableEdit; +import org.netbeans.junit.NbTestCase; +import org.openide.awt.UndoRedo; +import org.openide.util.ContextAwareAction; +import org.openide.util.Lookup; +import org.openide.util.lookup.AbstractLookup; +import org.openide.util.lookup.InstanceContent; + +/** + * + * @author Jaroslav Tulach + */ +public class UndoRedoActionTest extends NbTestCase +implements UndoRedo.Provider { + private UndoRedo.Manager ur; + private MyEdit me; + + public UndoRedoActionTest(String n) { + super(n); + } + + @Override + protected boolean runInEQ() { + return true; + } + + private Action undoAction(Lookup lkp) { + UndoAction u = UndoAction.get(UndoAction.class); + assertTrue("instance: " + u, u instanceof ContextAwareAction); + return ((ContextAwareAction) u).createContextAwareInstance(lkp); + } + + private Action redoAction(Lookup lkp) { + RedoAction r = RedoAction.get(RedoAction.class); + assertTrue("instance: " + r, r instanceof ContextAwareAction); + return ((ContextAwareAction) r).createContextAwareInstance(lkp); + } + + public void testUndoDeliversChanges() { + doUndoRedoTest(new UndoRedo.Manager(), true); + } + + public void testUndoDeliversChangesWithTooManyEdits() { + UndoRedo.Manager man = new UndoRedo.Manager() { + @Override + public boolean canUndo() { + if (super.canUndo()) { + undoableEditHappened(new UndoableEditEvent(UndoRedoActionTest.this, new MyEdit(true))); + } + return super.canUndo(); + } + }; + doUndoRedoTest(man, false); + } + + + private void doUndoRedoTest(UndoRedo.Manager man, boolean testCounts) { + me = new MyEdit(); + man.undoableEditHappened(new UndoableEditEvent(this, me)); + assertTrue("Can undo", man.canUndo()); + this.ur = man; + + InstanceContent ic = new InstanceContent(); + AbstractLookup lkp = new AbstractLookup(ic); + Action u = undoAction(lkp); + Action r = redoAction(lkp); + + assertFalse("Not enabled", u.isEnabled()); + assertFalse("Not enabledR", r.isEnabled()); + MyEdit lu = new MyEdit(); + MyEdit lr = new MyEdit(); + u.addPropertyChangeListener(lu); + r.addPropertyChangeListener(lr); + + ic.add(this); + + assertTrue("Action is enabled", u.isEnabled()); + assertEquals("One change", 1, lu.cnt); + assertEquals("No redo change", 0, lr.cnt); + + u.actionPerformed(new ActionEvent(this, 0, "")); + if (testCounts) { + assertEquals("my edit undone", 1, me.undo); + + assertFalse("No more undo", man.canUndo()); + assertTrue("But redo", man.canRedo()); + assertEquals("Another undo change", 2, lu.cnt); + assertEquals("New redo change", 1, lr.cnt); + assertTrue("Redo action enabled", r.isEnabled()); + } + + r.actionPerformed(new ActionEvent(this, 0, "")); + assertFalse("Redo action no longer enabled", r.isEnabled()); + } + + @Override + public UndoRedo getUndoRedo() { + return ur; + } + + private static final class MyEdit implements UndoableEdit, PropertyChangeListener { + private int undo; + private int redo; + private int cnt; + private boolean ignore; + + public MyEdit() { + this(false); + } + + public MyEdit(boolean ignore) { + this.ignore = ignore; + } + + @Override + public void propertyChange(PropertyChangeEvent evt) { + if ("enabled".equals(evt.getPropertyName())) { + cnt++; + } + } + + @Override + public void undo() throws CannotUndoException { + undo++; + } + + @Override + public boolean canUndo() { + return true; + } + + @Override + public void redo() throws CannotRedoException { + redo++; + } + + @Override + public boolean canRedo() { + return true; + } + + @Override + public void die() { + } + + @Override + public boolean addEdit(UndoableEdit anEdit) { + return false; + } + + @Override + public boolean replaceEdit(UndoableEdit anEdit) { + return false; + } + + @Override + public boolean isSignificant() { + return true; + } + + @Override + public String getPresentationName() { + return "My Edit"; + } + + @Override + public String getUndoPresentationName() { + return "My Undo"; + } + + @Override + public String getRedoPresentationName() { + return "My Redo"; + } + } +} \ No newline at end of file diff -r 1db5ce9ba37d openide.awt/manifest.mf --- a/openide.awt/manifest.mf Thu Feb 18 12:56:39 2010 +0100 +++ b/openide.awt/manifest.mf Thu Feb 18 19:22:19 2010 +0100 @@ -2,5 +2,5 @@ OpenIDE-Module: org.openide.awt OpenIDE-Module-Localizing-Bundle: org/openide/awt/Bundle.properties AutoUpdate-Essential-Module: true -OpenIDE-Module-Specification-Version: 7.21 +OpenIDE-Module-Specification-Version: 7.22 diff -r 1db5ce9ba37d openide.awt/src/org/openide/awt/UndoRedo.java --- a/openide.awt/src/org/openide/awt/UndoRedo.java Thu Feb 18 12:56:39 2010 +0100 +++ b/openide.awt/src/org/openide/awt/UndoRedo.java Thu Feb 18 19:22:19 2010 +0100 @@ -40,12 +40,13 @@ */ package org.openide.awt; -import org.openide.util.Task; import java.util.LinkedList; import javax.swing.event.*; import javax.swing.undo.*; import org.openide.util.ChangeSupport; +import org.openide.util.Lookup; +import org.openide.util.Utilities; /** Undo and Redo manager for top components and workspace elements. @@ -113,6 +114,18 @@ */ public String getRedoPresentationName(); + /** Components that provide {@link UndoRedo} shall announce that by + * implementing this provider interface. Both Edit/Undo and Edit/Redo actions + * seek this interface inside current selection (e.g. {@link Utilities#actionsGlobalContext()}). + * To control these actions make sure your implementation of this interface + * is exposed in instance representing {@link Lookup current context}. + * + * @since 7.21 + */ + public static interface Provider { + public UndoRedo getUndoRedo(); + } + /** An undo manager which fires a change event each time it consumes a new undoable edit. */ public static class Manager extends UndoManager implements UndoRedo { @@ -121,10 +134,7 @@ private final ChangeSupport cs = new ChangeSupport(this); /** vector of Edits to run */ - private LinkedList runus = new LinkedList(); // for fix of #8692 - - /** task that clears the queue */ - private Task task = Task.EMPTY; // for fix of #8692 + private final LinkedList runus = new LinkedList(); // for fix of #8692 /** Called from undoableEditHappened() inner class */ private void superUndoableEditHappened(UndoableEditEvent ue) { @@ -140,6 +150,7 @@ * Delegates to superclass and notifies listeners. * @param ue the edit */ + @Override public void undoableEditHappened(final UndoableEditEvent ue) { /* Edits are posted to request processor and the deadlock * in #8692 between undoredo and document that fires @@ -153,6 +164,7 @@ } /** Discard all the existing edits from the undomanager. */ + @Override public void discardAllEdits() { synchronized (runus) { runus.add(null); @@ -161,56 +173,43 @@ updateTask(); } - public boolean canUndo() { - /* First it must be checked that there are - * undoable edits waiting to be added to undoredo. - */ - boolean empty; + @Override + public void undo() throws CannotUndoException { + super.undo(); + updateTask(); + } - synchronized (runus) { - empty = runus.isEmpty(); - } + @Override + public void redo() throws CannotRedoException { + super.redo(); + updateTask(); + } - if (!empty) { - task.waitFinished(); - } - - return super.canUndo(); + @Override + public void undoOrRedo() throws CannotRedoException, CannotUndoException { + super.undoOrRedo(); + updateTask(); } private void updateTask() { - /* The following task is finished when there are no - * undoable edits waiting to be added to undoredo. - */ - class R implements Runnable { - public void run() { - for (;;) { - UndoableEditEvent ue; + for (;;) { + UndoableEditEvent ue; - synchronized (runus) { - if (runus.isEmpty()) { - break; - } + synchronized (runus) { + if (runus.isEmpty()) { + break; + } - ue = runus.removeFirst(); - } + ue = runus.removeFirst(); + } - if (ue == null) { - superDiscardAllEdits(); - } else { - superUndoableEditHappened(ue); - } - - cs.fireChange(); - } + if (ue == null) { + superDiscardAllEdits(); + } else { + superUndoableEditHappened(ue); } } - - R r = new R(); - r.run(); - - //Use internal not default RequestProcessor to solve deadlock #10826 - //task = internalRequestProcessor.post (r, 0, Thread.MAX_PRIORITY); + cs.fireChange(); } /* Attaches change listener to the this object. @@ -219,20 +218,24 @@ */ //#32313 - synchronization of this method was removed + @Override public void addChangeListener(ChangeListener l) { cs.addChangeListener(l); } /* Removes the listener */ + @Override public void removeChangeListener(ChangeListener l) { cs.removeChangeListener(l); } + @Override public String getUndoPresentationName() { return this.canUndo() ? super.getUndoPresentationName() : ""; // NOI18N } + @Override public String getRedoPresentationName() { return this.canRedo() ? super.getRedoPresentationName() : ""; // NOI18N } @@ -245,32 +248,40 @@ */ @Deprecated public static final class Empty extends Object implements UndoRedo { + @Override public boolean canUndo() { return false; } + @Override public boolean canRedo() { return false; } + @Override public void undo() throws CannotUndoException { throw new CannotUndoException(); } + @Override public void redo() throws CannotRedoException { throw new CannotRedoException(); } + @Override public void addChangeListener(ChangeListener l) { } + @Override public void removeChangeListener(ChangeListener l) { } + @Override public String getUndoPresentationName() { return ""; // NOI18N } + @Override public String getRedoPresentationName() { return ""; // NOI18N } diff -r 1db5ce9ba37d openide.awt/test/unit/src/org/openide/awt/UndoRedoTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/openide.awt/test/unit/src/org/openide/awt/UndoRedoTest.java Thu Feb 18 19:22:19 2010 +0100 @@ -0,0 +1,183 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2010 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common + * Development and Distribution License("CDDL") (collectively, the + * "License"). You may not use this file except in compliance with the + * License. You can obtain a copy of the License at + * http://www.netbeans.org/cddl-gplv2.html + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the + * specific language governing permissions and limitations under the + * License. When distributing the software, include this License Header + * Notice in each file and include the License file at + * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the GPL Version 2 section of the License file that + * accompanied this code. If applicable, add the following below the + * License Header, with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * If you wish your version of this file to be governed by only the CDDL + * or only the GPL Version 2, indicate your decision by adding + * "[Contributor] elects to include this software in this distribution + * under the [CDDL or GPL Version 2] license." If you do not indicate a + * single choice of license, a recipient has the option to distribute + * your version of this file under either the CDDL, the GPL Version 2 or + * to extend the choice of license to its licensees as provided above. + * However, if you add GPL Version 2 code and therefore, elected the GPL + * Version 2 license, then the option applies only if the new code is + * made subject to such option by the copyright holder. + * + * Contributor(s): + * + * Portions Copyrighted 2010 Sun Microsystems, Inc. + */ + +package org.openide.awt; + +import javax.swing.undo.CannotRedoException; +import javax.swing.undo.CannotUndoException; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; +import javax.swing.undo.UndoableEdit; +import javax.swing.event.ChangeEvent; +import javax.swing.event.ChangeListener; +import javax.swing.event.UndoableEditEvent; +import org.netbeans.junit.NbTestCase; +import static org.junit.Assert.*; + +/** + * + * @author Jaroslav Tulach + */ +public class UndoRedoTest extends NbTestCase implements ChangeListener { + private int cnt; + + public UndoRedoTest(String n) { + super(n); + } + + public void testUndoDeliversChanges() { + UndoRedo.Manager ur = new UndoRedo.Manager(); + doUndoRedoTest(ur); + } + + public void testUndoDeliversChangesWithTooManyEdits() { + UndoRedo.Manager ur = new UndoRedo.Manager() { + @Override + public boolean canUndo() { + if (super.canUndo()) { + undoableEditHappened(new UndoableEditEvent(this, new MyEdit(true))); + } + return super.canUndo(); + } + }; + doUndoRedoTest(ur); + } + + private void doUndoRedoTest(UndoRedo.Manager ur) { + assertFalse("Nothing to undo", ur.canUndo()); + ur.addChangeListener(this); + MyEdit me = new MyEdit(); + ur.undoableEditHappened(new UndoableEditEvent(this, me)); + assertEquals("One change", 1, cnt); + assertTrue("Can undo now", ur.canUndo()); + ur.undo(); + assertFalse("Cannot undo", ur.canUndo()); + assertEquals("Snd change", 2, cnt); + + assertTrue("But redo", ur.canRedo()); + ur.redo(); + assertEquals("Third change", 3, cnt); + assertEquals("One undo", 1, me.undo); + assertEquals("One redo", 1, me.redo); + } + + @Override + public void stateChanged(ChangeEvent e) { + cnt++; + } + private static final class MyEdit implements UndoableEdit, PropertyChangeListener { + private int undo; + private int redo; + private int cnt; + private boolean ignore; + + public MyEdit() { + this(false); + } + + public MyEdit(boolean ignore) { + this.ignore = ignore; + } + + @Override + public void propertyChange(PropertyChangeEvent evt) { + if ("enabled".equals(evt.getPropertyName())) { + cnt++; + } + } + + @Override + public void undo() throws CannotUndoException { + undo++; + } + + @Override + public boolean canUndo() { + return true; + } + + @Override + public void redo() throws CannotRedoException { + redo++; + } + + @Override + public boolean canRedo() { + return true; + } + + @Override + public void die() { + } + + @Override + public boolean addEdit(UndoableEdit anEdit) { + if (anEdit instanceof MyEdit && ((MyEdit)anEdit).ignore) { + return true; + } + return false; + } + + @Override + public boolean replaceEdit(UndoableEdit anEdit) { + return false; + } + + @Override + public boolean isSignificant() { + return true; + } + + @Override + public String getPresentationName() { + return "My Edit"; + } + + @Override + public String getUndoPresentationName() { + return "My Undo"; + } + + @Override + public String getRedoPresentationName() { + return "My Redo"; + } + } + +} \ No newline at end of file diff -r 1db5ce9ba37d openide.text/src/org/openide/text/CloneableEditorSupport.java --- a/openide.text/src/org/openide/text/CloneableEditorSupport.java Thu Feb 18 12:56:39 2010 +0100 +++ b/openide.text/src/org/openide/text/CloneableEditorSupport.java Thu Feb 18 19:22:19 2010 +0100 @@ -120,7 +120,8 @@ * * @author Jaroslav Tulach */ -public abstract class CloneableEditorSupport extends CloneableOpenSupport { +public abstract class CloneableEditorSupport extends CloneableOpenSupport +implements UndoRedo.Provider { private static final RequestProcessor RP = new RequestProcessor("org.openide.text Document Processing"); /** Common name for editor mode. */ diff -r 1db5ce9ba37d openide.windows/src/org/openide/windows/TopComponent.java --- a/openide.windows/src/org/openide/windows/TopComponent.java Thu Feb 18 12:56:39 2010 +0100 +++ b/openide.windows/src/org/openide/windows/TopComponent.java Thu Feb 18 19:22:19 2010 +0100 @@ -112,7 +112,7 @@ * * @author Jaroslav Tulach, Petr Hamernik, Jan Jancura */ -public class TopComponent extends JComponent implements Externalizable, Accessible, HelpCtx.Provider, Lookup.Provider { +public class TopComponent extends JComponent implements Externalizable, Accessible, HelpCtx.Provider, Lookup.Provider, UndoRedo.Provider { /** UI logger to notify about invocation of an action */ private static Logger UILOG = Logger.getLogger("org.netbeans.ui.actions"); // NOI18N /** generated Serialized Version UID */