diff --git a/core.ui/src/org/netbeans/core/ui/warmup/MenuWarmUpTask.java b/core.ui/src/org/netbeans/core/ui/warmup/MenuWarmUpTask.java --- a/core.ui/src/org/netbeans/core/ui/warmup/MenuWarmUpTask.java +++ b/core.ui/src/org/netbeans/core/ui/warmup/MenuWarmUpTask.java @@ -90,7 +90,7 @@ if (comps != null) { - walkMenu(comps); + // XXX disabling: walkMenu(comps); comps = null; } diff --git a/o.n.core/src/org/netbeans/core/NbKeymap.java b/o.n.core/src/org/netbeans/core/NbKeymap.java --- a/o.n.core/src/org/netbeans/core/NbKeymap.java +++ b/o.n.core/src/org/netbeans/core/NbKeymap.java @@ -41,20 +41,17 @@ package org.netbeans.core; +import java.awt.Toolkit; import java.awt.event.ActionEvent; import java.awt.event.KeyEvent; import java.util.ArrayList; -import java.util.Arrays; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; -import java.util.HashSet; -import java.util.Iterator; import java.util.List; import java.util.Map; -import java.util.Map.Entry; import java.util.Observable; -import java.util.Set; +import java.util.TreeMap; import java.util.logging.Level; import java.util.logging.Logger; import javax.swing.AbstractAction; @@ -62,7 +59,13 @@ import javax.swing.KeyStroke; import javax.swing.text.Keymap; import org.openide.awt.StatusDisplayer; -import org.openide.util.Mutex; +import org.openide.cookies.InstanceCookie; +import org.openide.filesystems.FileObject; +import org.openide.filesystems.FileUtil; +import org.openide.loaders.DataObject; +import org.openide.loaders.DataObjectNotFoundException; +import org.openide.loaders.DataShadow; +import org.openide.util.Utilities; import org.openide.util.actions.SystemAction; import org.openide.util.lookup.ServiceProvider; @@ -76,25 +79,116 @@ String name; /** Parent keymap */ Keymap parent; - /** Hashtable holding KeyStroke > Action mappings */ - Map bindings; /** Default action */ Action defaultAction; - /** hash table to map (Action -> ArrayList of KeyStrokes) */ - Map> actions; + + /** Represents a binding of a keystroke. */ + private static class Binding { + /** file defining an action; null if nested is not null */ + final FileObject actionDefinition; + /** lazily instantiated actual action, in case actionDefinition is not null */ + private Action action; + /** nested bindings; null if actionDefinition is not null */ + final Map nested; + Binding(FileObject def) { + actionDefinition = def; + nested = null; + } + Binding() { + actionDefinition = null; + nested = new HashMap(); + } + synchronized Action loadAction() { + assert actionDefinition != null; + if (action == null) { + try { + DataObject d = DataObject.find(actionDefinition); + InstanceCookie ic = d.getLookup().lookup(InstanceCookie.class); + if (ic == null) { + return null; + } + return (Action) ic.instanceCreate(); + } catch (/*ClassNotFoundException,IOException,ClassCastException*/Exception x) { + LOG.log(Level.WARNING, "could not load action for " + actionDefinition.getPath(), x); + return new AbstractAction("") { // NOI18N + public void actionPerformed(ActionEvent e) { + Toolkit.getDefaultToolkit().beep(); + } + }; + } + } + return action; + } + } + + private Map bindings; + private Map id2Stroke; + + private synchronized Map bindings() { + if (bindings == null) { + bindings = new HashMap(); + id2Stroke = new TreeMap(); + List dirs = new ArrayList(2); + dirs.add(FileUtil.getConfigFile("Shortcuts")); // NOI18N + FileObject keymaps = FileUtil.getConfigFile("Keymaps"); // NOI18N + if (keymaps != null) { + String curr = (String) keymaps.getAttribute("currentKeymap"); // NOI18N + if (curr == null) { + curr = "NetBeans"; // NOI18N + } + dirs.add(keymaps.getFileObject(curr)); + } + for (FileObject dir : dirs) { + if (dir != null) { + for (FileObject def : dir.getChildren()) { + if (def.isData()) { + KeyStroke[] strokes = Utilities.stringToKeys(def.getName()); + if (strokes == null || strokes.length == 0) { + LOG.log(Level.WARNING, "could not load parse name of " + def.getPath()); + continue; + } + Map binder = bindings; + for (int i = 0; i < strokes.length - 1; i++) { + Binding sub = binder.get(strokes[i]); + if (sub != null && sub.nested == null) { + LOG.log(Level.WARNING, "conflict between " + sub.actionDefinition.getPath() + " and " + def.getPath()); + sub = null; + } + if (sub == null) { + binder.put(strokes[i], sub = new Binding()); + } + binder = sub.nested; + } + // XXX warn about conflicts here too: + binder.put(strokes[strokes.length - 1], new Binding(def)); + if (strokes.length == 1) { + id2Stroke.put(idForFile(def), strokes[0]); + } + } + } + } + } + if (LOG.isLoggable(Level.FINE)) { + for (Map.Entry entry : id2Stroke.entrySet()) { + LOG.fine(entry.getValue() + " => " + entry.getKey()); + } + } + } + return bindings; + } - private static List context = new ArrayList(); + private static List context; - public static void resetContext() { + private static void resetContext() { context.clear(); StatusDisplayer.getDefault().setStatusText(""); } - public static KeyStroke[] getContext() { + public static KeyStroke[] getContext() { // called from ShortcutAndMenuKeyEventProcessor return context.toArray(new KeyStroke[context.size()]); } - - public static void shiftContext(KeyStroke stroke) { + + private static void shiftContext(KeyStroke stroke) { context.add(stroke); StringBuilder text = new StringBuilder(); @@ -114,15 +208,8 @@ KeyEvent.getKeyText (keyStroke.getKeyCode ()); } - private final Action NO_ACTION = new KeymapAction(null, null); private static final Logger LOG = Logger.getLogger(NbKeymap.class.getName()); - public Action createMapAction(Keymap k, KeyStroke stroke) { - return new KeymapAction(k, stroke); - } - - /** Default constructor - */ public NbKeymap() { this("Default", null); // NOI18N } @@ -130,7 +217,7 @@ NbKeymap(final String name, final Keymap parent) { this.name = name; this.parent = parent; - bindings = new HashMap(); + context = new ArrayList(); } public Action getDefaultAction() { @@ -152,21 +239,74 @@ return name; } - public Action getAction(KeyStroke key) { + private class Delegate extends AbstractAction { + final Action real; + Delegate(Action real) { + this.real = real; + } + public void actionPerformed(ActionEvent e) { + resetContext(); + if (real != null) { + real.actionPerformed(e); + } + } + public @Override boolean isEnabled() { + return real != null ? real.isEnabled() : true; + } + public @Override Object getValue(String key) { + return real != null ? real.getValue(key) : null; + } + public @Override void putValue(String key, Object newValue) { + if (real != null) { + real.putValue(key, newValue); + } + } + public @Override String toString() { + return String.valueOf(real); + } + } + + public Action getAction(final KeyStroke key) { LOG.log(Level.FINE, "getAction {0}", key); - + Map binder = bindings(); + for (KeyStroke ctx : context) { + Binding sub = binder.get(ctx); + if (sub == null) { + // Not found. + return new Delegate(null); + } + binder = sub.nested; + if (binder == null) { + // Probably impossible. + return new Delegate(null); + } + } + Binding b = binder.get(key); + if (b == null) { + // Not found. + return new Delegate(parent != null ? parent.getAction(key) : null); + } + if (b.nested == null) { + return new Delegate(b.loadAction()); + } else { + return new AbstractAction() { + public void actionPerformed(ActionEvent e) { + shiftContext(key); + } + }; + } + /* XXX pick up the weird context cancellation logic from here: Action a; - KeyStroke[] ctx = getContext(); Keymap activ = this; - for (int i=0; i> localActions = actions; - if (localActions == null) { - localActions = buildReverseMapping (); - } - - List strokes = localActions.get (a); - if (strokes != null) { - return strokes.toArray(new KeyStroke[strokes.size ()]); - } else { + FileObject definingFile = (FileObject) a.getValue("definingFile"); // cf. o.o.awt.Toolbar.setAccelerator + if (definingFile == null) { + LOG.log(Level.FINE, "no defining file known for {0}", id(a)); return new KeyStroke[0]; } + String id = idForFile(definingFile); + bindings(); + KeyStroke k = id2Stroke.get(id); + LOG.log(Level.FINE, "found keystroke {0} for {1} with ID {2}", new Object[] {k, id(a), id}); + // XXX return all available keystrokes, or make Toolbar.setAccelerator pick the most common, e.g. C-V over PASTE (use this as Comparator) + return k != null ? new KeyStroke[] {k} : new KeyStroke[0]; } - - private Map> buildReverseMapping () { - Map> localActions = actions = new HashMap> (); - - synchronized (this) { - for (Map.Entry curEntry: bindings.entrySet()) { - Action curAction = curEntry.getValue(); - KeyStroke curKey = curEntry.getKey(); - - List keysForAction = localActions.get (curAction); - if (keysForAction == null) { - keysForAction = Collections.synchronizedList (new ArrayList (1)); - localActions.put (curAction, keysForAction); + /** + * Traverses shadow files to origin. + * Returns impl class name if that is obvious (common for SystemAction's); + * else just returns file path (usual for more modern registrations). + */ + private static String idForFile(FileObject f) { + if (f.hasExt("shadow")) { + String path = (String) f.getAttribute("originalFile"); + if (path != null) { + f = FileUtil.getConfigFile(path); + } else { + try { + f = ((DataShadow) DataObject.find(f)).getOriginal().getPrimaryFile(); + } catch (DataObjectNotFoundException x) { + LOG.log(Level.INFO, f.getPath(), x); } - keysForAction.add (curKey); } } - - return localActions; + // Cannot actually load instanceCreate methodvalue=... attribute; just want to see if it is there. + if (f.hasExt("instance") && !Collections.list(f.getAttributes()).contains("instanceCreate")) { + String clazz = (String) f.getAttribute("instanceClass"); + if (clazz != null) { + return clazz; + } else { + return f.getName().replace('-', '.'); + } + } + return f.getPath(); } public synchronized boolean isLocallyDefined(KeyStroke key) { - LOG.log(Level.FINE, "isLocallyDefined {0}", key); - return bindings.containsKey(key); + assert false; + return false; } - /** Updates action accelerator. */ - private void updateActionAccelerator(final Action a) { - if(a == null) { - return; - } - - Mutex.EVENT.writeAccess(new Runnable() { - public void run() { - KeyStroke[] keystrokes = getKeyStrokesForAction(a); - Arrays.sort (keystrokes, NbKeymap.this); - a.putValue(Action.ACCELERATOR_KEY, keystrokes.length > 0 ? keystrokes[0] : null); - } - }); - } - public int compare(KeyStroke k1, KeyStroke k2) { //#47024 and 32733 - "Find" should not be shown as an accelerator, //nor should "Backspace" for Delete. Solution: The shorter text wins. @@ -308,70 +425,15 @@ public void addActionForKeyStroke(KeyStroke key, Action a) { - LOG.log(Level.FINE, "addActionForKeyStroke {0} => {1}", new Object[] { key, id(a) }); - // Update reverse binding for old action too (#30455): - Action old; - synchronized (this) { - old = bindings.put(key, a); - actions = null; - } - - updateActionAccelerator(a); - updateActionAccelerator(old); - setChanged(); - notifyObservers(); - } - - void addActionForKeyStrokeMap(Map map) { - Set actionsSet = new HashSet(); - synchronized (this) { - for (Entry entry: map.entrySet ()) { - KeyStroke key = entry.getKey(); - Action value = entry.getValue(); - // Add both old and new action: - actionsSet.add(value); - actionsSet.add(bindings.put(key, value)); - } - actions = null; - } - - for(Action a: actionsSet) { - updateActionAccelerator(a); - } - - setChanged(); - notifyObservers(); + assert false; } public void removeKeyStrokeBinding(KeyStroke key) { - LOG.log(Level.FINE, "removeKeyStrokeBinding {0}", key); - - Action a; - synchronized (this) { - a = bindings.remove(key); - actions = null; - } - updateActionAccelerator(a); - setChanged(); - notifyObservers(); + assert false; } public void removeBindings() { - LOG.log(Level.FINE, "removeBindings"); - - Set actionsSet; - synchronized (this) { - actionsSet = new HashSet(bindings.values()); - bindings.clear(); - actions = null; - } - - for(Action a: actionsSet) { - updateActionAccelerator(a); - } - - setChanged(); - notifyObservers(); + assert false; } public Keymap getResolveParent() { @@ -385,13 +447,6 @@ notifyObservers(); } - /** Returns string representation - can be looong. - */ - @Override - public String toString() { - return "Keymap[" + name + "]" + bindings; // NOI18N - } - private static Object id(Action a) { if (a instanceof SystemAction) { return a.getClass(); @@ -468,25 +523,4 @@ } - public static class KeymapAction extends AbstractAction { - private Keymap keymap; - private KeyStroke stroke; - - public KeymapAction(Keymap keymap, KeyStroke stroke) { - this.keymap = keymap; - this.stroke = stroke; - } - - public Keymap getSubMap() { - return keymap; - } - - public void actionPerformed(ActionEvent e) { - if (stroke == null) { // NO_ACTION -> reset - resetContext(); - } else { - shiftContext(stroke); - } - } - } } diff --git a/o.n.core/src/org/netbeans/core/NonGui.java b/o.n.core/src/org/netbeans/core/NonGui.java --- a/o.n.core/src/org/netbeans/core/NonGui.java +++ b/o.n.core/src/org/netbeans/core/NonGui.java @@ -147,13 +147,6 @@ Splash.getInstance().increment(10); StartLog.logProgress ("Timer initialized"); // NOI18N - // ----------------------------------------------------------------------------------------------------- - // 13. Initialize Shortcuts - ShortcutsFolder.initShortcuts(); - Splash.getInstance().increment(1); - StartLog.logProgress ("Shortcuts initialized"); // NOI18N - - // ----------------------------------------------------------------------------------------------------- // 14. Open main window StatusDisplayer.getDefault().setStatusText (NbBundle.getMessage (NonGui.class, "MSG_WindowShowInit")); diff --git a/o.n.core/src/org/netbeans/core/ShortcutsFolder.java b/o.n.core/src/org/netbeans/core/ShortcutsFolder.java deleted file mode 100644 --- a/o.n.core/src/org/netbeans/core/ShortcutsFolder.java +++ /dev/null @@ -1,236 +0,0 @@ -/* - * 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.netbeans.core; - -import java.io.IOException; -import java.util.Collection; -import java.util.Enumeration; -import java.util.LinkedList; -import java.util.logging.Level; -import java.util.logging.Logger; -import javax.swing.Action; -import javax.swing.KeyStroke; -import javax.swing.text.Keymap; -import org.netbeans.core.NbKeymap.KeymapAction; -import org.netbeans.core.startup.StartLog; -import org.openide.cookies.InstanceCookie; -import org.openide.filesystems.FileAttributeEvent; -import org.openide.filesystems.FileChangeAdapter; -import org.openide.filesystems.FileEvent; -import org.openide.filesystems.FileObject; -import org.openide.filesystems.FileUtil; -import org.openide.loaders.DataFolder; -import org.openide.loaders.DataObject; -import org.openide.util.Exceptions; -import org.openide.util.Lookup; -import org.openide.util.RequestProcessor; -import org.openide.util.Utilities; - -/** - * Bridge to old layers based system. - * - * @author Jan Jancura - */ -class ShortcutsFolder { - - private static final String PROFILES_FOLDER = "Keymaps"; - private static final String SHORTCUTS_FOLDER = "Shortcuts"; - private static final String CURRENT_PROFILE_ATTRIBUTE = "currentKeymap"; - - private static ShortcutsFolder shortcutsFolder; - private Listener listener = new Listener (); - private FileObject profilesFileObject; - private FileObject shortcutsFileObject; - private FileObject currentFolder; - private Logger debug = Logger.getLogger(ShortcutsFolder.class.getName ()); - private Collection dataObjects; - - - static void initShortcuts () { - StartLog.logStart("initShortcuts"); - try { - if (shortcutsFolder != null) return; - shortcutsFolder = new ShortcutsFolder (); - } finally { - StartLog.logEnd("initShortcuts"); - } - } - - private ShortcutsFolder () { - try { - FileObject root = FileUtil.getConfigRoot (); - profilesFileObject = root.getFileObject (PROFILES_FOLDER); - if (profilesFileObject == null) - profilesFileObject = root.createFolder (PROFILES_FOLDER); - profilesFileObject.addFileChangeListener (listener); - - shortcutsFileObject = root.getFileObject (SHORTCUTS_FOLDER); - if (shortcutsFileObject == null) - shortcutsFileObject = root.createFolder (SHORTCUTS_FOLDER); - shortcutsFileObject.addFileChangeListener (listener); - } catch (IOException ex) { - Exceptions.printStackTrace(ex); - } - refresh (); - } - - static void waitFinished () { - shortcutsFolder.listener.task.waitFinished (); - } - - private void refresh () { - - // get keymap and delete old shortcuts - NbKeymap keymap = (NbKeymap) Lookup.getDefault ().lookup (Keymap.class); - keymap.removeBindings (); - dataObjects = new LinkedList(); - - // update main shortcuts - readShortcuts (keymap, shortcutsFileObject); - - // update shortcuts from profile - String keymapName = (String) profilesFileObject.getAttribute - (CURRENT_PROFILE_ATTRIBUTE); - if (keymapName == null || "".equals (keymapName)) - keymapName = "NetBeans"; // NOI18N - if (currentFolder != null) - currentFolder.removeFileChangeListener (listener); - currentFolder = FileUtil.getConfigFile (PROFILES_FOLDER + '/' + keymapName); - if (currentFolder == null) { - try { - currentFolder = profilesFileObject.createFolder(keymapName); - } catch (IOException ioe) { - Exceptions.printStackTrace(ioe); - } - } - if (currentFolder != null) { - readShortcuts (keymap, currentFolder); - // add listener to current profile folder - currentFolder.addFileChangeListener (listener); - } - } - - - private void readShortcuts (NbKeymap keymap, FileObject fileObject) { - debug.fine("\nreadShortcuts " + fileObject); - DataFolder folder = DataFolder.findFolder (fileObject); - Enumeration en = folder.children(false); - while (en.hasMoreElements ()) { - DataObject dataObject = en.nextElement(); - if (dataObject instanceof DataFolder) continue; - InstanceCookie ic = dataObject.getCookie(InstanceCookie.class); - if (ic == null) continue; - try { - Action action = (Action) ic.instanceCreate (); - String shortcuts = dataObject.getName (); - debug.fine(" " + shortcuts + " : " + action); - KeyStroke[] keyStrokes = Utilities.stringToKeys (shortcuts); - if (keyStrokes != null) { - addShortcut(keymap, action, keyStrokes); - } else { // see e.g. secondary exception in #74169 - debug.warning("Unrecognized shortcut name from " + dataObject.getPrimaryFile().getPath()); // NOI18N - } - //remember DataObjects used to create the Actions so that there are - //the same Action instances in the menu - dataObjects.add( dataObject ); - } catch (ClassNotFoundException x) { - Logger.getLogger(ShortcutsFolder.class.getName()).log(Level.WARNING, - "{0} ignored; cannot load class {1}", - new Object[] {dataObject.getPrimaryFile().getPath(), ic.instanceName()}); - } catch (Exception ex) { - Logger.getLogger(ShortcutsFolder.class.getName()).log(Level.WARNING, null, ex); - } - } - } - - private static void addShortcut ( - NbKeymap keymap, - Action action, - KeyStroke[] keyStrokes - ) { - Keymap currentKeymap = keymap; - int i, k = keyStrokes.length - 1; - for (i = 0; i < k; i++) { - Action a = currentKeymap.getAction (keyStrokes [i]); - if (a == null) { - a = keymap.createMapAction - (new NbKeymap.SubKeymap (null), keyStrokes [i]); - currentKeymap.addActionForKeyStroke (keyStrokes [i], a); - } - if (!(a instanceof KeymapAction)) return; - currentKeymap = ((KeymapAction) a).getSubMap (); - } - currentKeymap.addActionForKeyStroke (keyStrokes [k], action); - } - - private class Listener extends FileChangeAdapter implements Runnable { - - private RequestProcessor.Task task = new RequestProcessor ("ShortcutsFolder").create (this); - - public void run () { - refresh (); - } - - @Override - public void fileDataCreated (FileEvent fe) { - task.schedule (500); - } - - @Override - public void fileChanged (FileEvent fe) { - task.schedule (500); - } - - @Override - public void fileDeleted (FileEvent fe) { - task.schedule (500); - } - - @Override - public void fileAttributeChanged (FileAttributeEvent fe) { - if (fe.getName () != null && - !CURRENT_PROFILE_ATTRIBUTE.equals (fe.getName ()) - ) return; - task.schedule (500); - } - } -} diff --git a/o.n.core/test/unit/src/org/netbeans/core/NbKeymapTest.java b/o.n.core/test/unit/src/org/netbeans/core/NbKeymapTest.java --- a/o.n.core/test/unit/src/org/netbeans/core/NbKeymapTest.java +++ b/o.n.core/test/unit/src/org/netbeans/core/NbKeymapTest.java @@ -42,183 +42,211 @@ package org.netbeans.core; import java.awt.event.ActionEvent; -import java.beans.PropertyChangeEvent; -import java.beans.PropertyChangeListener; +import java.awt.event.KeyEvent; +import java.io.IOException; +import java.io.OutputStream; import java.util.Arrays; import java.util.Collections; -import java.util.HashMap; -import java.util.Map; -import java.util.Observable; -import java.util.Observer; +import java.util.concurrent.atomic.AtomicReference; import javax.swing.AbstractAction; import javax.swing.Action; import javax.swing.KeyStroke; import javax.swing.text.Keymap; +import org.netbeans.junit.MockServices; import org.netbeans.junit.NbTestCase; +import org.openide.filesystems.FileObject; +import org.openide.filesystems.FileUtil; +import org.openide.loaders.DataFolder; +import org.openide.loaders.DataObject; +import org.openide.loaders.DataShadow; +import org.openide.loaders.Environment; +import org.openide.loaders.InstanceSupport; +import org.openide.loaders.XMLDataObject; +import org.openide.util.HelpCtx; +import org.openide.util.Lookup; +import org.openide.util.actions.CallableSystemAction; +import org.openide.util.lookup.Lookups; +import org.w3c.dom.Document; -/** Test NbKeymap. - * @author Jesse Glick - * @see "#30455" */ public class NbKeymapTest extends NbTestCase { public NbKeymapTest(String name) { super(name); } - - @Override - protected boolean runInEQ () { - return true; + + static { + System.setProperty("os.name", "Linux"); // just to standardize modifier key binding + } + + protected @Override void setUp() throws Exception { + for (FileObject f : FileUtil.getConfigRoot().getChildren()) { + f.delete(); + } } - public void testBasicFunctionality() throws Exception { - Keymap km = new NbKeymap(); - Action a1 = new DummyAction("a1"); - Action a2 = new DummyAction("a2"); - Action d = new DummyAction("d"); - KeyStroke k1 = KeyStroke.getKeyStroke("X"); - KeyStroke k2 = KeyStroke.getKeyStroke("Y"); - assertFalse(k1.equals(k2)); - assertNull(km.getAction(k1)); - assertNull(km.getAction(k2)); - assertEquals(Collections.EMPTY_LIST, Arrays.asList(km.getBoundActions())); - assertEquals(Collections.EMPTY_LIST, Arrays.asList(km.getBoundKeyStrokes())); - assertNull(km.getDefaultAction()); - km.setDefaultAction(d); - assertEquals(d, km.getDefaultAction()); - km.addActionForKeyStroke(k1, a1); - assertEquals(a1, km.getAction(k1)); - assertTrue(km.isLocallyDefined(k1)); - assertEquals(null, km.getAction(k2)); - assertEquals(Collections.singletonList(a1), Arrays.asList(km.getBoundActions())); - assertEquals(Collections.singletonList(k1), Arrays.asList(km.getBoundKeyStrokes())); - km.addActionForKeyStroke(k2, a2); - assertEquals(a1, km.getAction(k1)); - assertEquals(a2, km.getAction(k2)); - assertEquals(2, km.getBoundActions().length); - assertEquals(2, km.getBoundKeyStrokes().length); - km.addActionForKeyStroke(k1, d); - assertEquals(d, km.getAction(k1)); - assertEquals(a2, km.getAction(k2)); - assertEquals(2, km.getBoundActions().length); - assertEquals(2, km.getBoundKeyStrokes().length); - assertEquals(Collections.EMPTY_LIST, Arrays.asList(km.getKeyStrokesForAction(a1))); - assertEquals(Collections.singletonList(k2), Arrays.asList(km.getKeyStrokesForAction(a2))); - assertEquals(Collections.singletonList(k1), Arrays.asList(km.getKeyStrokesForAction(d))); - km.removeKeyStrokeBinding(k2); - assertEquals(d, km.getAction(k1)); - assertNull(km.getAction(k2)); - assertEquals(Collections.singletonList(d), Arrays.asList(km.getBoundActions())); - assertEquals(Collections.singletonList(k1), Arrays.asList(km.getBoundKeyStrokes())); - km.removeBindings(); - assertNull(km.getAction(k1)); - assertNull(km.getAction(k2)); - assertEquals(Collections.EMPTY_LIST, Arrays.asList(km.getBoundActions())); - assertEquals(Collections.EMPTY_LIST, Arrays.asList(km.getBoundKeyStrokes())); + private FileObject make(String path) throws IOException { + return FileUtil.createData(FileUtil.getConfigRoot(), path); } - - public void testObservability() throws Exception { - NbKeymap km = new NbKeymap(); - O o = new O(); - km.addObserver(o); - assertFalse(o.changed); - Action a1 = new DummyAction("a1"); - Action a2 = new DummyAction("a2"); - KeyStroke k1 = KeyStroke.getKeyStroke("X"); - km.addActionForKeyStroke(k1, a1); - assertTrue(o.changed); - o.changed = false; - km.addActionForKeyStroke(k1, a2); - assertTrue(o.changed); - o.changed = false; - km.removeKeyStrokeBinding(k1); - assertTrue(o.changed); + + private FileObject makeFolder(String path) throws IOException { + return FileUtil.createFolder(FileUtil.getConfigRoot(), path); + } + + private void assertMapping(Keymap km, KeyStroke stroke, FileObject presenterDefinition, String actionName) throws Exception { + Action a = km.getAction(stroke); + assertNotNull("for " + stroke, a); + assertEquals(actionName, a.getValue(Action.NAME)); + a.putValue("definingFile", presenterDefinition); + assertEquals("for " + stroke + " from " + presenterDefinition.getPath(), + Collections.singletonList(stroke), Arrays.asList(km.getKeyStrokesForAction(a))); } public void testAcceleratorMapping() throws Exception { + FileObject def1 = make("Actions/DummyAction1.instance"); + def1.setAttribute("instanceCreate", new DummyAction("one")); + FileObject def2 = make("Actions/DummyAction2.instance"); + def2.setAttribute("instanceCreate", new DummyAction("two")); + FileObject def3 = make("Actions/DummySystemAction1.instance"); + def3.setAttribute("instanceClass", DummySystemAction1.class.getName()); + FileObject def4 = make("Actions/" + DummySystemAction2.class.getName().replace('.', '-') + ".instance"); + DataFolder shortcuts = DataFolder.findFolder(makeFolder("Shortcuts")); + DataShadow.create(shortcuts, "1", DataObject.find(def1)).getPrimaryFile(); + DataShadow.create(shortcuts, "2", DataObject.find(def2)).getPrimaryFile(); + DataShadow.create(shortcuts, "3", DataObject.find(def3)).getPrimaryFile(); + DataShadow.create(shortcuts, "C-4", DataObject.find(def4)).getPrimaryFile(); + DataFolder menu = DataFolder.findFolder(makeFolder("Menu/Tools")); + FileObject menuitem1 = DataShadow.create(menu, "whatever1", DataObject.find(def1)).getPrimaryFile(); + FileObject menuitem2 = DataShadow.create(menu, "whatever2", DataObject.find(def2)).getPrimaryFile(); + FileObject menuitem3 = DataShadow.create(menu, "whatever3", DataObject.find(def3)).getPrimaryFile(); + FileObject menuitem4 = DataShadow.create(menu, "whatever4", DataObject.find(def4)).getPrimaryFile(); Keymap km = new NbKeymap(); - Action a1 = new DummyAction("a1"); - Action a2 = new DummyAction("a2"); - KeyStroke k1 = KeyStroke.getKeyStroke("X"); - KeyStroke k2 = KeyStroke.getKeyStroke("Y"); - assertNull(a1.getValue(Action.ACCELERATOR_KEY)); - assertNull(a2.getValue(Action.ACCELERATOR_KEY)); - AccL l = new AccL(); - a1.addPropertyChangeListener(l); - assertFalse(l.changed); - km.addActionForKeyStroke(k1, a1); - assertEquals(k1, a1.getValue(Action.ACCELERATOR_KEY)); - assertTrue(l.changed); - l.changed = false; - km.addActionForKeyStroke(k2, a2); - assertEquals(k2, a2.getValue(Action.ACCELERATOR_KEY)); - km.addActionForKeyStroke(k2, a1); - Object acc = a1.getValue(Action.ACCELERATOR_KEY); - assertTrue(acc == k1 || acc == k2); - assertNull(a2.getValue(Action.ACCELERATOR_KEY)); - km.removeKeyStrokeBinding(k1); - assertEquals(k2, a1.getValue(Action.ACCELERATOR_KEY)); - km.removeKeyStrokeBinding(k2); - assertNull(a1.getValue(Action.ACCELERATOR_KEY)); - assertTrue(l.changed); + assertMapping(km, KeyStroke.getKeyStroke(KeyEvent.VK_1, 0), menuitem1, "one"); + assertMapping(km, KeyStroke.getKeyStroke(KeyEvent.VK_2, 0), menuitem2, "two"); + assertMapping(km, KeyStroke.getKeyStroke(KeyEvent.VK_3, 0), menuitem3, "DummySystemAction1"); + assertMapping(km, KeyStroke.getKeyStroke(KeyEvent.VK_4, KeyEvent.CTRL_MASK), menuitem4, "DummySystemAction2"); + } + + public void testMultipleAcceleratorMapping() throws Exception { + // XXX + // create >1 registration and test that most basic is returned, e.g. Ctrl-V over Paste } - public void testAddActionForKeyStrokeMap() throws Exception { - NbKeymap km = new NbKeymap(); - O o = new O(); - km.addObserver(o); - Action a1 = new DummyAction("a1"); - Action a2 = new DummyAction("a2"); - Action a3 = new DummyAction("a3"); - KeyStroke k1 = KeyStroke.getKeyStroke("X"); - KeyStroke k2 = KeyStroke.getKeyStroke("Y"); - Map m = new HashMap(); - m.put(k1, a1); - m.put(k2, a2); - km.addActionForKeyStrokeMap(m); - assertTrue(o.changed); - assertEquals(a1, km.getAction(k1)); - assertEquals(a2, km.getAction(k2)); - assertEquals(k1, a1.getValue(Action.ACCELERATOR_KEY)); - assertEquals(k2, a2.getValue(Action.ACCELERATOR_KEY)); - assertEquals(2, km.getBoundActions().length); - assertEquals(2, km.getBoundKeyStrokes().length); - km.removeBindings(); - km.addActionForKeyStroke(k1, a3); - km.addActionForKeyStrokeMap(m); - assertEquals(a1, km.getAction(k1)); - assertEquals(a2, km.getAction(k2)); - assertEquals(k1, a1.getValue(Action.ACCELERATOR_KEY)); - assertEquals(k2, a2.getValue(Action.ACCELERATOR_KEY)); - assertNull(a3.getValue(Action.ACCELERATOR_KEY)); - assertEquals(2, km.getBoundActions().length); - assertEquals(2, km.getBoundKeyStrokes().length); + public void testUnusualInstanceFileExtensions() throws Exception { + MockServices.setServices(ENV.class); + FileObject inst = make("Shortcuts/C-F11.xml"); + OutputStream os = inst.getOutputStream(); + os.write("".getBytes()); + os.close(); + assertMapping(new NbKeymap(), KeyStroke.getKeyStroke(KeyEvent.VK_F11, KeyEvent.CTRL_MASK), inst, "whatever"); + } + + public void testAbstractModifiers() throws Exception { + Keymap km = new NbKeymap(); + FileObject inst1 = make("Shortcuts/D-1.instance"); + inst1.setAttribute("instanceCreate", new DummyAction("one")); + FileObject inst2 = make("Shortcuts/O-1.instance"); + inst2.setAttribute("instanceCreate", new DummyAction("two")); + assertMapping(km, KeyStroke.getKeyStroke(KeyEvent.VK_1, KeyEvent.CTRL_MASK), inst1, "one"); + assertMapping(km, KeyStroke.getKeyStroke(KeyEvent.VK_1, KeyEvent.ALT_MASK), inst2, "two"); + } + + public void testDifferentKeymaps() throws Exception { + // XXX + // Switch to various keymaps and make sure getAction works. + // Existing actions ought to have their accelerators changed if you change keymap. + } + + public void testMultiKeyShortcuts() throws Exception { + final AtomicReference ran = new AtomicReference(); + class A extends AbstractAction { + final String s; + A(String s) { + this.s = s; + } + public void actionPerformed(ActionEvent e) { + ran.set(s); + } + } + make("Shortcuts/C-X 1.instance").setAttribute("instanceCreate", new A("C-X 1")); + make("Shortcuts/C-X 2.instance").setAttribute("instanceCreate", new A("C-X 2")); + make("Shortcuts/C-U A B.instance").setAttribute("instanceCreate", new A("C-U A B")); + Keymap km = new NbKeymap(); + Action a = km.getAction(KeyStroke.getKeyStroke(KeyEvent.VK_X, KeyEvent.CTRL_MASK)); + assertNotNull(a); + a.actionPerformed(null); + assertEquals(null, ran.getAndSet(null)); + a = km.getAction(KeyStroke.getKeyStroke(KeyEvent.VK_1, 0)); + assertNotNull(a); + a.actionPerformed(null); + assertEquals("C-X 1", ran.getAndSet(null)); + a = km.getAction(KeyStroke.getKeyStroke(KeyEvent.VK_X, KeyEvent.CTRL_MASK)); + assertNotNull(a); + a.actionPerformed(null); + assertEquals(null, ran.getAndSet(null)); + a = km.getAction(KeyStroke.getKeyStroke(KeyEvent.VK_2, 0)); + assertNotNull(a); + a.actionPerformed(null); + assertEquals("C-X 2", ran.getAndSet(null)); + a = km.getAction(KeyStroke.getKeyStroke(KeyEvent.VK_U, KeyEvent.CTRL_MASK)); + assertNotNull(a); + a.actionPerformed(null); + assertEquals(null, ran.getAndSet(null)); + a = km.getAction(KeyStroke.getKeyStroke(KeyEvent.VK_A, 0)); + assertNotNull(a); + a.actionPerformed(null); + assertEquals(null, ran.getAndSet(null)); + a = km.getAction(KeyStroke.getKeyStroke(KeyEvent.VK_B, 0)); + assertNotNull(a); + a.actionPerformed(null); + assertEquals("C-U A B", ran.getAndSet(null)); + } + + public void testChangesInShortcutRegistrations() throws Exception { + // XXX + // Add, remove shortcut registrations and see what happens. + } + + private static final class DummyAction extends AbstractAction { + public DummyAction(String name) { + super(name); + } + public void actionPerformed(ActionEvent e) {} } - private static final class DummyAction extends AbstractAction { - private final String name; - public DummyAction(String name) { - this.name = name; - } - public void actionPerformed(ActionEvent e) {} - @Override - public String toString() { - return "DummyAction[" + name + "]"; + public static class ENV extends Object implements Environment.Provider { + public Lookup getEnvironment(DataObject obj) { + if (obj instanceof XMLDataObject) { + try { + Document doc = ((XMLDataObject) obj).getDocument(); + if (doc.getDocumentElement().getNodeName().equals("action")) { + return Lookups.singleton(new InstanceSupport.Instance(new DummyAction("whatever"))); + } + } catch (Exception ex) { + ex.printStackTrace(); + fail("No exception: " + ex.getMessage()); + } + } + return Lookup.EMPTY; } } - - private static final class O implements Observer { - public boolean changed = false; - public void update(Observable o, Object arg) { - changed = true; + + public static final class DummySystemAction1 extends CallableSystemAction { + public void performAction() {} + public String getName() { + return "DummySystemAction1"; + } + public HelpCtx getHelpCtx() { + return null; } } - - private static final class AccL implements PropertyChangeListener { - public boolean changed = false; - public void propertyChange(PropertyChangeEvent evt) { - if (Action.ACCELERATOR_KEY.equals(evt.getPropertyName())) { - changed = true; - } + + public static final class DummySystemAction2 extends CallableSystemAction { + public void performAction() {} + public String getName() { + return "DummySystemAction2"; + } + public HelpCtx getHelpCtx() { + return null; } } - + } diff --git a/o.n.core/test/unit/src/org/netbeans/core/ShortcutsFolder66845Test.java b/o.n.core/test/unit/src/org/netbeans/core/ShortcutsFolder66845Test.java deleted file mode 100644 --- a/o.n.core/test/unit/src/org/netbeans/core/ShortcutsFolder66845Test.java +++ /dev/null @@ -1,79 +0,0 @@ -/* - * 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.netbeans.core; - -import java.util.logging.Level; -import javax.swing.text.Keymap; -import org.netbeans.core.startup.Main; -import org.netbeans.junit.Log; -import org.netbeans.junit.NbTestCase; -import org.openide.filesystems.FileUtil; -import org.openide.util.Lookup; - -public class ShortcutsFolder66845Test extends NbTestCase { - - public ShortcutsFolder66845Test(String s) { - super(s); - } - - private Keymap keymap; - private CharSequence logs; - - @Override - protected void setUp() throws Exception { - Main.initializeURLFactory (); - keymap = Lookup.getDefault().lookup(Keymap.class); - assertNotNull("There is a keymap", keymap); - assertEquals("of correct type", NbKeymap.class, keymap.getClass()); - ShortcutsFolder.initShortcuts (); - logs = Log.enable(ShortcutsFolder.class.getName(), Level.WARNING); - } - - public void testLogging() throws Exception { - FileUtil.createData(FileUtil.getConfigRoot(), "Keymaps/NetBeans/org-nb-Neznam.instance"); - - ShortcutsFolder.waitFinished (); - - assertTrue("got message in " + logs, logs.toString().contains("Neznam")); - } - -} diff --git a/o.n.core/test/unit/src/org/netbeans/core/ShortcutsFolderTest.java b/o.n.core/test/unit/src/org/netbeans/core/ShortcutsFolderTest.java deleted file mode 100644 --- a/o.n.core/test/unit/src/org/netbeans/core/ShortcutsFolderTest.java +++ /dev/null @@ -1,206 +0,0 @@ -/* - * 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.netbeans.core; - -import java.awt.Toolkit; -import java.awt.event.ActionEvent; -import java.awt.event.KeyEvent; -import java.io.IOException; -import java.lang.ref.Reference; -import java.lang.ref.WeakReference; -import java.util.Arrays; -import java.util.Collections; -import java.util.logging.Level; -import javax.swing.AbstractAction; -import javax.swing.Action; -import javax.swing.KeyStroke; -import javax.swing.text.Keymap; -import org.netbeans.core.startup.Main; -import org.netbeans.junit.*; -import org.openide.ErrorManager; -import org.openide.cookies.InstanceCookie; -import org.openide.filesystems.FileLock; -import org.openide.filesystems.FileObject; -import org.openide.filesystems.FileSystem; -import org.openide.filesystems.FileUtil; -import org.openide.loaders.DataObject; -import org.openide.loaders.DataShadow; -import org.openide.loaders.Environment; -import org.openide.loaders.InstanceSupport; -import org.openide.loaders.XMLDataObject; -import org.openide.util.Lookup; -import org.openide.util.lookup.Lookups; -import org.w3c.dom.Document; - -public class ShortcutsFolderTest extends NbTestCase { - private ErrorManager err; - private Keymap keymap; - - /** Constructor required by JUnit. - * @param testName method name to be used as testcase - */ - public ShortcutsFolderTest(String s) { - super(s); - } - - @Override - protected Level logLevel() { - return Level.ALL; - } - - @Override - protected void setUp() throws Exception { - MockServices.setServices(ENV.class); - - Main.initializeURLFactory (); - keymap = Lookup.getDefault().lookup(Keymap.class); - - assertNotNull("There is a keymap", keymap); - ShortcutsFolder.initShortcuts (); - - err = ErrorManager.getDefault().getInstance("TEST-" + getName()); - } - - public void testApplyChangeToFactoryActionIssue49597 () throws Exception { - final FileObject shortcuts = FileUtil.getConfigFile ("Shortcuts"); - FileObject inst = FileUtil.createData (FileUtil.getConfigRoot (), "Actions/Tools/TestAction.instance"); - TestAction action = new TestAction (); - inst.setAttribute ("instanceCreate", action); - - Reference ref = new WeakReference(inst); - inst = null; - assertGC ("File can disappear", ref); - -// ShortcutsFolder.waitFinished (); - - assertEquals ("Nothing registered", Collections.EMPTY_LIST, Arrays.asList (keymap.getBoundActions ())); - - final KeyStroke stroke = KeyStroke.getKeyStroke(KeyEvent.VK_F9, KeyEvent.ALT_MASK | Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()); - - class R implements FileSystem.AtomicAction { - FileObject inst2; - - public void run() throws IOException { - inst2 = FileUtil.createData (FileUtil.getConfigRoot (), "/Shortcuts/CA-F9.shadow"); - inst2.setAttribute ("originalFile", "/Actions/Tools/TestAction.instance"); - } - } - R run = new R(); - FileUtil.runAtomicAction(run); - - ShortcutsFolder.waitFinished (); - err.log("ShortcutsFolder.waitFinished"); - - FileObject[] arr = shortcuts.getChildren (); - err.log("children are here"); - - assertEquals ("One element is there", 1, arr.length); - DataObject obj = DataObject.find (arr[0]); - err.log("Object is here" + obj); - - assertEquals("It is DataShadow", DataShadow.class, obj.getClass()); - - Object a = keymap.getAction (stroke); - assertNotNull ("There is an action", a); - assertEquals ("It is test action", TestAction.class, a.getClass ()); - } - - @RandomlyFails - public void testShortcutsForDifferentFilesThanInstanceOrShadows () throws Exception { - FileObject inst = FileUtil.createData (FileUtil.getConfigRoot (), "Shortcuts/C-F11.xml"); - - FileLock lock = inst.lock (); - java.io.PrintStream ps = new java.io.PrintStream (inst.getOutputStream (lock)); - ps.println (""); - ps.println (""); - ps.println (""); - ps.println (""); - ps.println (""); - ps.println (""); - ps.close(); - lock.releaseLock (); - - DataObject obj = DataObject.find (inst); - assertEquals ("XML Data object", XMLDataObject.class, obj.getClass()); - InstanceCookie ic = obj.getCookie(InstanceCookie.class); - assertNotNull ("Has cookie", ic); - - final KeyStroke stroke = KeyStroke.getKeyStroke(KeyEvent.VK_F11, KeyEvent.CTRL_MASK); - - ShortcutsFolder.waitFinished (); - - Action action = keymap.getAction(stroke); - if (action == null) { - fail("There should be some action for " + stroke + " in:\n" + keymap); - } - - inst.delete (); - ShortcutsFolder.waitFinished (); - action = keymap.getAction (stroke); - assertNull ("Action removed", action); - } - - public static class TestAction extends AbstractAction { - public void actionPerformed (ActionEvent ae) {} - } - - public static class ENV extends Object implements Environment.Provider { - public Lookup getEnvironment(DataObject obj) { - if (obj instanceof XMLDataObject) { - try { - Document doc = ((XMLDataObject) obj).getDocument(); - if (doc.getDocumentElement().getNodeName().equals ("project")) { - return Lookups.singleton( - new InstanceSupport.Instance( - new TestAction () - ) - ); - } - } catch (Exception ex) { - ex.printStackTrace(); - fail ("No exception: " + ex.getMessage()); - } - } - return Lookup.EMPTY; - } - } - -} diff --git a/openide.awt/src/org/openide/awt/AlwaysEnabledAction.java b/openide.awt/src/org/openide/awt/AlwaysEnabledAction.java --- a/openide.awt/src/org/openide/awt/AlwaysEnabledAction.java +++ b/openide.awt/src/org/openide/awt/AlwaysEnabledAction.java @@ -14,8 +14,6 @@ import javax.swing.AbstractAction; import javax.swing.Action; import javax.swing.Icon; -import javax.swing.KeyStroke; -import javax.swing.text.Keymap; import org.openide.util.ContextAwareAction; import org.openide.util.ImageUtilities; import org.openide.util.Lookup; @@ -129,8 +127,9 @@ return null; } } - - return extractCommonAttribute(map, this, name); + Object o = extractCommonAttribute(map, this, name); + // cf. #137709 JG18: + return o != null ? o : super.getValue(name); } static final Object extractCommonAttribute(Map fo, Action action, String name) { @@ -176,13 +175,6 @@ if ("noIconInMenu".equals(name)) { // NOI18N return fo == null ? null : fo.get("noIconInMenu"); // NOI18N } - if (Action.ACCELERATOR_KEY.equals(name)) { - Keymap map = Lookup.getDefault().lookup(Keymap.class); - if (map != null) { - KeyStroke[] arr = map.getKeyStrokesForAction(action); - return arr.length > 0 ? arr[0] : null; - } - } // Delegate query to other properties to "fo" ignoring special properties if (!"delegate".equals(name) && !"instanceCreate".equals(name)) { return fo.get(name); @@ -218,7 +210,10 @@ return false; } - + @Override + public String toString() { + return "AlwaysEnabledAction[" + getValue(Action.NAME) + "]"; // NOI18N + } public void propertyChange(PropertyChangeEvent evt) { if (evt.getSource() == delegate) { diff --git a/openide.loaders/src/org/openide/awt/DynaMenuModel.java b/openide.loaders/src/org/openide/awt/DynaMenuModel.java --- a/openide.loaders/src/org/openide/awt/DynaMenuModel.java +++ b/openide.loaders/src/org/openide/awt/DynaMenuModel.java @@ -51,12 +51,12 @@ import java.util.Map; import javax.swing.Action; import javax.swing.Icon; -import javax.swing.ImageIcon; import javax.swing.JComponent; import javax.swing.JMenu; import javax.swing.JMenuItem; import javax.swing.JPopupMenu; import javax.swing.JSeparator; +import org.openide.filesystems.FileObject; import org.openide.util.ImageUtilities; import org.openide.util.Utilities; import org.openide.util.actions.Presenter; @@ -76,7 +76,7 @@ actionToMenuMap = new HashMap(); } - public void loadSubmenu(List cInstances, JMenu m) { + public void loadSubmenu(List cInstances, JMenu m, Map cookiesToFiles) { // clear first - refresh the menu's content boolean addSeparator = false; Icon curIcon = null; @@ -85,6 +85,9 @@ actionToMenuMap.clear(); while (it.hasNext()) { Object obj = it.next(); + if (obj instanceof Action) { + Toolbar.setAccelerator((Action) obj, cookiesToFiles.get(obj)); + } if (obj instanceof Presenter.Menu) { // does this still apply?? obj = ((Presenter.Menu)obj).getMenuPresenter(); diff --git a/openide.loaders/src/org/openide/awt/MenuBar.java b/openide.loaders/src/org/openide/awt/MenuBar.java --- a/openide.loaders/src/org/openide/awt/MenuBar.java +++ b/openide.loaders/src/org/openide/awt/MenuBar.java @@ -51,8 +51,10 @@ import java.io.ObjectOutput; import java.util.ArrayList; import java.util.Arrays; +import java.util.HashMap; import java.util.Iterator; import java.util.LinkedList; +import java.util.Map; import javax.swing.Action; import javax.swing.BorderFactory; import javax.swing.ImageIcon; @@ -627,6 +629,14 @@ super.waitFinished(); } + private Map cookiesToFiles = new HashMap(); + + @Override + protected Object instanceForCookie(DataObject obj, InstanceCookie cookie) throws IOException, ClassNotFoundException { + Object result = super.instanceForCookie(obj, cookie); + cookiesToFiles.put(result, obj.getPrimaryFile()); + return result; + } /** * Accepts only cookies that can provide Menu. @@ -685,7 +695,7 @@ m.add(item); } - m.dynaModel.loadSubmenu(cInstances, m); + m.dynaModel.loadSubmenu(cInstances, m, cookiesToFiles); return m; } diff --git a/openide.loaders/src/org/openide/awt/Toolbar.java b/openide.loaders/src/org/openide/awt/Toolbar.java --- a/openide.loaders/src/org/openide/awt/Toolbar.java +++ b/openide.loaders/src/org/openide/awt/Toolbar.java @@ -58,14 +58,18 @@ import javax.swing.JComponent; import javax.swing.JSeparator; import javax.swing.JToolBar; +import javax.swing.KeyStroke; import javax.swing.UIManager; import javax.swing.plaf.metal.MetalLookAndFeel; +import javax.swing.text.Keymap; import org.netbeans.modules.openide.loaders.DataObjectAccessor; import org.openide.cookies.InstanceCookie; +import org.openide.filesystems.FileObject; import org.openide.loaders.DataFolder; import org.openide.loaders.DataObject; import org.openide.loaders.FolderInstance; import org.openide.util.ImageUtilities; +import org.openide.util.Lookup; import org.openide.util.Task; import org.openide.util.actions.Presenter; @@ -352,7 +356,7 @@ return Toolbar.this.getClass(); } - private Map cookiesToObjects = new HashMap(); + private Map cookiesToObjects = new HashMap(); @Override protected Object instanceForCookie (DataObject obj, InstanceCookie cookie) @@ -411,7 +415,7 @@ for (int i = 0; i < cookies.length; i++) { try { Object obj = cookies[i].instanceCreate(); - Object file = cookiesToObjects.get(obj); + DataObject file = cookiesToObjects.get(obj); if (obj instanceof Presenter.Toolbar) { obj = ((Presenter.Toolbar) obj).getToolbarPresenter(); @@ -447,6 +451,7 @@ org.openide.awt.Actions.connect(b, a); b.putClientProperty("file", file); org.openide.awt.Toolbar.this.add(b); + setAccelerator(a, file.getPrimaryFile()); continue; } } @@ -479,6 +484,24 @@ } // end of inner class Folder + static void setAccelerator(Action a, FileObject file) { + if (file == null) { + return; + } + a.putValue("definingFile", file); // cf. o.n.core.NbKeymap.getKeyStrokesForAction + KeyStroke[] keys; + try { + Keymap keymap = Lookup.getDefault().lookup(Keymap.class); + keys = keymap != null ? keymap.getKeyStrokesForAction(a) : new KeyStroke[0]; + assert keys != null : keymap; + } finally { + a.putValue("definingFile", null); + } + if (keys.length > 0) { + a.putValue(Action.ACCELERATOR_KEY, keys[0]); + } + } + @Override public void setUI(javax.swing.plaf.ToolBarUI ui) { super.setUI(ui); diff --git a/openide.loaders/test/unit/src/org/openide/awt/DynaMenuModelTest.java b/openide.loaders/test/unit/src/org/openide/awt/DynaMenuModelTest.java --- a/openide.loaders/test/unit/src/org/openide/awt/DynaMenuModelTest.java +++ b/openide.loaders/test/unit/src/org/openide/awt/DynaMenuModelTest.java @@ -44,6 +44,7 @@ import java.awt.Component; import java.awt.event.ActionEvent; import java.util.ArrayList; +import java.util.Collections; import java.util.List; import javax.swing.AbstractAction; import javax.swing.JComponent; @@ -52,6 +53,7 @@ import javax.swing.JPopupMenu; import javax.swing.JSeparator; import junit.framework.TestCase; +import org.openide.filesystems.FileObject; import org.openide.util.actions.Presenter; /** @@ -70,13 +72,13 @@ public void testLoadSubmenu() { System.out.println("loadSubmenu"); - List cInstances = new ArrayList(); + List cInstances = new ArrayList(); cInstances.add(new Act1()); cInstances.add(new Act2()); JMenu m = new JMenu(); DynaMenuModel instance = new DynaMenuModel(); - instance.loadSubmenu(cInstances, m); + instance.loadSubmenu(cInstances, m, Collections.emptyMap()); Component[] comps = m.getPopupMenu().getComponents(); assertEquals("0", ((JMenuItem)comps[0]).getText()); assertEquals("1", ((JMenuItem)comps[1]).getText()); @@ -88,13 +90,13 @@ * Test of checkSubmenu method, of class org.openide.awt.DynaMenuModel. */ public void testCheckSubmenu() { - List cInstances = new ArrayList(); + List cInstances = new ArrayList(); cInstances.add(new Act1()); cInstances.add(new Act2()); JMenu m = new JMenu(); DynaMenuModel instance = new DynaMenuModel(); - instance.loadSubmenu(cInstances, m); + instance.loadSubmenu(cInstances, m, Collections.emptyMap()); instance.checkSubmenu(m); Component[] comps = m.getPopupMenu().getComponents();