diff -r 4fe4a8fce84e core.ui/nbproject/project.xml --- a/core.ui/nbproject/project.xml Mon Jul 27 00:17:05 2009 +0400 +++ b/core.ui/nbproject/project.xml Mon Jul 27 09:35:02 2009 +0200 @@ -94,7 +94,7 @@ - 7.10 + 7.12 diff -r 4fe4a8fce84e core.ui/src/org/netbeans/core/ui/resources/layer.xml --- a/core.ui/src/org/netbeans/core/ui/resources/layer.xml Mon Jul 27 00:17:05 2009 +0400 +++ b/core.ui/src/org/netbeans/core/ui/resources/layer.xml Mon Jul 27 09:35:02 2009 +0200 @@ -73,6 +73,7 @@ + diff -r 4fe4a8fce84e openide.awt/apichanges.xml --- a/openide.awt/apichanges.xml Mon Jul 27 00:17:05 2009 +0400 +++ b/openide.awt/apichanges.xml Mon Jul 27 09:35:02 2009 +0200 @@ -47,6 +47,32 @@ AWT API + + + Support for asynchronous actions + + + + + + The layer definition used by factory methods for + + context + and + + callback + and + + alwaysEnabledAction + were enhanced to understand <attr name="asynchronous" boolvalue="true"/> + attribute. This extends these new factories with capabilities + already present in old SystemAction, thus makes + it easier to migrate from old to new while retaining compatible + behaviour. + + + + Added dispose() method. diff -r 4fe4a8fce84e openide.awt/nbproject/project.properties --- a/openide.awt/nbproject/project.properties Mon Jul 27 00:17:05 2009 +0400 +++ b/openide.awt/nbproject/project.properties Mon Jul 27 09:35:02 2009 +0200 @@ -44,4 +44,4 @@ javadoc.arch=${basedir}/arch.xml javadoc.apichanges=${basedir}/apichanges.xml -spec.version.base=7.11.0 +spec.version.base=7.12.0 diff -r 4fe4a8fce84e openide.awt/src/org/openide/awt/Actions.java --- a/openide.awt/src/org/openide/awt/Actions.java Mon Jul 27 00:17:05 2009 +0400 +++ b/openide.awt/src/org/openide/awt/Actions.java Mon Jul 27 09:35:02 2009 +0200 @@ -404,6 +404,7 @@ * <attr name="displayName" bundlevalue="your.pkg.Bundle#key"/> * <attr name="iconBase" stringvalue="your/pkg/YourImage.png"/> * <!-- if desired: <attr name="noIconInMenu" boolvalue="false"/> --> + * <!-- if desired: <attr name="asynchronous" boolvalue="true"/> --> * </file> * * In case the "delegate" is not just {@link ActionListener}, but also @@ -451,6 +452,7 @@ * <attr name="displayName" bundlevalue="your.pkg.Bundle#key"/> * <attr name="iconBase" stringvalue="your/pkg/YourImage.png"/> * <!-- if desired: <attr name="noIconInMenu" boolvalue="false"/> --> + * <!-- if desired: <attr name="asynchronous" boolvalue="true"/> --> * </file> * * @@ -508,6 +510,7 @@ * <attr name="displayName" bundlevalue="your.pkg.Bundle#key"/> * <attr name="iconBase" stringvalue="your/pkg/YourImage.png"/> * <!-- if desired: <attr name="noIconInMenu" boolvalue="false"/> --> + * <!-- if desired: <attr name="asynchronous" boolvalue="true"/> --> * </file> * * In the previous case there has to be a class with public default constructor diff -r 4fe4a8fce84e openide.awt/src/org/openide/awt/AlwaysEnabledAction.java --- a/openide.awt/src/org/openide/awt/AlwaysEnabledAction.java Mon Jul 27 00:17:05 2009 +0400 +++ b/openide.awt/src/org/openide/awt/AlwaysEnabledAction.java Mon Jul 27 09:35:02 2009 +0200 @@ -16,6 +16,8 @@ import javax.swing.Icon; import javax.swing.KeyStroke; import javax.swing.text.Keymap; +import org.netbeans.modules.openide.util.ActionsBridge; +import org.netbeans.modules.openide.util.ActionsBridge.ActionRunnable; import org.openide.util.ContextAwareAction; import org.openide.util.ImageUtilities; import org.openide.util.Lookup; @@ -102,7 +104,7 @@ return true; } - public void actionPerformed(ActionEvent e) { + public void actionPerformed(final ActionEvent e) { assert EventQueue.isDispatchThread(); if (getDelegate() instanceof Action) { if (!((Action)getDelegate()).isEnabled()) { @@ -112,7 +114,14 @@ } } - getDelegate().actionPerformed(e); + boolean async = Boolean.TRUE.equals(map.get("asynchronous")); // NOI18N + ActionRunnable ar = new ActionRunnable(e, this, async) { + @Override + protected void run() { + getDelegate().actionPerformed(e); + } + }; + ActionsBridge.doPerformAction(this, ar); } @Override diff -r 4fe4a8fce84e openide.awt/src/org/openide/awt/ContextAction.java --- a/openide.awt/src/org/openide/awt/ContextAction.java Mon Jul 27 00:17:05 2009 +0400 +++ b/openide.awt/src/org/openide/awt/ContextAction.java Mon Jul 27 09:35:02 2009 +0200 @@ -103,8 +103,6 @@ /** Invoked when an action occurs. */ public void actionPerformed(final java.awt.event.ActionEvent e) { - assert EventQueue.isDispatchThread(); - global.actionPerformed(e, performer, type, selectMode); } diff -r 4fe4a8fce84e openide.awt/src/org/openide/awt/GeneralAction.java --- a/openide.awt/src/org/openide/awt/GeneralAction.java Mon Jul 27 00:17:05 2009 +0400 +++ b/openide.awt/src/org/openide/awt/GeneralAction.java Mon Jul 27 09:35:02 2009 +0200 @@ -49,6 +49,8 @@ import java.util.logging.Logger; import javax.swing.Action; import javax.swing.ActionMap; +import org.netbeans.modules.openide.util.ActionsBridge; +import org.netbeans.modules.openide.util.ActionsBridge.ActionRunnable; import org.openide.awt.ContextAction.Performer; import org.openide.util.ContextAwareAction; import org.openide.util.Lookup; @@ -69,12 +71,12 @@ static final Logger LOG = Logger.getLogger(GeneralAction.class.getName()); public static ContextAwareAction callback( - String key, Action defaultDelegate, Lookup context, boolean surviveFocusChange + String key, Action defaultDelegate, Lookup context, boolean surviveFocusChange, boolean async ) { if (key == null) { throw new NullPointerException(); } - return new DelegateAction(null, key, context, defaultDelegate, surviveFocusChange); + return new DelegateAction(null, key, context, defaultDelegate, surviveFocusChange, async); } public static Action alwaysEnabled(Map map) { @@ -161,6 +163,8 @@ private Action fallback; /** key to delegate to */ private Object key; + /** are we asynchronous? */ + private final boolean async; /** global lookup to work with */ private GlobalManager global; @@ -175,12 +179,13 @@ * listens for changes of ActionMap in order to delegate * to right action. */ - public DelegateAction(Map map, Object key, Lookup actionContext, Action fallback, boolean surviveFocusChange) { + public DelegateAction(Map map, Object key, Lookup actionContext, Action fallback, boolean surviveFocusChange, boolean async) { this.map = map; this.key = key; this.fallback = fallback; this.global = GlobalManager.findManager(actionContext, surviveFocusChange); this.weakL = WeakListeners.propertyChange(this, fallback); + this.async = async; if (fallback != null) { fallback.addPropertyChangeListener(weakL); } @@ -192,7 +197,8 @@ map.get("key"), // NOI18N Utilities.actionsGlobalContext(), // NOI18N fallback, // NOI18N - Boolean.TRUE.equals(map.get("surviveFocusChange")) // NOI18N + Boolean.TRUE.equals(map.get("surviveFocusChange")), // NOI18N + Boolean.TRUE.equals(map.get("asynchronous")) // NOI18N ); } @@ -208,7 +214,8 @@ assert EventQueue.isDispatchThread(); final javax.swing.Action a = findAction(); if (a != null) { - a.actionPerformed(e); + ActionRunnable ar = ActionRunnable.create(e, a, async); + ActionsBridge.doPerformAction(a, ar); } } @@ -288,7 +295,7 @@ if (f instanceof ContextAwareAction) { f = ((ContextAwareAction)f).createContextAwareInstance(actionContext); } - return new DelegateAction(map, key, actionContext, f, global.isSurvive()); + return new DelegateAction(map, key, actionContext, f, global.isSurvive(), async); } public void propertyChange(PropertyChangeEvent evt) { diff -r 4fe4a8fce84e openide.awt/test/unit/src/org/openide/awt/AsynchronousTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/openide.awt/test/unit/src/org/openide/awt/AsynchronousTest.java Mon Jul 27 09:35:02 2009 +0200 @@ -0,0 +1,185 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 1997-2007 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-2006 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.awt; + +import java.awt.event.ActionEvent; +import java.util.logging.Level; +import javax.swing.AbstractAction; +import javax.swing.Action; +import org.netbeans.junit.Log; +import org.netbeans.junit.NbTestCase; +import org.openide.filesystems.FileUtil; +import org.openide.util.ContextAwareAction; +import org.openide.util.Lookup; + +/** Verifies asynchronous aspects of actions systems are close to the + * original behaviour of SystemAction one. + * Taken from org.openide.util.actions.AsynchronousTest + * @author Jaroslav Tulach + */ +public class AsynchronousTest extends NbTestCase { + + private CharSequence err; + + public AsynchronousTest(String name) { + super(name); + } + + @Override + protected int timeOut() { + return 5000; + } + + @Override + protected boolean runInEQ() { + return true; + } + + @Override + protected void setUp() { + err = Log.enable("", Level.WARNING); + AC.finished = false; + } + + public void testExecutionOfActionsThatDoesNotDefineAsynchronousIsSynchronousNoWarningIsPrinted() throws Exception { + Action action = (Action)FileUtil.getConfigFile("actions/async/none.instance").getAttribute("instanceCreate"); + + synchronized (AsynchronousTest.class) { + action.actionPerformed(new ActionEvent(this, 0, "")); + assertTrue("The synchronous action is finished immediatelly", AC.finished); + } + + if (err.length() > 0) { + fail("There should be no warning about missing asynchronous: " + err); + } + } + + public void testExecutionCanBeAsynchronous() throws Exception { + Action action = (Action)FileUtil.getConfigFile("actions/async/true.instance").getAttribute("instanceCreate"); + + synchronized (AsynchronousTest.class) { + action.actionPerformed(new ActionEvent(this, 0, "")); + Thread.sleep(500); + assertFalse("Shall Not be finished yet", AC.finished); + AsynchronousTest.class.wait(); + assertTrue("The asynchronous action is finished", AC.finished); + } + + if (err.length() > 0) { + fail("No warning about the class: " + err); + } + } + + public void testExecutionCanBeAsynchronousForAlways() throws Exception { + Action action = (Action)FileUtil.getConfigFile("actions/async/true-always.instance").getAttribute("instanceCreate"); + + synchronized (AsynchronousTest.class) { + action.actionPerformed(new ActionEvent(this, 0, "")); + Thread.sleep(500); + assertFalse("Shall Not be finished yet", AC.finished); + AsynchronousTest.class.wait(); + assertTrue("The asynchronous action is finished", AC.finished); + } + + if (err.length() > 0) { + fail("No warning about the class: " + err); + } + } + public void testExecutionCanBeSynchronous() throws Exception { + Action action = (Action)FileUtil.getConfigFile("actions/async/false.instance").getAttribute("instanceCreate"); + + synchronized (AsynchronousTest.class) { + action.actionPerformed(new ActionEvent(this, 0, "")); + assertTrue("The synchronous action is finished immediatelly", AC.finished); + } + + if (err.length() > 0) { + fail("No warning about the class: " + err); + } + } + + public void testExecutionCanBeForcedToBeSynchronous() throws Exception { + Action action = (Action)FileUtil.getConfigFile("actions/async/true.instance").getAttribute("instanceCreate"); + + synchronized (AsynchronousTest.class) { + action.actionPerformed(new ActionEvent(this, 0, "waitFinished")); + assertTrue("When asked for synchronous the action is finished immediatelly", AC.finished); + } + + if (err.length() > 0) { + fail("No warning about the class: " + err); + } + } + + public void testExecutionCanBeAsynchronousForContext() throws Exception { + Action action = (Action)FileUtil.getConfigFile("actions/async/true-context.instance").getAttribute("instanceCreate"); + + synchronized (AsynchronousTest.class) { + action.actionPerformed(new ActionEvent(this, 0, "")); + Thread.sleep(500); + assertFalse("Shall Not be finished yet", AC.finished); + AsynchronousTest.class.wait(); + assertTrue("The asynchronous action is finished", AC.finished); + } + + if (err.length() > 0) { + fail("No warning about the class: " + err); + } + } + + public static class AC extends AbstractAction { + static boolean finished; + + public void actionPerformed(ActionEvent ev) { + synchronized (AsynchronousTest.class) { + AsynchronousTest.class.notifyAll(); + finished = true; + } + } + } + + public static class CAC extends AC implements ContextAwareAction { + public Action createContextAwareInstance(Lookup actionContext) { + return this; + } + } +} diff -r 4fe4a8fce84e openide.awt/test/unit/src/org/openide/awt/CallbackActionTest.java --- a/openide.awt/test/unit/src/org/openide/awt/CallbackActionTest.java Mon Jul 27 00:17:05 2009 +0400 +++ b/openide.awt/test/unit/src/org/openide/awt/CallbackActionTest.java Mon Jul 27 09:35:02 2009 +0200 @@ -180,7 +180,7 @@ } static ContextAwareAction callback(String key, AbstractAction fallAction, Lookup al, boolean b) { - return GeneralAction.callback(key, fallAction, al, b); + return GeneralAction.callback(key, fallAction, al, b, false); } private static final class CntListener extends Object diff -r 4fe4a8fce84e openide.awt/test/unit/src/org/openide/awt/GeneralActionTest.java --- a/openide.awt/test/unit/src/org/openide/awt/GeneralActionTest.java Mon Jul 27 00:17:05 2009 +0400 +++ b/openide.awt/test/unit/src/org/openide/awt/GeneralActionTest.java Mon Jul 27 09:35:02 2009 +0200 @@ -81,7 +81,7 @@ ContextAwareAction expResult = null; try { - ContextAwareAction result = GeneralAction.callback(key, defaultDelegate, context, false); + ContextAwareAction result = GeneralAction.callback(key, defaultDelegate, context, false, false); fail("Shall fail as key is null"); } catch (NullPointerException ex) { // ok diff -r 4fe4a8fce84e openide.awt/test/unit/src/org/openide/awt/test-layer.xml --- a/openide.awt/test/unit/src/org/openide/awt/test-layer.xml Mon Jul 27 00:17:05 2009 +0400 +++ b/openide.awt/test/unit/src/org/openide/awt/test-layer.xml Mon Jul 27 09:35:02 2009 +0200 @@ -170,5 +170,42 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff -r 4fe4a8fce84e openide.util/src/org/netbeans/modules/openide/util/ActionsBridge.java --- a/openide.util/src/org/netbeans/modules/openide/util/ActionsBridge.java Mon Jul 27 00:17:05 2009 +0400 +++ b/openide.util/src/org/netbeans/modules/openide/util/ActionsBridge.java Mon Jul 27 09:35:02 2009 +0200 @@ -61,6 +61,12 @@ protected abstract void invokeAction(Action action, ActionEvent ev); public static void doPerformAction(CallableSystemAction action, final ActionsBridge.ActionRunnable r) { + implPerformAction(action, r); + } + public static void doPerformAction(Action action, final ActionsBridge.ActionRunnable r) { + implPerformAction(action, r); + } + private static void implPerformAction(Action action, final ActionsBridge.ActionRunnable r) { assert java.awt.EventQueue.isDispatchThread() : "Action " + action.getClass().getName() + " may not be invoked from the thread " + Thread.currentThread().getName() + ", only the event queue: http://www.netbeans.org/download/4_1/javadoc/OpenAPIs/apichanges.html#actions-event-thread"; @@ -83,15 +89,27 @@ */ public static abstract class ActionRunnable implements Action { final ActionEvent ev; - final SystemAction action; + final Action action; final boolean async; public ActionRunnable(ActionEvent ev, SystemAction action, boolean async) { + this(ev, (Action)action, async); + } + public ActionRunnable(ActionEvent ev, Action action, boolean async) { this.ev = ev; this.action = action; this.async = async; } + public static ActionRunnable create(ActionEvent ev, Action a, boolean async) { + return new ActionRunnable(ev, a, async) { + @Override + protected void run() { + action.actionPerformed(ev); + } + }; + } + public final boolean needsToBeSynchronous() { return "waitFinished".equals(ev.getActionCommand()); // NOI18N }