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 @@ -43,18 +43,15 @@ import java.awt.event.ActionEvent; import java.awt.event.KeyEvent; +import java.io.IOException; 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,12 @@ 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.util.Exceptions; +import org.openide.util.Utilities; import org.openide.util.actions.SystemAction; import org.openide.util.lookup.ServiceProvider; @@ -76,8 +78,6 @@ 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) */ @@ -130,7 +130,6 @@ NbKeymap(final String name, final Keymap parent) { this.name = name; this.parent = parent; - bindings = new HashMap(); } public Action getDefaultAction() { @@ -161,7 +160,7 @@ 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) { + System.err.println("XXX no defining file known for " + a); return new KeyStroke[0]; } + synchronized (this) { + if (id2Stroke == null) { + id2Stroke = new TreeMap(); + FileObject shortcuts = FileUtil.getConfigFile("Shortcuts"); + for (FileObject f : shortcuts.getChildren()) { + id2Stroke.put(idForFile(f), f.getName()); + } + shortcuts = FileUtil.getConfigFile("Keymaps/NetBeans"); + for (FileObject f : shortcuts.getChildren()) { + id2Stroke.put(idForFile(f), f.getName()); + } + { // XXX for debugging + for (Map.Entry entry : id2Stroke.entrySet()) { + System.err.println(entry.getValue() + " => " + entry.getKey()); + } + } + } + } + String id = idForFile(definingFile); + String k = id2Stroke.get(id); + System.err.println("XXX found keystroke " + k + " for " + a + " with ID " + id); + // XXX return all available keystrokes, or make Toolbar.setAccelerator pick the most common, e.g. C-V over PASTE + return k != null ? new KeyStroke[] {Utilities.stringToKey(k)} : new KeyStroke[0]; + } + private Map id2Stroke; + /** + * 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"); + f = FileUtil.getConfigFile(path); + } + // 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(); } - 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); - } - keysForAction.add (curKey); + private Action find(KeyStroke key) { + FileObject shortcuts = FileUtil.getConfigFile("Keymaps/NetBeans"); + String nm = Utilities.keyToString(key); + FileObject def = shortcuts.getFileObject(nm + ".shadow"); + if (def == null) { + def = shortcuts.getFileObject(nm + ".instance"); + } + if (def == null) { + shortcuts = FileUtil.getConfigFile("Shortcuts"); + def = shortcuts.getFileObject(nm + ".shadow"); + if (def == null) { + def = shortcuts.getFileObject(nm + ".instance"); + } + if (def == null) { + return null; } } - - return localActions; + try { + DataObject d = DataObject.find(def); + InstanceCookie ic = d.getLookup().lookup(InstanceCookie.class); + if (ic == null) { + return null; + } + return (Action) ic.instanceCreate(); + } catch (ClassNotFoundException x) { + Exceptions.printStackTrace(x); + } catch (IOException x) { + Exceptions.printStackTrace(x); + } + return null; } 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 +326,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 +348,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(); 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/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) { @@ -168,13 +167,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); 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);