diff --git a/editor/nbproject/project.xml b/editor/nbproject/project.xml --- a/editor/nbproject/project.xml +++ b/editor/nbproject/project.xml @@ -162,7 +162,7 @@ - 4.11 + 7.13 diff --git a/editor/src/org/netbeans/modules/editor/impl/ActionsList.java b/editor/src/org/netbeans/modules/editor/impl/ActionsList.java --- a/editor/src/org/netbeans/modules/editor/impl/ActionsList.java +++ b/editor/src/org/netbeans/modules/editor/impl/ActionsList.java @@ -16,6 +16,7 @@ import java.util.logging.Logger; import javax.swing.Action; import javax.swing.JSeparator; +import org.openide.awt.AcceleratorBinding; import org.openide.cookies.InstanceCookie; import org.openide.filesystems.FileObject; import org.openide.loaders.DataFolder; @@ -142,7 +143,9 @@ } if (toAdd instanceof Action) { - actions.add((Action) toAdd); + Action action = (Action) toAdd; + actions.add(action); + AcceleratorBinding.setAccelerator(action, item); } else if (isSeparator(toAdd)) { if (prohibitSeparatorsAndActionNames) { if (LOG.isLoggable(Level.INFO)) { diff --git a/o.n.core/manifest.mf b/o.n.core/manifest.mf --- a/o.n.core/manifest.mf +++ b/o.n.core/manifest.mf @@ -4,4 +4,4 @@ OpenIDE-Module-Layer: org/netbeans/core/resources/mf-layer.xml AutoUpdate-Show-In-Client: false AutoUpdate-Essential-Module: true -OpenIDE-Module-Specification-Version: 3.18 +OpenIDE-Module-Specification-Version: 3.19 diff --git a/o.n.core/nbproject/project.xml b/o.n.core/nbproject/project.xml --- a/o.n.core/nbproject/project.xml +++ b/o.n.core/nbproject/project.xml @@ -131,7 +131,7 @@ - 5.1 + 7.13 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 @@ -59,6 +59,7 @@ import javax.swing.Action; import javax.swing.KeyStroke; import javax.swing.text.Keymap; +import org.openide.awt.AcceleratorBinding; import org.openide.awt.StatusDisplayer; import org.openide.cookies.InstanceCookie; import org.openide.filesystems.FileAttributeEvent; @@ -71,6 +72,7 @@ import org.openide.loaders.DataObject; import org.openide.loaders.DataObjectNotFoundException; import org.openide.loaders.DataShadow; +import org.openide.util.Lookup; import org.openide.util.Utilities; import org.openide.util.actions.SystemAction; import org.openide.util.lookup.ServiceProvider; @@ -334,18 +336,31 @@ } public KeyStroke[] getKeyStrokesForAction(Action a) { - 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]; - } + return new KeyStroke[0]; + } + + KeyStroke keyStrokeForAction(Action a, FileObject definingFile) { String id = idForFile(definingFile); bindings(); action2Id.put(a, id); KeyStroke k = id2Stroke.get(id); LOG.log(Level.FINE, "found keystroke {0} for {1} with ID {2}", new Object[] {k, id(a), id}); - return k != null ? new KeyStroke[] {k} : new KeyStroke[0]; + return k; } + + @ServiceProvider(service=AcceleratorBinding.class) + public static final class AcceleratorBindingImpl extends AcceleratorBinding { + protected @Override KeyStroke keyStrokeForAction(Action action, FileObject definingFile) { + Keymap km = Lookup.getDefault().lookup(Keymap.class); + if (km instanceof NbKeymap) { + return ((NbKeymap) km).keyStrokeForAction(action, definingFile); + } else { + LOG.log(Level.WARNING, "unexpected keymap: {0}", km); + return null; + } + } + } + /** * Traverses shadow files to origin. * Returns impl class name if that is obvious (common for SystemAction's); 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 @@ -46,8 +46,6 @@ import java.awt.event.KeyEvent; import java.io.IOException; import java.io.OutputStream; -import java.util.Arrays; -import java.util.Collections; import java.util.concurrent.atomic.AtomicReference; import javax.swing.AbstractAction; import javax.swing.Action; @@ -92,13 +90,12 @@ return FileUtil.createFolder(FileUtil.getConfigRoot(), path); } - private void assertMapping(Keymap km, KeyStroke stroke, FileObject presenterDefinition, String actionName) throws Exception { + private void assertMapping(NbKeymap 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))); + stroke, km.keyStrokeForAction(a, presenterDefinition)); } public void testAcceleratorMapping() throws Exception { @@ -119,7 +116,7 @@ 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(); + NbKeymap km = new NbKeymap(); 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"); @@ -132,7 +129,7 @@ DataFolder shortcuts = DataFolder.findFolder(makeFolder("Shortcuts")); DataShadow.create(shortcuts, "C-V", DataObject.find(def)); DataShadow.create(shortcuts, "PASTE", DataObject.find(def)); - Keymap km = new NbKeymap(); + NbKeymap km = new NbKeymap(); assertMapping(km, KeyStroke.getKeyStroke(KeyEvent.VK_V, KeyEvent.CTRL_MASK), def, "paste"); } @@ -147,7 +144,7 @@ DataFolder netbeans = DataFolder.findFolder(makeFolder("Keymaps/NetBeans")); DataShadow.create(netbeans, "C-F5", DataObject.find(def1)); DataShadow.create(netbeans, "F5", DataObject.find(def2)); - Keymap km = new NbKeymap(); + NbKeymap km = new NbKeymap(); assertMapping(km, KeyStroke.getKeyStroke(KeyEvent.VK_F5, KeyEvent.CTRL_MASK), def1, "start"); assertMapping(km, KeyStroke.getKeyStroke(KeyEvent.VK_F5, 0), def2, "continue"); } @@ -162,7 +159,7 @@ } public void testAbstractModifiers() throws Exception { - Keymap km = new NbKeymap(); + NbKeymap km = new NbKeymap(); FileObject inst1 = make("Shortcuts/D-1.instance"); inst1.setAttribute("instanceCreate", new DummyAction("one")); FileObject inst2 = make("Shortcuts/O-1.instance"); @@ -190,7 +187,7 @@ def.setAttribute("instanceCreate", a); DataShadow.create(DataFolder.findFolder(makeFolder("Keymaps/NetBeans")), "C-A", DataObject.find(def)); DataShadow.create(DataFolder.findFolder(makeFolder("Keymaps/Eclipse")), "C-B", DataObject.find(def)); - Keymap km = new NbKeymap(); + NbKeymap km = new NbKeymap(); assertMapping(km, KeyStroke.getKeyStroke(KeyEvent.VK_A, KeyEvent.CTRL_MASK), def, "one"); assertEquals(null, a.getValue(Action.ACCELERATOR_KEY)); FileUtil.getConfigFile("Keymaps").setAttribute("currentKeymap", "Eclipse"); @@ -264,8 +261,7 @@ FileObject def = make("Menu/x.shadow"); def.setAttribute("originalFile", "Action/nonexistent.instance"); Action a = new DummyAction("x"); - a.putValue("definingFile", def); - new NbKeymap().getKeyStrokesForAction(a); + new NbKeymap().keyStrokeForAction(a, def); } private static final class DummyAction extends AbstractAction { diff --git a/openide.loaders/apichanges.xml b/openide.loaders/apichanges.xml --- a/openide.loaders/apichanges.xml +++ b/openide.loaders/apichanges.xml @@ -106,6 +106,22 @@ + + + Added AcceleratorBinding + + + + + +

+ Added a new class to help with action accelerators. + Should not be used by typical modules. +

+
+ + +
New constructor of XMLDataObject diff --git a/openide.loaders/manifest.mf b/openide.loaders/manifest.mf --- a/openide.loaders/manifest.mf +++ b/openide.loaders/manifest.mf @@ -1,6 +1,6 @@ Manifest-Version: 1.0 OpenIDE-Module: org.openide.loaders -OpenIDE-Module-Specification-Version: 7.12 +OpenIDE-Module-Specification-Version: 7.13 OpenIDE-Module-Localizing-Bundle: org/openide/loaders/Bundle.properties OpenIDE-Module-Provides: org.netbeans.modules.templates.v1_0 AutoUpdate-Essential-Module: true diff --git a/openide.loaders/src/org/openide/awt/AcceleratorBinding.java b/openide.loaders/src/org/openide/awt/AcceleratorBinding.java new file mode 100644 --- /dev/null +++ b/openide.loaders/src/org/openide/awt/AcceleratorBinding.java @@ -0,0 +1,89 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2010 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common + * Development and Distribution License("CDDL") (collectively, the + * "License"). You may not use this file except in compliance with the + * License. You can obtain a copy of the License at + * http://www.netbeans.org/cddl-gplv2.html + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the + * specific language governing permissions and limitations under the + * License. When distributing the software, include this License Header + * Notice in each file and include the License file at + * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the GPL Version 2 section of the License file that + * accompanied this code. If applicable, add the following below the + * License Header, with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * If you wish your version of this file to be governed by only the CDDL + * or only the GPL Version 2, indicate your decision by adding + * "[Contributor] elects to include this software in this distribution + * under the [CDDL or GPL Version 2] license." If you do not indicate a + * single choice of license, a recipient has the option to distribute + * your version of this file under either the CDDL, the GPL Version 2 or + * to extend the choice of license to its licensees as provided above. + * However, if you add GPL Version 2 code and therefore, elected the GPL + * Version 2 license, then the option applies only if the new code is + * made subject to such option by the copyright holder. + * + * Contributor(s): + * + * Portions Copyrighted 2010 Sun Microsystems, Inc. + */ + +package org.openide.awt; + +import javax.swing.Action; +import javax.swing.KeyStroke; +import javax.swing.text.Keymap; +import org.openide.filesystems.FileObject; +import org.openide.util.Lookup; + +/** + * Permits accelerators to be set on actions based on global registrations. + * This class is only intended for use from NetBeans infrastructure code: + * that which either reads shortcut definitions (i.e. the global {@link Keymap}); + * or which loads actions into presenters (e.g. {@link Toolbar}). + * @since org.openide.loaders 7.13 + */ +public abstract class AcceleratorBinding { + + /** + * Subclass constructor. Only certain implementations are permitted. + */ + protected AcceleratorBinding() { + assert getClass().getName().equals("org.netbeans.core.NbKeymap$AcceleratorBindingImpl") : this; + } + + /** + * Finds a keystroke for an action. + * @param action an action + * @param definingFile an instance file which defines the action + * @return a keystroke or null + */ + protected abstract KeyStroke keyStrokeForAction(Action action, FileObject definingFile); + + /** + * Associates an {@link Action#ACCELERATOR_KEY} with an action based on a declared shortcut. + * If an instance of {@link AcceleratorBinding} can be found in default lookup, + * it will be used to determine the binding. Otherwise nothing is done. + * @param action an action defined in layers + * @param definingFile instance file defining the action + */ + public static void setAccelerator(Action action, FileObject definingFile) { + for (AcceleratorBinding bnd : Lookup.getDefault().lookupAll(AcceleratorBinding.class)) { + KeyStroke key = bnd.keyStrokeForAction(action, definingFile); + if (key != null) { + action.putValue(Action.ACCELERATOR_KEY, key); + break; + } + } + } + +} 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 @@ -86,7 +86,10 @@ while (it.hasNext()) { Object obj = it.next(); if (obj instanceof Action) { - Toolbar.setAccelerator((Action) obj, cookiesToFiles.get(obj)); + FileObject file = cookiesToFiles.get(obj); + if (file != null) { + AcceleratorBinding.setAccelerator((Action) obj, file); + } } if (obj instanceof Presenter.Menu) { // does this still apply?? 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,18 +58,14 @@ 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; @@ -411,7 +407,7 @@ if (obj instanceof Presenter.Toolbar) { if (obj instanceof Action && file != null) { - setAccelerator((Action)obj, file.getPrimaryFile()); + AcceleratorBinding.setAccelerator((Action)obj, file.getPrimaryFile()); } obj = ((Presenter.Toolbar) obj).getToolbarPresenter(); } @@ -447,7 +443,7 @@ b.putClientProperty("file", file); org.openide.awt.Toolbar.this.add(b); if (file != null) { - setAccelerator(a, file.getPrimaryFile()); + AcceleratorBinding.setAccelerator(a, file.getPrimaryFile()); } continue; } @@ -481,25 +477,6 @@ } // 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 { - assert a.getValue("definingFile") == file : a.getClass() + " violated Action.putValue contract"; - 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);