--- a/core.output2/nbproject/project.xml
+++ a/core.output2/nbproject/project.xml
@@ -58,6 +58,14 @@
+ org.netbeans.modules.options.keymap
+
+
+
+ 1.17
+
+
+
org.openide.actions
--- a/core.output2/src/org/netbeans/core/output2/OutputTab.java
+++ a/core.output2/src/org/netbeans/core/output2/OutputTab.java
@@ -82,6 +82,7 @@
import javax.swing.event.PopupMenuEvent;
import javax.swing.event.PopupMenuListener;
import javax.swing.text.Document;
+import org.netbeans.core.options.keymap.api.KeyStrokeUtils;
import org.netbeans.core.output2.Controller.ControllerOutputEvent;
import org.netbeans.core.output2.ui.AbstractOutputPane;
import org.netbeans.core.output2.ui.AbstractOutputTab;
@@ -99,7 +100,7 @@
import org.openide.xml.XMLUtil;
import static org.netbeans.core.output2.OutputTab.ACTION.*;
-import org.openide.windows.IOProvider;
+import org.netbeans.core.output2.ui.OutputKeymapManager;
/**
@@ -716,6 +717,7 @@
private final Map actions = new EnumMap(ACTION.class);;
private void createActions() {
+ KeyStrokeUtils.refreshActionCache();
for (ACTION a : ACTION.values()) {
TabAction action;
switch(a) {
@@ -782,10 +784,22 @@
TabAction(ACTION action, String bundleKey) {
if (bundleKey != null) {
String name = NbBundle.getMessage(OutputTab.class, bundleKey);
- KeyStroke accelerator = getAcceleratorFor(bundleKey);
+ List accels = getAcceleratorsFor(action);
this.action = action;
putValue(NAME, name);
- putValue(ACCELERATOR_KEY, accelerator);
+ if (accels != null && accels.size() > 0) {
+ List l = new ArrayList(accels.size());
+ for (KeyStroke[] ks : accels) {
+ if (ks.length == 1) { // ignore multi-key accelerators
+ l.add(ks[0]);
+ }
+ }
+ if (l.size() > 0) {
+ putValue(ACCELERATORS_KEY,
+ l.toArray(new KeyStroke[l.size()]));
+ putValue(ACCELERATOR_KEY, l.get(0));
+ }
+ }
}
}
@@ -814,15 +828,62 @@
* Get a keyboard accelerator from the resource bundle, with special handling
* for the mac keyboard layout.
*
- * @param name The bundle key prefix
+ * @param action Action to get accelerator for.
* @return A keystroke
*/
- private KeyStroke getAcceleratorFor(String name) {
- String key = name + ".accel"; //NOI18N
- if (Utilities.isMac()) {
- key += ".mac"; //NOI18N
+ private List getAcceleratorsFor(ACTION action) {
+ switch (action) {
+ case COPY:
+ return KeyStrokeUtils.getKeyStrokesForAction(
+ "copy-to-clipboard", null); //NOI18N
+ case PASTE:
+ return KeyStrokeUtils.getKeyStrokesForAction(
+ "paste-from-clipboard", null); //NOI18N
+ case SAVEAS:
+ return KeyStrokeUtils.getKeyStrokesForAction(
+ OutputKeymapManager.SAVE_AS_ACTION_ID, null);
+ case CLOSE:
+ return KeyStrokeUtils.getKeyStrokesForAction(
+ OutputKeymapManager.CLOSE_ACTION_ID, null);
+ case NEXT_ERROR:
+ return KeyStrokeUtils.getKeyStrokesForAction(
+ "next-error", null); //NOI18N
+ case PREV_ERROR:
+ return KeyStrokeUtils.getKeyStrokesForAction(
+ "previous-error", null); //NOI18N
+ case SELECT_ALL:
+ return KeyStrokeUtils.getKeyStrokesForAction(
+ "select-all", null); //NOI18N
+ case FIND:
+ return KeyStrokeUtils.getKeyStrokesForAction(
+ "incremental-search-forward", null); //NOI18N
+ case FIND_NEXT:
+ return KeyStrokeUtils.getKeyStrokesForAction(
+ "find-next", null); //NOI18N
+ case FIND_PREVIOUS:
+ return KeyStrokeUtils.getKeyStrokesForAction(
+ "find-previous", null); //NOI18N
+ case FILTER:
+ return KeyStrokeUtils.getKeyStrokesForAction(
+ OutputKeymapManager.FILTER_ACTION_ID, null);
+ case LARGER_FONT:
+ return KeyStrokeUtils.getKeyStrokesForAction(
+ OutputKeymapManager.LARGER_FONT_ACTION_ID, null);
+ case SMALLER_FONT:
+ return KeyStrokeUtils.getKeyStrokesForAction(
+ OutputKeymapManager.SMALLER_FONT_ACTION_ID, null);
+ case FONT_TYPE:
+ return KeyStrokeUtils.getKeyStrokesForAction(
+ OutputKeymapManager.FONT_TYPE_ACTION_ID, null);
+ case CLEAR:
+ return KeyStrokeUtils.getKeyStrokesForAction(
+ OutputKeymapManager.CLEAR_ACTION_ID, null);
+ case WRAP:
+ return KeyStrokeUtils.getKeyStrokesForAction(
+ OutputKeymapManager.WRAP_ACTION_ID, null);
+ default:
+ return null;
}
- return Utilities.stringToKey(NbBundle.getMessage(OutputTab.class, key));
}
public ACTION getAction() {
--- a/core.output2/src/org/netbeans/core/output2/ui/AbstractOutputTab.java
+++ a/core.output2/src/org/netbeans/core/output2/ui/AbstractOutputTab.java
@@ -68,6 +68,7 @@
private boolean inputVisible = false;
private AbstractOutputPane outputPane;
private Action[] actions = new Action[0];
+ protected static final String ACCELERATORS_KEY = "ACCELERATORS_KEY";//NOI18N
private Component toFocus;
@@ -191,22 +192,28 @@
//It is a Controller.ControllerAction - don't create a memory leak by listening to it
a = new WeakAction(a);
}
- KeyStroke accel = null;
+ KeyStroke[] accels = null;
String name;
- Object o = a.getValue (Action.ACCELERATOR_KEY);
- if (o instanceof KeyStroke) {
- accel = (KeyStroke) o;
+ Object o = a.getValue(ACCELERATORS_KEY);
+ if (o instanceof KeyStroke[]) {
+ accels = (KeyStroke[]) o;
}
name = (String) a.getValue(Action.NAME);
- if (accel != null) {
- if (Controller.LOG) Controller.log ("Installed action " + name + " on " + accel);
- // if the logic here changes, check the popup escaping hack in Controller
- // it temporarily removes the VK_ESCAPE from input maps..
- JComponent c = getOutputPane().textView;
- c.getInputMap().put(accel, name);
- c.getActionMap().put(name, a);
- getInputMap (WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put (accel, name);
- getActionMap().put(name, a);
+ if (accels != null) {
+ for (KeyStroke accel : accels) {
+ if (Controller.LOG) {
+ Controller.log("Installed action " //NOI18N
+ + name + " on " + accel); //NOI18N
+ }
+ // if the logic here changes, check the popup escaping hack in
+ // Controller it temporarily removes the VK_ESCAPE from input
+ // maps..
+ JComponent c = getOutputPane().textView;
+ c.getInputMap().put(accel, name);
+ c.getActionMap().put(name, a);
+ getInputMap(WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(accel, name);
+ getActionMap().put(name, a);
+ }
}
}
--- a/core.output2/src/org/netbeans/core/output2/ui/OutputKeymapManager.java
+++ a/core.output2/src/org/netbeans/core/output2/ui/OutputKeymapManager.java
@@ -0,0 +1,334 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2012 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * 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. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle 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 2012 Sun Microsystems, Inc.
+ */
+package org.netbeans.core.output2.ui;
+
+import java.io.IOException;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import org.netbeans.core.options.keymap.api.ShortcutAction;
+import org.netbeans.core.options.keymap.spi.KeymapManager;
+import org.netbeans.core.output2.NbIOProvider;
+import org.openide.filesystems.FileObject;
+import org.openide.filesystems.FileUtil;
+import org.openide.util.NbBundle;
+import org.openide.util.Utilities;
+import org.openide.util.lookup.ServiceProvider;
+
+/**
+ *
+ * @author jhavlin
+ */
+@ServiceProvider(service = KeymapManager.class)
+public class OutputKeymapManager extends KeymapManager {
+
+ private static final Logger LOG = Logger.getLogger(
+ OutputKeymapManager.class.getName());
+ private static final String CATEGORY_NAME = NbBundle.getMessage(
+ NbIOProvider.class, "OpenIDE-Module-Name"); //NOI18N
+ /**
+ * ID of actions in keymap settings panel.
+ */
+ public static final String CLEAR_ACTION_ID =
+ "output-window-clear"; //NOI18N
+ public static final String FILTER_ACTION_ID =
+ "output-window-filter"; //NOI18N
+ public static final String LARGER_FONT_ACTION_ID =
+ "output-window-larger-font"; //NOI18N
+ public static final String SMALLER_FONT_ACTION_ID =
+ "output-window-smaller-font"; //NOI18N
+ public static final String CLOSE_ACTION_ID =
+ "output-window-close"; //NOI18N
+ public static final String FONT_TYPE_ACTION_ID =
+ "output-window-font-type"; //NOI18N
+ public static final String SAVE_AS_ACTION_ID =
+ "output-window-save-as"; //NOI18N
+ public static final String WRAP_ACTION_ID =
+ "output-window-wrap"; //NOI18N
+ /**
+ * Constants for persistence.
+ */
+ public static final String STORAGE_DIR =
+ "org-netbeans-core-output2/actions/"; //NOI18N
+ public static final String SHORTCUT_PREFIX = "sc"; //NOI18N
+ /**
+ * Actions
+ */
+ private final OutWinShortCutAction wrap = new OutWinShortCutAction(
+ WRAP_ACTION_ID, "ACTION_WRAP"); //NOI18N
+ private final OutWinShortCutAction clear = new OutWinShortCutAction(
+ CLEAR_ACTION_ID, "ACTION_CLEAR"); //NOI18N
+ private final OutWinShortCutAction filter = new OutWinShortCutAction(
+ FILTER_ACTION_ID, "ACTION_FILTER"); //NOI18N
+ private final OutWinShortCutAction largerFont = new OutWinShortCutAction(
+ LARGER_FONT_ACTION_ID, "ACTION_LARGER_FONT"); //NOI18N
+ private final OutWinShortCutAction smallerFont = new OutWinShortCutAction(
+ SMALLER_FONT_ACTION_ID, "ACTION_SMALLER_FONT"); //NOI18N
+ private final OutWinShortCutAction closeWindow = new OutWinShortCutAction(
+ CLOSE_ACTION_ID, "ACTION_CLOSE"); //NOI18N
+ private final OutWinShortCutAction fontType = new OutWinShortCutAction(
+ FONT_TYPE_ACTION_ID, "ACTION_FONT_TYPE"); //NOI18N
+ private final OutWinShortCutAction saveAs = new OutWinShortCutAction(
+ SAVE_AS_ACTION_ID, "ACTION_SAVEAS"); //NOI18N
+ /**
+ * Map of keymaps. Keys are profile names.
+ */
+ Map>> keymaps =
+ new HashMap>>();
+ /**
+ * The default keymap. Used if keys for a profile are not set.
+ */
+ Map> defaultKeymap =
+ new HashMap>();
+ /**
+ * List of all actions.
+ */
+ private final Set allActions =
+ new HashSet();
+ /**
+ * Map of actions of categories. There is only one category in this case.
+ */
+ Map> actions =
+ new HashMap>();
+
+ public OutputKeymapManager() {
+ super("OutputWindowKeymapManager"); //NOI18N
+ actions = new HashMap>();
+ Collections.addAll(allActions, wrap, clear, filter, largerFont,
+ smallerFont, closeWindow, fontType, saveAs);
+ Set set = new HashSet();
+ set.addAll(allActions);
+ actions.put(CATEGORY_NAME, set);
+ fillDefaultKeyMap();
+ loadShortCuts();
+ }
+
+ private void fillDefaultKeyMap() {
+ for (OutWinShortCutAction a : allActions) {
+ String dflt = a.getDefaultShortcut();
+ defaultKeymap.put(a, (dflt != null && !dflt.isEmpty())
+ ? Collections.singleton(dflt)
+ : Collections.emptySet());
+ }
+ }
+
+ @Override
+ public Map> getActions() {
+ return actions;
+ }
+
+ @Override
+ public void refreshActions() {
+ }
+
+ @Override
+ public Map> getKeymap(String profileName) {
+ Map> km = keymaps.get(profileName);
+ if (km == null) {
+ km = new HashMap>(defaultKeymap);
+ keymaps.put(profileName, km);
+ }
+ return km;
+ }
+
+ @Override
+ public Map> getDefaultKeymap(
+ String profileName) {
+ return defaultKeymap;
+ }
+
+ @Override
+ public void saveKeymap(String profileName,
+ Map> actionToShortcuts) {
+
+ Map> newShortcuts =
+ new HashMap>();
+ keymaps.put(profileName, newShortcuts);
+ for (OutWinShortCutAction owsa : allActions) {
+ Set shortcuts = actionToShortcuts.get(owsa);
+ if (shortcuts == null) {
+ shortcuts = Collections.emptySet();
+ }
+ newShortcuts.put(owsa, shortcuts);
+ }
+ storeShortCuts(profileName);
+ }
+
+ @Override
+ public List getProfiles() {
+ return null;
+ }
+
+ @Override
+ public String getCurrentProfile() {
+ return null;
+ }
+
+ @Override
+ public void setCurrentProfile(String profileName) {
+ }
+
+ @Override
+ public void deleteProfile(String profileName) {
+ }
+
+ @Override
+ public boolean isCustomProfile(String profileName) {
+ return false;
+ }
+
+ private class OutWinShortCutAction implements ShortcutAction {
+
+ private String id;
+ private String bundleKey;
+ private String displayName;
+ private String defaultShortcut;
+
+ public OutWinShortCutAction(String id, String bundleKey) {
+ this.id = id;
+ this.bundleKey = bundleKey;
+ this.displayName = NbBundle.getMessage(
+ NbIOProvider.class, bundleKey);
+ String nbKeysBundleKey = Utilities.isMac()
+ ? bundleKey + ".accel.mac" //NOI18N
+ : bundleKey + ".accel"; //NOI18N
+ String nbKeys = NbBundle.getMessage(NbIOProvider.class,
+ nbKeysBundleKey);
+ this.defaultShortcut = nbKeys;
+ }
+
+ public String getId() {
+ return id;
+ }
+
+ public String getBundleKey() {
+ return bundleKey;
+ }
+
+ public String getDisplayName() {
+ return displayName;
+ }
+
+ public String getDefaultShortcut() {
+ return defaultShortcut;
+ }
+
+ @Override
+ public String getDelegatingActionId() {
+ return null;
+ }
+
+ @Override
+ public ShortcutAction getKeymapManagerInstance(
+ String keymapManagerName) {
+ return null;
+ }
+ }
+
+ private void storeShortCuts(String profileName) {
+ FileObject root = FileUtil.getConfigRoot();
+ try {
+ FileObject actionsDir = FileUtil.createFolder(
+ root, STORAGE_DIR + profileName);
+ for (OutWinShortCutAction a : allActions) {
+ FileObject data = actionsDir.getFileObject(a.getId());
+ if (data == null) {
+ data = actionsDir.createData(a.getId());
+ } else if (data.isFolder()) {
+ throw new IOException(data + " is a folder."); //NOI18N
+ }
+ Enumeration atts = data.getAttributes();
+ while (atts.hasMoreElements()) {
+ String attName = atts.nextElement();
+ data.setAttribute(attName, null);
+ }
+ int index = 1;
+ if (keymaps.get(profileName).get(a) == null) {
+ continue;
+ }
+ for (String shortCut : keymaps.get(profileName).get(a)) {
+ data.setAttribute(SHORTCUT_PREFIX + index++, shortCut);
+ }
+ }
+ } catch (IOException e) {
+ LOG.log(Level.WARNING, "Cannot create folder", e); //NOI18N
+ }
+ }
+
+ private void loadShortCuts() {
+ FileObject root = FileUtil.getConfigRoot();
+ FileObject actionsDir = root.getFileObject(STORAGE_DIR);
+ if (actionsDir == null) {
+ return;
+ }
+ for (FileObject profileDir : actionsDir.getChildren()) {
+ if (!profileDir.isFolder()) {
+ continue;
+ }
+ Map> keymap =
+ new HashMap>();
+ keymaps.put(profileDir.getName(), keymap);
+ for (OutWinShortCutAction a : allActions) {
+ FileObject actionFile = profileDir.getFileObject(a.getId());
+ if (actionFile == null || !actionFile.isData()) {
+ keymap.put(a, Collections.emptySet());
+ continue;
+ }
+ Enumeration atts = actionFile.getAttributes();
+ Set strokes = new HashSet();
+ while (atts.hasMoreElements()) {
+ String att = atts.nextElement();
+ if (att.startsWith(SHORTCUT_PREFIX)) {
+ strokes.add((String) actionFile.getAttribute(att));
+ }
+ }
+ keymap.put(a, strokes);
+ }
+ }
+ }
+}
--- a/options.keymap/nbproject/project.xml
+++ a/options.keymap/nbproject/project.xml
@@ -50,6 +50,15 @@
org.netbeans.modules.options.keymap
+ org.netbeans.api.annotations.common
+
+
+
+ 1
+ 1.15
+
+
+
org.netbeans.modules.options.api
@@ -152,6 +161,7 @@
+ org.netbeans.core.output2
org.netbeans.modules.editor.macros
org.netbeans.modules.jumpto
org.netbeans.modules.jvi
--- a/options.keymap/src/org/netbeans/modules/options/keymap/Utils.java
+++ a/options.keymap/src/org/netbeans/modules/options/keymap/Utils.java
@@ -42,10 +42,23 @@
* made subject to such option by the copyright holder.
*/
-package org.netbeans.modules.options.keymap;
+package org.netbeans.core.options.keymap.api;
import java.awt.event.InputEvent;
import java.awt.event.KeyEvent;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.StringTokenizer;
+import java.util.logging.Level;
+import java.util.logging.Logger;
import javax.swing.KeyStroke;
+import org.netbeans.api.annotations.common.CheckForNull;
+import org.netbeans.api.annotations.common.NonNull;
+import org.netbeans.api.annotations.common.NullAllowed;
+import org.openide.util.Lookup;
import org.openide.util.Utilities;
@@ -53,17 +66,29 @@
*
* @author Jan Jancura
*/
-class Utils {
-
-
- static String getKeyStrokesAsText (KeyStroke[] keyStrokes, String delim) {
- if (keyStrokes == null) return "";
- if (keyStrokes.length == 0) return "";
- StringBuffer sb = new StringBuffer (getKeyStrokeAsText (keyStrokes [0]));
+public class KeyStrokeUtils {
+
+ private static final Logger LOG = Logger.getLogger(
+ KeyStrokeUtils.class.getName());
+
+ /**
+ * Convert an array of {@link KeyStroke key stroke} to a string composed of
+ * human-readable names of these key strokes, delimited by {@code delim}.
+ */
+ public static String getKeyStrokesAsText(
+ @NullAllowed KeyStroke[] keyStrokes, @NonNull String delim) {
+ if (keyStrokes == null) {
+ return ""; //NOI18N
+ }
+ if (keyStrokes.length == 0) {
+ return ""; //NOI18N
+ }
+ StringBuilder sb = new StringBuilder(getKeyStrokeAsText(keyStrokes[0]));
int i, k = keyStrokes.length;
- for (i = 1; i < k; i++)
- sb.append (delim).append (getKeyStrokeAsText (keyStrokes [i]));
- return new String (sb);
+ for (i = 1; i < k; i++) {
+ sb.append(delim).append(getKeyStrokeAsText(keyStrokes[i]));
+ }
+ return new String(sb);
}
// Important: keep in sync with Editor Settings Storage StorageSupport
@@ -90,7 +115,12 @@
}
}
- static KeyStroke getKeyStroke (String keyStroke) {
+ /**
+ * Convert human-readable keystroke name to {@link KeyStroke} object.
+ */
+ public static @CheckForNull KeyStroke getKeyStroke(
+ @NonNull String keyStroke) {
+
int modifiers = 0;
while (true) {
if (keyStroke.startsWith(EMACS_CTRL)) {
@@ -124,7 +154,10 @@
}
}
- static String getKeyStrokeAsText (KeyStroke keyStroke) {
+ /**
+ * Get human-readable name for a {@link KeyStroke}.
+ */
+ public static String getKeyStrokeAsText(@NonNull KeyStroke keyStroke) {
int modifiers = keyStroke.getModifiers ();
StringBuilder sb = new StringBuilder ();
if ((modifiers & InputEvent.CTRL_DOWN_MASK) > 0) {
@@ -150,4 +183,124 @@
}
return sb.toString ();
}
+
+ /**
+ * Converts a textual representation of key strokes to an array of KeyStroke
+ * objects. Please see {@link #keyStrokesToString(Collection, boolean)}
+ * ror details about the available formats.
+ *
+ * @param key The textual representation of keystorkes to convert. Its format
+ * depends on the value of emacsStyle
parameter.
+ *
+ * @return The KeyStroke
s that were represented by the key
+ * text or null
if the textual representation was malformed.
+ * @since 1.16
+ */
+ public static @CheckForNull KeyStroke[] getKeyStrokes(@NonNull String key) {
+ assert key != null : "The parameter key must not be null"; //NOI18N
+
+ List result = new ArrayList();
+ String delimiter = " "; //NOI18N
+
+ for(StringTokenizer st = new StringTokenizer(key, delimiter); st.hasMoreTokens();) { //NOI18N
+ String ks = st.nextToken().trim();
+ KeyStroke keyStroke = getKeyStroke(ks);
+
+ if (keyStroke != null) {
+ result.add(keyStroke);
+ } else {
+ if (LOG.isLoggable(Level.FINE)) {
+ LOG.log(Level.FINE,
+ "Invalid keystroke string: ''{0}''", ks); //NOI18N
+ }
+ return null;
+ }
+ }
+
+ return result.toArray(new KeyStroke[result.size()]);
+ }
+
+ /**
+ * Find key strokes for an action. The currently selected profile is used.
+ * If there are more than one key strokes, the most convenient one is the
+ * first one in the array (index 0). If no key stroke is found and
+ * {@code defaultKeyStroke} is not null, an array containing only
+ * {@code defaultKeyStroke} is returned. If no key stroke is found and
+ * {@code defaultKeyStroke} is null, an empty array is returned.
+ *
+ * @param actionId ID of action.
+ * @param defaultKeyStroke Default key stroke, used in case no key stroke is
+ * found for the action. Can be null.
+ * @return Array of key strokes, or an empty array if no key stroke is
+ * available.
+ */
+ public static List getKeyStrokesForAction(@NonNull String actionId,
+ @NullAllowed KeyStroke defaultKeyStroke) {
+ for (ShortcutsFinder sf : Lookup.getDefault().lookupAll(
+ ShortcutsFinder.class)) {
+ ShortcutAction sa = sf.findActionForId(actionId);
+ if (sa != null) {
+ String[] shortcuts = sf.getShortcuts(sa);
+ if (shortcuts != null && shortcuts.length > 0) {
+ List ks = new LinkedList();
+ for (int i = 0; i < shortcuts.length; i++) {
+ if (shortcuts[i] != null) {
+ KeyStroke s[] = getKeyStrokes(shortcuts[i]);
+ if (s != null) {
+ ks.add(s);
+ }
+ }
+ }
+ return sortKeyStrokesByPreference(ks);
+ }
+ }
+ }
+ return defaultKeyStroke == null
+ ? Collections.emptyList()
+ : Collections.singletonList(new KeyStroke[]{defaultKeyStroke});
+ }
+
+ /**
+ * Sort the list, so that the most appropriate accelerator is at index 0.
+ */
+ private static List sortKeyStrokesByPreference(
+ List keystrokes) {
+ if (keystrokes.size() < 2) {
+ return keystrokes;
+ }
+ KeyStroke best[] = null;
+ boolean isSolaris =
+ Utilities.getOperatingSystem() == Utilities.OS_SOLARIS;
+ for (int i = 0; i < keystrokes.size(); i++) {
+ KeyStroke[] ks = keystrokes.get(i);
+ if (ks.length > 1) {
+ continue;
+ }
+ boolean solarisKey = ks[0].getKeyCode() >= KeyEvent.VK_STOP
+ && ks[0].getKeyCode() <= KeyEvent.VK_CUT;
+ if (isSolaris == solarisKey
+ && (best == null
+ || best[0].getKeyCode() > ks[0].getKeyCode())) {
+ //Solaris key on solaris OS or other key on other OS.
+ best = ks;
+ }
+ }
+ if (best != null) {
+ keystrokes.remove(best);
+ keystrokes.add(0, best);
+ }
+ return keystrokes;
+ }
+
+ /**
+ * Force caches to be refreshed, so that
+ * {@link #getKeyStrokesForAction(String, KeyStroke)} returns correct and
+ * up-to-date results.
+ */
+ public static void refreshActionCache() {
+ for (ShortcutsFinder sf :
+ Lookup.getDefault().lookupAll(ShortcutsFinder.class)) {
+ sf.refreshActions();
+ }
+ }
}
--- a/options.keymap/src/org/netbeans/modules/options/keymap/KeymapViewModel.java
+++ a/options.keymap/src/org/netbeans/modules/options/keymap/KeymapViewModel.java
@@ -68,11 +68,13 @@
import javax.swing.SwingUtilities;
import javax.swing.event.TableModelEvent;
import javax.swing.table.DefaultTableModel;
+import org.netbeans.core.options.keymap.api.KeyStrokeUtils;
import org.netbeans.core.options.keymap.api.ShortcutAction;
import org.netbeans.core.options.keymap.api.ShortcutsFinder;
import org.openide.DialogDescriptor;
import org.openide.DialogDisplayer;
import org.openide.ErrorManager;
+import org.openide.util.Exceptions;
import org.openide.util.NbBundle;
import org.openide.util.RequestProcessor;
import org.openide.util.Utilities;
@@ -625,6 +627,7 @@
public void refreshActions () {
categoryToActionsCache = new HashMap> ();
+ shortcutsCache = new HashMap>>();
model.refreshActions ();
}
@@ -658,7 +661,7 @@
shortcutsCache = new HashMap>> ();
model = new KeymapModel ();
applyInProgress = false;
- }
+ }
});
}
@@ -766,7 +769,7 @@
Set shortcuts = new LinkedHashSet ();
for (String emacsShortcut: entry.getValue()) {
KeyStroke[] keyStroke = Utilities.stringToKeys (emacsShortcut);
- shortcuts.add (Utils.getKeyStrokesAsText (keyStroke, " "));
+ shortcuts.add (KeyStrokeUtils.getKeyStrokesAsText (keyStroke, " "));
}
result.put (action, shortcuts);
}
@@ -784,7 +787,7 @@
List result = new ArrayList ();
while (st.hasMoreTokens ()) {
String ks = st.nextToken ().trim ();
- KeyStroke keyStroke = Utils.getKeyStroke (ks);
+ KeyStroke keyStroke = KeyStrokeUtils.getKeyStroke (ks);
if (keyStroke == null) return null; // text is not parsable
result.add (keyStroke);
}
--- a/options.keymap/src/org/netbeans/modules/options/keymap/ProfilesPanel.java
+++ a/options.keymap/src/org/netbeans/modules/options/keymap/ProfilesPanel.java
@@ -56,6 +56,7 @@
import javax.swing.AbstractListModel;
import javax.swing.JFileChooser;
import javax.swing.KeyStroke;
+import org.netbeans.core.options.keymap.api.KeyStrokeUtils;
import org.netbeans.core.options.keymap.api.ShortcutAction;
import org.openide.DialogDisplayer;
import org.openide.NotifyDescriptor;
@@ -347,7 +348,7 @@
for(StringTokenizer st = new StringTokenizer(key, delimiter); st.hasMoreTokens();) { //NOI18N
String ks = st.nextToken().trim();
- KeyStroke keyStroke = Utils.getKeyStroke(ks);
+ KeyStroke keyStroke = KeyStrokeUtils.getKeyStroke(ks);
if (keyStroke != null) {
buf.append(Utilities.keyToString(keyStroke, true));
@@ -373,7 +374,7 @@
KeyStroke keyStroke = Utilities.stringToKey(ks);
if (keyStroke != null) {
- buf.append(Utils.getKeyStrokeAsText(keyStroke));
+ buf.append(KeyStrokeUtils.getKeyStrokeAsText(keyStroke));
if (st.hasMoreTokens())
buf.append(' ');
} else {
--- a/options.keymap/src/org/netbeans/modules/options/keymap/ShortcutListener.java
+++ a/options.keymap/src/org/netbeans/modules/options/keymap/ShortcutListener.java
@@ -45,6 +45,7 @@
import java.awt.event.KeyListener;
import javax.swing.JTextField;
import javax.swing.KeyStroke;
+import org.netbeans.core.options.keymap.api.KeyStrokeUtils;
/**
* KeyListener trasforming keystrokes to human-readable and displaying them
@@ -125,7 +126,7 @@
}
private void addKeyStroke(KeyStroke keyStroke, boolean add) {
- String k = Utils.getKeyStrokeAsText(keyStroke);
+ String k = KeyStrokeUtils.getKeyStrokeAsText(keyStroke);
if (key.equals("")) { //NOI18N
textField.setText(k);
if (add)
--- a/options.keymap/src/org/netbeans/modules/options/keymap/ShortcutProvider.java
+++ a/options.keymap/src/org/netbeans/modules/options/keymap/ShortcutProvider.java
@@ -47,6 +47,7 @@
import java.util.LinkedHashSet;
import java.util.Set;
import javax.swing.KeyStroke;
+import org.netbeans.core.options.keymap.api.KeyStrokeUtils;
import org.openide.util.Utilities;
/**
@@ -129,35 +130,35 @@
//CTRL
for (int i = 0; i < letters.length; i++) {
- shortcutSet.add(Utils.getKeyStrokeAsText(KeyStroke.getKeyStroke(letters[i], InputEvent.CTRL_MASK)));
+ shortcutSet.add(KeyStrokeUtils.getKeyStrokeAsText(KeyStroke.getKeyStroke(letters[i], InputEvent.CTRL_MASK)));
}
if (Utilities.isMac())
//META
for (int i = 0; i < letters.length; i++) {
- shortcutSet.add(Utils.getKeyStrokeAsText(KeyStroke.getKeyStroke(letters[i], InputEvent.META_MASK)));
+ shortcutSet.add(KeyStrokeUtils.getKeyStrokeAsText(KeyStroke.getKeyStroke(letters[i], InputEvent.META_MASK)));
}
else
//ALT
for (int i = 0; i < letters.length; i++) {
- shortcutSet.add(Utils.getKeyStrokeAsText(KeyStroke.getKeyStroke(letters[i], InputEvent.ALT_MASK)));
+ shortcutSet.add(KeyStrokeUtils.getKeyStrokeAsText(KeyStroke.getKeyStroke(letters[i], InputEvent.ALT_MASK)));
}
//CTRL+SHIFT
for (int i = 0; i < letters.length; i++) {
- shortcutSet.add(Utils.getKeyStrokeAsText(KeyStroke.getKeyStroke(letters[i], InputEvent.CTRL_MASK | InputEvent.SHIFT_MASK)));
+ shortcutSet.add(KeyStrokeUtils.getKeyStrokeAsText(KeyStroke.getKeyStroke(letters[i], InputEvent.CTRL_MASK | InputEvent.SHIFT_MASK)));
}
if (Utilities.isMac())
//SHIFT+META
for (int i = 0; i < letters.length; i++) {
- shortcutSet.add(Utils.getKeyStrokeAsText(KeyStroke.getKeyStroke(letters[i], InputEvent.SHIFT_MASK | InputEvent.META_MASK)));
+ shortcutSet.add(KeyStrokeUtils.getKeyStrokeAsText(KeyStroke.getKeyStroke(letters[i], InputEvent.SHIFT_MASK | InputEvent.META_MASK)));
}
else
//SHIFT+ALT
for (int i = 0; i < letters.length; i++) {
- shortcutSet.add(Utils.getKeyStrokeAsText(KeyStroke.getKeyStroke(letters[i], InputEvent.SHIFT_MASK | InputEvent.ALT_MASK)));
+ shortcutSet.add(KeyStrokeUtils.getKeyStrokeAsText(KeyStroke.getKeyStroke(letters[i], InputEvent.SHIFT_MASK | InputEvent.ALT_MASK)));
}
}
return shortcutSet;
--- a/options.keymap/src/org/netbeans/modules/options/keymap/ShortcutsDialog.form
+++ a/options.keymap/src/org/netbeans/modules/options/keymap/ShortcutsDialog.form
@@ -1,4 +1,4 @@
-
+