Index: openide/src/org/openide/actions/DeleteAction.java =================================================================== RCS file: /cvs/openide/src/org/openide/actions/DeleteAction.java,v retrieving revision 1.20 diff -u -r1.20 DeleteAction.java --- openide/src/org/openide/actions/DeleteAction.java 27 Aug 2003 20:35:01 -0000 1.20 +++ openide/src/org/openide/actions/DeleteAction.java 13 Feb 2004 17:10:47 -0000 @@ -46,7 +46,7 @@ } protected boolean asynchronous() { - return false; + return true; } } Index: openide/src/org/openide/actions/PasteAction.java =================================================================== RCS file: /cvs/openide/src/org/openide/actions/PasteAction.java,v retrieving revision 1.52 diff -u -r1.52 PasteAction.java --- openide/src/org/openide/actions/PasteAction.java 7 Jan 2004 13:19:50 -0000 1.52 +++ openide/src/org/openide/actions/PasteAction.java 13 Feb 2004 17:10:47 -0000 @@ -139,7 +139,8 @@ } if(t != null) { - executePasteType (t); + // posts the action in RP thread + new ActionPT (t, ev.getActionCommand ()); } else { ErrorManager.getDefault().notify( ErrorManager.INFORMATIONAL, @@ -147,43 +148,11 @@ "No paste types available when performing paste action")); // NOI18N } } - + protected boolean asynchronous() { return false; } - /** Does the execution of a paste type with all handling around - */ - private static void executePasteType (PasteType t) { - NodeSelector sel = null; - try { - ExplorerManager em = findExplorerManager (); - if (em != null) { - sel = new NodeSelector (em, null); - } - - Transferable trans = t.paste(); - Clipboard clipboard = getClipboard(); - - - if (trans != null) { - ClipboardOwner owner = trans instanceof ClipboardOwner ? - (ClipboardOwner)trans - : - new StringSelection (""); // NOI18N - clipboard.setContents(trans, owner); - } - } catch (UserCancelException exc) { - // ignore - user just pressed cancel in some dialog.... - } catch (java.io.IOException e) { - ErrorManager.getDefault().notify(e); - } finally { - if (sel != null) { - sel.select (); - } - } - } - /** Set possible paste types. * Automatically enables or disables the paste action according to whether there are any. * @param types the new types to allow, or null @@ -392,6 +361,10 @@ } public void performActionAt(int index) { + performActionAt (index, null); + } + + public void performActionAt(int index, ActionEvent ev) { Action[] action = new Action[1]; Object[] arr = getPasteTypesOrActions (action); @@ -401,7 +374,8 @@ if (arr[index] instanceof PasteType) { PasteType t = (PasteType)arr[index]; - new ActionPT(t).actionPerformed(new ActionEvent(t, ActionEvent.ACTION_PERFORMED, javax.swing.Action.NAME)); + // posts the action is RP thread + new ActionPT(t, ev == null ? null : ev.getActionCommand ()); return; } else { // is action @@ -676,7 +650,7 @@ */ public void actionPerformed(java.awt.event.ActionEvent e) { if (model != null) { - model.performActionAt(0); + model.performActionAt(0, e); } } @@ -710,15 +684,72 @@ /** Action that wraps paste type. */ - private static final class ActionPT extends javax.swing.AbstractAction { + private static final class ActionPT extends javax.swing.AbstractAction + implements Runnable { private PasteType t; + private NodeSelector sel; + private boolean secondInvocation; - public ActionPT (PasteType t) { + public ActionPT (PasteType t, String command) { this.t = t; + + ExplorerManager em = findExplorerManager (); + if (em != null) { + this.sel = new NodeSelector (em, null); + } + + if ("synchronous".equals (command)) { + run (); + } else { + org.openide.util.RequestProcessor.getDefault ().post (this); + } } + public void actionPerformed (java.awt.event.ActionEvent ev) { - executePasteType (t); + try { + Transferable trans = t.paste(); + Clipboard clipboard = getClipboard(); + + + if (trans != null) { + ClipboardOwner owner = trans instanceof ClipboardOwner ? + (ClipboardOwner)trans + : + new StringSelection (""); // NOI18N + clipboard.setContents(trans, owner); + } + } catch (UserCancelException exc) { + // ignore - user just pressed cancel in some dialog.... + } catch (java.io.IOException e) { + ErrorManager.getDefault().notify(e); + } finally { + javax.swing.SwingUtilities.invokeLater (this); + } } + + public void run () { + if (secondInvocation) { + if (sel != null) { + sel.select (); + } + } else { + secondInvocation = true; + ActionManager.getDefault ().invokeAction ( + this, + new ActionEvent (t, ActionEvent.ACTION_PERFORMED, javax.swing.Action.NAME) + ); + } + } + + public boolean isEnabled () { + return ((PasteAction)PasteAction.get (PasteAction.class)).isEnabled (); + } + + public Object getValue (String key) { + return ((PasteAction)PasteAction.get (PasteAction.class)).getValue (key); + } + } + } Index: openide/src/org/openide/actions/PrintAction.java =================================================================== RCS file: /cvs/openide/src/org/openide/actions/PrintAction.java,v retrieving revision 1.20 diff -u -r1.20 PrintAction.java --- openide/src/org/openide/actions/PrintAction.java 3 Feb 2004 15:24:19 -0000 1.20 +++ openide/src/org/openide/actions/PrintAction.java 13 Feb 2004 17:10:47 -0000 @@ -32,20 +32,16 @@ } protected void performAction(final Node[] activatedNodes) { - RequestProcessor.getDefault().post(new Runnable() { - public void run() { - for (int i = 0; i < activatedNodes.length; i++) { - PrintCookie pc = (PrintCookie)activatedNodes[i].getCookie (PrintCookie.class); - if (pc != null) { - pc.print(); - } - } - } - }); + for (int i = 0; i < activatedNodes.length; i++) { + PrintCookie pc = (PrintCookie)activatedNodes[i].getCookie (PrintCookie.class); + if (pc != null) { + pc.print(); + } + } } protected boolean asynchronous() { - return false; + return true; } protected int mode () { Index: openide/src/org/openide/explorer/ExplorerActions.java =================================================================== RCS file: /cvs/openide/src/org/openide/explorer/ExplorerActions.java,v retrieving revision 1.63 diff -u -r1.63 ExplorerActions.java --- openide/src/org/openide/explorer/ExplorerActions.java 21 Nov 2003 10:51:19 -0000 1.63 +++ openide/src/org/openide/explorer/ExplorerActions.java 13 Feb 2004 17:10:47 -0000 @@ -617,12 +617,17 @@ } doDestroy(sel); - - if (attachPerformers) { - delete.setActionPerformer (null); // fixes bug #673 - } else { - setEnabled (false); + + class Run implements Runnable { + public void run () { + if (attachPerformers) { + delete.setActionPerformer (null); // fixes bug #673 + } else { + setEnabled (false); + } + } } + org.openide.util.Mutex.EVENT.readAccess (new Run ()); } } Index: openide/src/org/openide/util/actions/CallableSystemAction.java =================================================================== RCS file: /cvs/openide/src/org/openide/util/actions/CallableSystemAction.java,v retrieving revision 1.16 diff -u -r1.16 CallableSystemAction.java --- openide/src/org/openide/util/actions/CallableSystemAction.java 6 Sep 2003 16:14:02 -0000 1.16 +++ openide/src/org/openide/util/actions/CallableSystemAction.java 13 Feb 2004 17:10:47 -0000 @@ -73,7 +73,7 @@ */ public void actionPerformed(ActionEvent ev) { if (isEnabled()) { - doPerformAction(new Runnable() { + doPerformAction(new ActionRunnable(ev) { public void run() { performAction(); } @@ -103,31 +103,12 @@ * @param r a block to run * @see #asynchronous */ - final void doPerformAction(final Runnable r) { + final void doPerformAction(final ActionRunnable r) { assert EventQueue.isDispatchThread() : "Action " + getClass().getName() + " may not be invoked from the thread " + Thread.currentThread().getName() + ", only the event queue: http://www.netbeans.org/download/dev/javadoc/OpenAPIs/apichanges.html#actions-event-thread"; - if (asynchronous()) { - if (warnedAsynchronousActions.add(getClass())) { - ErrorManager.getDefault().log(ErrorManager.WARNING, "Warning - " + getClass().getName() + " should override CallableSystemAction.asynchronous() to return false"); - } + if (asynchronous() && !r.needsToBeSynchronous ()) { Runnable r2 = new Runnable() { public void run() { - try { - EventQueue.invokeLater(new Runnable() { - public void run() { - MouseCursorUtils.showWaitCursor(r); - } - }); - //addRunningAction(r); - r.run(); - } finally { - //removeRunningAction(r); - EventQueue.invokeLater(new Runnable() { - public void run() { - MouseCursorUtils.hideWaitCursor(r); - } - }); - //fireRunningActionsChange(); - } + r.doRun(); } }; RP.post(r2); @@ -155,10 +136,55 @@ * @since 4.11 */ protected boolean asynchronous() { + if (warnedAsynchronousActions.add(getClass())) { + ErrorManager.getDefault().log(ErrorManager.WARNING, "Warning - " + getClass().getName() + " should override CallableSystemAction.asynchronous() to return false"); + } return DEFAULT_ASYNCH; } private static final boolean DEFAULT_ASYNCH = !Boolean.getBoolean("org.openide.util.actions.CallableSystemAction.synchronousByDefault"); + /** variables for invokeAction methods */ + private static Object invokeInstance; + private static Object invokeAction; + /** Call ActionManager.invokeAction method. + */ + public static void invokeAction (javax.swing.Action action, java.awt.event.ActionEvent ev) { + if (invokeAction == null) { + ClassLoader loader = (ClassLoader)org.openide.util.Lookup.getDefault ().lookup (ClassLoader.class); + if (loader != null) { + loader = CallableSystemAction.class.getClassLoader (); + } + try { + Class clazz = Class.forName ("org.openide.actions.ActionManager", true, loader); + invokeInstance = org.openide.util.Lookup.getDefault ().lookup (clazz); + if (invokeInstance != null) { + invokeAction = clazz.getMethod ("invokeAction", new Class[] { + javax.swing.Action.class, + java.awt.event.ActionEvent.class + }); + } else { + // dummy value + invokeAction = new Object (); + } + } catch (Exception ex) { + ErrorManager.getDefault ().notify (ErrorManager.INFORMATIONAL, ex); + // some empty value + invokeAction = new Object (); + } + } + + if (invokeAction instanceof java.lang.reflect.Method) { + java.lang.reflect.Method m = (java.lang.reflect.Method)invokeAction; + try { + m.invoke (invokeInstance, new Object[] { action, ev }); + return; + } catch (Exception ex) { + ErrorManager.getDefault ().notify (ex); + } + } + action.actionPerformed (ev); + } + /** * Adds action to runningActions map using runnable as a key. * @param r the block being run @@ -196,5 +222,56 @@ // whatever } */ + + /** Special class that can be passed to invokeAction and delegates + * to correct values + */ + abstract class ActionRunnable implements javax.swing.Action { + private ActionEvent ev; + + public ActionRunnable (ActionEvent ev) { + this.ev = ev; + } + + public final boolean needsToBeSynchronous () { + return "synchronous".equals (ev.getActionCommand ()); // NOI18N + } + + public final void doRun () { + invokeAction (this, ev); + } + + protected abstract void run (); + + public final void actionPerformed (ActionEvent e) { + run (); + } + + public final void addPropertyChangeListener (java.beans.PropertyChangeListener listener) { + throw new java.lang.UnsupportedOperationException (); + } + + public final Object getValue (String key) { + return CallableSystemAction.this.getValue (key); + } + + public final boolean isEnabled () { + return CallableSystemAction.this.isEnabled (); + } + + public final void putValue (String key, Object value) { + throw new java.lang.UnsupportedOperationException (); + } + + public final void removePropertyChangeListener (java.beans.PropertyChangeListener listener) { + throw new java.lang.UnsupportedOperationException (); + } + + public final void setEnabled (boolean b) { + throw new java.lang.UnsupportedOperationException (); + } + + } // end of ActionRunnable + } Index: openide/src/org/openide/util/actions/CallbackSystemAction.java =================================================================== RCS file: /cvs/openide/src/org/openide/util/actions/CallbackSystemAction.java,v retrieving revision 1.36 diff -u -r1.36 CallbackSystemAction.java --- openide/src/org/openide/util/actions/CallbackSystemAction.java 6 Sep 2003 16:17:37 -0000 1.36 +++ openide/src/org/openide/util/actions/CallbackSystemAction.java 13 Feb 2004 17:10:47 -0000 @@ -175,7 +175,7 @@ final ActionPerformer ap = getActionPerformer (); if (ap != null) { - doPerformAction(new Runnable() { + doPerformAction(new ActionRunnable (ev) { public void run() { ap.performAction(CallbackSystemAction.this); } @@ -430,10 +430,15 @@ /** Invoked when an action occurs. */ - public void actionPerformed(java.awt.event.ActionEvent e) { - javax.swing.Action a = findAction (); + public void actionPerformed(final java.awt.event.ActionEvent e) { + final javax.swing.Action a = findAction (); if (a != null) { - a.actionPerformed(e); + ActionRunnable run = delegate.new ActionRunnable (e) { + public void run () { + a.actionPerformed(e); + } + }; + delegate.doPerformAction (run); } else { // XXX #30303 if the action falls back to the old behaviour // it may not be performed in case it is in dialog and Index: openide/src/org/openide/util/actions/MouseCursorUtils.java =================================================================== RCS file: openide/src/org/openide/util/actions/MouseCursorUtils.java diff -N openide/src/org/openide/util/actions/MouseCursorUtils.java --- openide/src/org/openide/util/actions/MouseCursorUtils.java 25 Aug 2003 21:50:28 -0000 1.1 +++ /dev/null 1 Jan 1970 00:00:00 -0000 @@ -1,127 +0,0 @@ -/* - * Sun Public License Notice - * - * The contents of this file are subject to the Sun Public License - * Version 1.0 (the "License"). You may not use this file except in - * compliance with the License. A copy of the License is available at - * http://www.sun.com/ - * - * The Original Code is NetBeans. The Initial Developer of the Original - * Code is Sun Microsystems, Inc. Portions Copyright 1997-2003 Sun - * Microsystems, Inc. All Rights Reserved. - */ - -package org.openide.util.actions; - -import java.awt.Component; -import java.awt.Cursor; -import java.awt.EventQueue; -import java.awt.KeyboardFocusManager; -import java.awt.Window; -import java.util.HashMap; -import java.util.Map; -import javax.swing.RootPaneContainer; -import org.openide.ErrorManager; -import org.openide.util.Utilities; - -/** - * Static utility class for setting/resetting mouse cursor on surface of - * whatever window (e.g. main window) is associated with events. - * Event thread only. - * @author David Simonek, Jesse Glick - * @see "#27780" - */ -final class MouseCursorUtils { - - private static final ErrorManager err = ErrorManager.getDefault().getInstance("org.openide.util.actions.MouseCursorUtils"); // NOI18N - - private MouseCursorUtils() {} - - /** - * Running show/hide count for glass panes in use. - * Maps arbitrary keys to glass panes. - * Several keys may map to the same glass pane - the wait cursor is shown - * so long as there are any. - */ - private static final Map glassPaneUses = new HashMap(); // Map - - /** - * Try to find the active window's glass pane. - * @return a glass pane, or null - */ - private static Component activeGlassPane() { - Window w = KeyboardFocusManager.getCurrentKeyboardFocusManager().getActiveWindow(); - if (w instanceof RootPaneContainer) { - return ((RootPaneContainer)w).getGlassPane(); - } else { - return null; - } - } - - /** - * Sets wait cursor visible on the window associated with an event, if any. - * @param key something to pass to {@link #hideWaitCursor} to turn it off - */ - public static void showWaitCursor(Object key) { - assert EventQueue.isDispatchThread(); - assert !glassPaneUses.containsKey(key); - Component c = activeGlassPane(); - if (c == null) { - if (err.isLoggable(ErrorManager.WARNING)) { - err.log(ErrorManager.WARNING, "showWaitCursor could not find a suitable glass pane; key=" + key); - } - return; - } - if (glassPaneUses.values().contains(c)) { - if (err.isLoggable(ErrorManager.INFORMATIONAL)) { - err.log("wait cursor already displayed on " + c); - } - } else { - if (err.isLoggable(ErrorManager.INFORMATIONAL)) { - err.log("wait cursor will be displayed on " + c); - } - c.setCursor(Utilities.createProgressCursor(c)); - c.setVisible(true); - } - glassPaneUses.put(key, c); - } - - /** - * Resets cursor to default. - * @param key the same key passed to {@link #showWaitCursor} - */ - public static void hideWaitCursor(Object key) { - assert EventQueue.isDispatchThread(); - Component c = (Component)glassPaneUses.get(key); - if (c == null) { - return; - } - glassPaneUses.remove(key); - if (glassPaneUses.values().contains(c)) { - if (err.isLoggable(ErrorManager.INFORMATIONAL)) { - err.log("wait cursor still displayed on " + c); - } - } else { - if (err.isLoggable(ErrorManager.INFORMATIONAL)) { - err.log("wait cursor will be hidden on " + c); - } - c.setVisible(false); - c.setCursor(null); - } - } - - // XXX is this really necessary? - /** - * Defensive cursor reseting, resets cursor whenever some window is opened - * during the time action is performed. - * Please treat as this method as private, don't use outside this class. - * @param event incoming event - * / - public void eventDispatched(AWTEvent event) { - if (event.getID() == WindowEvent.WINDOW_OPENED) { - hideWaitCursor(); - } - } - */ - -} Index: openide/src/org/openide/util/actions/NodeAction.java =================================================================== RCS file: /cvs/openide/src/org/openide/util/actions/NodeAction.java,v retrieving revision 1.43 diff -u -r1.43 NodeAction.java --- openide/src/org/openide/util/actions/NodeAction.java 7 Jan 2004 13:19:45 -0000 1.43 +++ openide/src/org/openide/util/actions/NodeAction.java 13 Feb 2004 17:10:47 -0000 @@ -166,16 +166,16 @@ * a lookup containing all desired Node instances. * @param ev action event */ - public void actionPerformed(ActionEvent ev) { + public void actionPerformed(final ActionEvent ev) { final Object s = ev == null ? null : ev.getSource(); if (s instanceof Node) { - doPerformAction(new Runnable() { + doPerformAction(new ActionRunnable (ev) { public void run() { performAction(new Node[] {(Node)s}); } }); } else if (s instanceof Node[]) { - doPerformAction(new Runnable() { + doPerformAction(new ActionRunnable(ev) { public void run() { performAction((Node[])s); } @@ -435,7 +435,7 @@ /** Invoked when an action occurs. */ public void actionPerformed(ActionEvent e) { - delegate.doPerformAction(new Runnable() { + delegate.doPerformAction(delegate.new ActionRunnable(e) { public void run() { delegate.performAction(nodes()); } Index: openide/test/unit/src/org/openide/actions/AbstractCallbackActionTestHidden.java =================================================================== RCS file: /cvs/openide/test/unit/src/org/openide/actions/AbstractCallbackActionTestHidden.java,v retrieving revision 1.5 diff -u -r1.5 AbstractCallbackActionTestHidden.java --- openide/test/unit/src/org/openide/actions/AbstractCallbackActionTestHidden.java 11 Dec 2003 17:24:54 -0000 1.5 +++ openide/test/unit/src/org/openide/actions/AbstractCallbackActionTestHidden.java 13 Feb 2004 17:10:48 -0000 @@ -59,8 +59,13 @@ /** The key that is used in the action map */ protected abstract String actionKey (); + + protected boolean runInEQ () { + return true; + } + protected void setUp() throws Exception { global = (CallbackSystemAction)CallbackSystemAction.get (actionClass ()); map = new ActionMap (); @@ -74,7 +79,7 @@ } public void testThatDefaultEditorKitPasteActionIsTheCorrectKeyOfPasteAction () { - clone.actionPerformed (new java.awt.event.ActionEvent (this, 0, "")); + clone.actionPerformed (new java.awt.event.ActionEvent (this, 0, "synchronous")); action.assertCnt ("Clone correctly delegates to OurAction", 1); } Index: openide/test/unit/src/org/openide/actions/PasteActionTest.java =================================================================== RCS file: /cvs/openide/test/unit/src/org/openide/actions/PasteActionTest.java,v retrieving revision 1.4 diff -u -r1.4 PasteActionTest.java --- openide/test/unit/src/org/openide/actions/PasteActionTest.java 11 Dec 2003 17:24:55 -0000 1.4 +++ openide/test/unit/src/org/openide/actions/PasteActionTest.java 13 Feb 2004 17:10:48 -0000 @@ -116,7 +116,7 @@ assertTrue ("Enabled again", clone.isEnabled ()); - clone.actionPerformed (new java.awt.event.ActionEvent (this, 0, "")); + clone.actionPerformed (new java.awt.event.ActionEvent (this, 0, "synchronous")); arr[0].assertCnt ("First delegate invoked", 1); } @@ -141,7 +141,7 @@ action.putValue ("delegates", arr); assertTrue ("Enabled again", clone.isEnabled ()); - clone.actionPerformed (new java.awt.event.ActionEvent (this, 0, "")); + clone.actionPerformed (new java.awt.event.ActionEvent (this, 0, "synchronous")); arr[0].assertCnt ("First delegate invoked", 1); arr = new OurPasteType[] { new OurPasteType () }; @@ -149,7 +149,7 @@ action.putValue ("delegates", arr); assertTrue ("Enabled still", clone.isEnabled ()); - clone.actionPerformed (new java.awt.event.ActionEvent (this, 0, "")); + clone.actionPerformed (new java.awt.event.ActionEvent (this, 0, "synchronous")); arr[0].assertCnt ("First delegate invoked", 1); } Index: openide/test/unit/src/org/openide/explorer/ExplorerPanelTest.java =================================================================== RCS file: /cvs/openide/test/unit/src/org/openide/explorer/ExplorerPanelTest.java,v retrieving revision 1.9 diff -u -r1.9 ExplorerPanelTest.java --- openide/test/unit/src/org/openide/explorer/ExplorerPanelTest.java 12 Jan 2004 16:16:59 -0000 1.9 +++ openide/test/unit/src/org/openide/explorer/ExplorerPanelTest.java 13 Feb 2004 17:10:48 -0000 @@ -135,10 +135,10 @@ assertTrue("Copy action has to be enabled", copy.isEnabled()); assertTrue("Cut action has to be enabled", cut.isEnabled()); - copy.actionPerformed (new java.awt.event.ActionEvent (this, 0, "")); + copy.actionPerformed (new java.awt.event.ActionEvent (this, 0, "synchronous")); assertEquals ("clipboardCopy invoked", 1, enabledNode.countCopy); - cut.actionPerformed (new java.awt.event.ActionEvent (this, 0, "")); + cut.actionPerformed (new java.awt.event.ActionEvent (this, 0, "synchronous")); assertEquals ("clipboardCut invoked", 1, enabledNode.countCut); @@ -175,7 +175,7 @@ manager.setSelectedNodes (new Node[] { enabledNode, enabledNode2 }); assertTrue ("It gets enabled", delete.isEnabled ()); - delete.actionPerformed(new java.awt.event.ActionEvent (this, 0, "")); + delete.actionPerformed(new java.awt.event.ActionEvent (this, 0, "synchronous")); assertEquals ("Destoy was called", 1, enabledNode.countDelete); assertEquals ("Destoy was called", 1, enabledNode2.countDelete); @@ -209,7 +209,7 @@ assertTrue ("Paste is enabled", paste.isEnabled ()); - paste.actionPerformed(new java.awt.event.ActionEvent (this, 0, "")); + paste.actionPerformed(new java.awt.event.ActionEvent (this, 0, "synchronous")); assertEquals ("Paste invoked", 1, arr[0].count); manager.setSelectedNodes (new Node[] { disabledNode }); Index: openide/test/unit/src/org/openide/util/actions/AsynchronousTest.java =================================================================== RCS file: openide/test/unit/src/org/openide/util/actions/AsynchronousTest.java diff -N openide/test/unit/src/org/openide/util/actions/AsynchronousTest.java --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ openide/test/unit/src/org/openide/util/actions/AsynchronousTest.java 13 Feb 2004 17:10:48 -0000 @@ -0,0 +1,180 @@ +/* + * Sun Public License Notice + * + * The contents of this file are subject to the Sun Public License + * Version 1.0 (the "License"). You may not use this file except in + * compliance with the License. A copy of the License is available at + * http://www.sun.com/ + * + * The Original Code is NetBeans. The Initial Developer of the Original + * Code is Sun Microsystems, Inc. Portions Copyright 1997-2004 Sun + * Microsystems, Inc. All Rights Reserved. + */ + +package org.openide.util.actions; + +import java.awt.Image; +import java.awt.Toolkit; +import java.awt.event.ActionEvent; +import java.awt.image.BufferedImage; +import java.awt.image.ImageObserver; +import java.awt.image.PixelGrabber; +import javax.swing.Icon; +import javax.swing.JButton; +import org.netbeans.junit.*; +import junit.textui.TestRunner; +import org.openide.util.HelpCtx; +import org.openide.util.lookup.AbstractLookup; + +/** Test general aspects of system actions. + * Currently, just the icon. + * @author Jesse Glick + */ +public class AsynchronousTest extends NbTestCase { + + public AsynchronousTest (String name) { + super(name); + } + + public static void main(String[] args) { + System.setProperty("org.openide.util.Lookup", "org.openide.util.actions.AsynchronousTest$Lkp"); + TestRunner.run(new NbTestSuite(AsynchronousTest.class)); + } + + protected void setUp () { + ErrManager.messages.delete (0, ErrManager.messages.length ()); + } + + protected boolean runInEQ () { + return true; + } + + public void testExecutionOfActionsThatDoesNotOverrideAsynchronousIsAsynchronousButWarningIsPrinted () throws Exception { + DoesNotOverride action = (DoesNotOverride)DoesNotOverride.get (DoesNotOverride.class); + + synchronized (action) { + action.actionPerformed (new ActionEvent(this, 0, "")); + Thread.sleep (500); + assertFalse ("Not yet finished", action.finished); + action.wait (); + assertTrue ("The asynchronous action is finished", action.finished); + } + + if (ErrManager.messages.toString ().indexOf (DoesNotOverride.class.getName () + " should override") < 0) { + fail ("There should be warning about not overriding asynchronous: " + ErrManager.messages); + } + } + + public void testExecutionCanBeAsynchronous () throws Exception { + DoesOverrideAndReturnsTrue action = (DoesOverrideAndReturnsTrue)DoesOverrideAndReturnsTrue.get (DoesOverrideAndReturnsTrue.class); + + synchronized (action) { + action.actionPerformed (new ActionEvent(this, 0, "")); + Thread.sleep (500); + assertFalse ("Not yet finished", action.finished); + action.wait (); + assertTrue ("The asynchronous action is finished", action.finished); + } + + if (ErrManager.messages.toString ().indexOf (DoesOverrideAndReturnsTrue.class.getName ()) >= 0) { + fail ("No warning about the class: " + ErrManager.messages); + } + } + + public void testExecutionCanBeSynchronous () throws Exception { + DoesOverrideAndReturnsFalse action = (DoesOverrideAndReturnsFalse)DoesOverrideAndReturnsFalse.get (DoesOverrideAndReturnsFalse.class); + + synchronized (action) { + action.actionPerformed (new ActionEvent(this, 0, "")); + assertTrue ("The synchronous action is finished immediatelly", action.finished); + } + + if (ErrManager.messages.toString ().indexOf (DoesOverrideAndReturnsTrue.class.getName ()) >= 0) { + fail ("No warning about the class: " + ErrManager.messages); + } + } + + public void testExecutionCanBeForcedToBeSynchronous () throws Exception { + DoesOverrideAndReturnsTrue action = (DoesOverrideAndReturnsTrue)DoesOverrideAndReturnsTrue.get (DoesOverrideAndReturnsTrue.class); + + synchronized (action) { + action.actionPerformed (new ActionEvent(this, 0, "synchronous")); + assertTrue ("When asked for synchronous the action is finished immediatelly", action.finished); + } + + if (ErrManager.messages.toString ().indexOf (DoesOverrideAndReturnsTrue.class.getName ()) >= 0) { + fail ("No warning about the class: " + ErrManager.messages); + } + } + + public static class DoesNotOverride extends CallableSystemAction { + boolean finished; + + public HelpCtx getHelpCtx () { + return HelpCtx.DEFAULT_HELP; + } + + public String getName () { + return "Should warn action"; + } + + public synchronized void performAction () { + notifyAll (); + finished = true; + } + + } + + public static class DoesOverrideAndReturnsTrue extends DoesNotOverride { + public boolean asynchronous () { + return true; + } + } + + public static final class DoesOverrideAndReturnsFalse extends DoesOverrideAndReturnsTrue { + public boolean asynchronous () { + return false; + } + } + + + public static final class Lkp extends AbstractLookup { + public Lkp () { + this (new org.openide.util.lookup.InstanceContent ()); + } + + private Lkp (org.openide.util.lookup.InstanceContent ic) { + super (ic); + ic.add (new ErrManager ()); + } + } + + private static final class ErrManager extends org.openide.ErrorManager { + public static final StringBuffer messages = new StringBuffer (); + + public Throwable annotate (Throwable t, int severity, String message, String localizedMessage, Throwable stackTrace, java.util.Date date) { + return t; + } + + public Throwable attachAnnotations (Throwable t, org.openide.ErrorManager.Annotation[] arr) { + return t; + } + + public org.openide.ErrorManager.Annotation[] findAnnotations (Throwable t) { + return null; + } + + public org.openide.ErrorManager getInstance (String name) { + return this; + } + + public void log (int severity, String s) { + messages.append (s); + messages.append ('\n'); + } + + public void notify (int severity, Throwable t) { + } + + } +} Index: openide/test/unit/src/org/openide/util/actions/CallbackSystemActionTest.java =================================================================== RCS file: /cvs/openide/test/unit/src/org/openide/util/actions/CallbackSystemActionTest.java,v retrieving revision 1.8 diff -u -r1.8 CallbackSystemActionTest.java --- openide/test/unit/src/org/openide/util/actions/CallbackSystemActionTest.java 25 Aug 2003 21:50:31 -0000 1.8 +++ openide/test/unit/src/org/openide/util/actions/CallbackSystemActionTest.java 13 Feb 2004 17:10:48 -0000 @@ -39,6 +39,10 @@ // May have used AWT thread. System.exit(0); } + + protected boolean runInEQ () { + return true; + } /** Make sure that the performer system works and controls enablement. */ Index: core/execution/src/org/netbeans/core/execution/Install.java =================================================================== RCS file: /cvs/core/execution/src/org/netbeans/core/execution/Install.java,v retrieving revision 1.12 diff -u -r1.12 Install.java --- core/execution/src/org/netbeans/core/execution/Install.java 26 Nov 2003 16:33:29 -0000 1.12 +++ core/execution/src/org/netbeans/core/execution/Install.java 13 Feb 2004 17:10:49 -0000 @@ -287,6 +287,7 @@ ArrayList pendingTasks = new ArrayList( 10 ); // XXX no access to running actions at the moment //pendingTasks.addAll(CallableSystemAction.getRunningActions()); + pendingTasks.addAll(org.netbeans.core.ModuleActions.getDefaultInstance().getRunningActions()); if ( !Boolean.getBoolean( "netbeans.full.hack" ) ) { // NOI18N // Avoid showing the tasks in the dialog when running internal tests Index: core/src/org/netbeans/core/ModuleActions.java =================================================================== RCS file: /cvs/core/src/org/netbeans/core/ModuleActions.java,v retrieving revision 1.29 diff -u -r1.29 ModuleActions.java --- core/src/org/netbeans/core/ModuleActions.java 25 Aug 2003 21:50:20 -0000 1.29 +++ core/src/org/netbeans/core/ModuleActions.java 13 Feb 2004 17:10:50 -0000 @@ -13,7 +13,9 @@ package org.netbeans.core; +import java.awt.event.ActionEvent; import java.util.*; +import javax.swing.Action; import org.openide.actions.ActionManager; import org.openide.util.actions.SystemAction; @@ -35,6 +37,8 @@ private static Map map = new HashMap (7); /** current module */ private static Object module = null; + /** Map of currently running actions, (maps action event to action) */ + private Map runningActions = new HashMap(); public static ModuleActions getDefaultInstance() { return (ModuleActions)ActionManager.getDefault(); @@ -52,13 +56,61 @@ return a; } + /** Invokes action in a RequestPrecessor dedicated to performing + * actions. + */ + public void invokeAction(final Action a, final ActionEvent e) { + try { + java.awt.EventQueue.invokeLater(new Runnable() { + public void run() { + showWaitCursor(e); + } + }); + addRunningAction(a, e); + + a.actionPerformed (e); + } finally { + removeRunningAction(e); + java.awt.EventQueue.invokeLater(new Runnable() { + public void run() { + hideWaitCursor(e); + } + }); + } + } + /** Listens on change of modules and if changed, * fires change to all listeners. */ private void fireChange () { firePropertyChange(PROP_CONTEXT_ACTIONS, null, null); } + + /** Adds action to runningAction map using event as a key. + * @param rp RequestProcessor which runs the actio task + * @param action action to put in map + * @param evt action event used as key in the map */ + private void addRunningAction(Action action, ActionEvent evt) { + synchronized(runningActions) { + runningActions.put(evt, action); + } + } + + /** Removes action from runningAction map for key. + * @param evt action event used as a key in map */ + private void removeRunningAction(ActionEvent evt) { + synchronized(runningActions) { + runningActions.remove(evt); + } + } + /** Gets collection of currently running actions. */ + public Collection getRunningActions() { + synchronized(runningActions) { + return new ArrayList(runningActions.values()); + } + } + /** Change enabled property of an action * public void propertyChange (PropertyChangeEvent ev) { @@ -142,6 +194,83 @@ return (SystemAction[])arr.toArray (new SystemAction[arr.size ()]); } + + + private static final ErrorManager err = ErrorManager.getDefault().getInstance("org.openide.util.actions.MouseCursorUtils"); // NOI18N + + /** + * Running show/hide count for glass panes in use. + * Maps arbitrary keys to glass panes. + * Several keys may map to the same glass pane - the wait cursor is shown + * so long as there are any. + */ + private static final Map glassPaneUses = new HashMap(); // Map + + /** + * Try to find the active window's glass pane. + * @return a glass pane, or null + */ + private static java.awt.Component activeGlassPane() { + java.awt.Window w = java.awt.KeyboardFocusManager.getCurrentKeyboardFocusManager().getActiveWindow(); + if (w instanceof javax.swing.RootPaneContainer) { + return ((javax.swing.RootPaneContainer)w).getGlassPane(); + } else { + return null; + } + } + + /** + * Sets wait cursor visible on the window associated with an event, if any. + * @param key something to pass to {@link #hideWaitCursor} to turn it off + */ + public static void showWaitCursor(Object key) { + assert java.awt.EventQueue.isDispatchThread(); + assert !glassPaneUses.containsKey(key); + java.awt.Component c = activeGlassPane(); + if (c == null) { + if (err.isLoggable(ErrorManager.WARNING)) { + err.log(ErrorManager.WARNING, "showWaitCursor could not find a suitable glass pane; key=" + key); + } + return; + } + if (glassPaneUses.values().contains(c)) { + if (err.isLoggable(ErrorManager.INFORMATIONAL)) { + err.log("wait cursor already displayed on " + c); + } + } else { + if (err.isLoggable(ErrorManager.INFORMATIONAL)) { + err.log("wait cursor will be displayed on " + c); + } + c.setCursor(org.openide.util.Utilities.createProgressCursor(c)); + c.setVisible(true); + } + glassPaneUses.put(key, c); + } + + /** + * Resets cursor to default. + * @param key the same key passed to {@link #showWaitCursor} + */ + public static void hideWaitCursor(Object key) { + assert java.awt.EventQueue.isDispatchThread(); + java.awt.Component c = (java.awt.Component)glassPaneUses.get(key); + if (c == null) { + return; + } + glassPaneUses.remove(key); + if (glassPaneUses.values().contains(c)) { + if (err.isLoggable(ErrorManager.INFORMATIONAL)) { + err.log("wait cursor still displayed on " + c); + } + } else { + if (err.isLoggable(ErrorManager.INFORMATIONAL)) { + err.log("wait cursor will be hidden on " + c); + } + c.setVisible(false); + c.setCursor(null); + } + } + } Index: core/test/unit/src/org/netbeans/core/ModuleActionsTest.java =================================================================== RCS file: core/test/unit/src/org/netbeans/core/ModuleActionsTest.java diff -N core/test/unit/src/org/netbeans/core/ModuleActionsTest.java --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ core/test/unit/src/org/netbeans/core/ModuleActionsTest.java 13 Feb 2004 17:10:50 -0000 @@ -0,0 +1,79 @@ +/* + * Sun Public License Notice + * + * The contents of this file are subject to the Sun Public License + * Version 1.0 (the "License"). You may not use this file except in + * compliance with the License. A copy of the License is available at + * http://www.sun.com/ + * + * The Original Code is NetBeans. The Initial Developer of the Original + * Code is Sun Microsystems, Inc. Portions Copyright 1997-2003 Sun + * Microsystems, Inc. All Rights Reserved. + */ + +package org.netbeans.core; + +import org.netbeans.junit.*; +import junit.textui.TestRunner; + +/** Tests behaviour of asynchronous actions and exit dialog. + */ +public class ModuleActionsTest extends NbTestCase { + + public ModuleActionsTest (String name) { + super(name); + } + + public static void main(String[] args) { + TestRunner.run(new NbTestSuite(ModuleActionsTest.class)); + } + + protected boolean runInEQ () { + return true; + } + + public void testActionIsListedAsRunning () throws Exception { + Act act = (Act)Act.get (Act.class); + + synchronized (act) { + act.actionPerformed (new java.awt.event.ActionEvent (this, 0, "")); + act.wait (); + } + + java.util.Collection col = ModuleActions.getDefaultInstance ().getRunningActions (); + java.util.Iterator it = col.iterator (); + while (it.hasNext ()) { + javax.swing.Action a = (javax.swing.Action)it.next (); + if (a.getValue (javax.swing.Action.NAME) == act.getName ()) { + return; + } + } + fail ("Act should be running: " + col); + } + + public static class Act extends org.openide.util.actions.CallableSystemAction { + + public org.openide.util.HelpCtx getHelpCtx () { + return org.openide.util.HelpCtx.DEFAULT_HELP; + } + + public String getName () { + return getClass().getName (); + } + + public synchronized void performAction () { + notifyAll (); + try { + wait (); + } catch (InterruptedException ex) { + fail ("Shall not be interupted"); + } + } + + protected boolean asynchronous () { + return true; + } + + } + +}