Index: core/src/org/netbeans/core/ShortcutsFolder.java =================================================================== RCS file: /cvs/core/src/org/netbeans/core/ShortcutsFolder.java,v retrieving revision 1.17 diff -c -r1.17 ShortcutsFolder.java *** core/src/org/netbeans/core/ShortcutsFolder.java 28 Jan 2004 07:11:55 -0000 1.17 --- core/src/org/netbeans/core/ShortcutsFolder.java 8 Feb 2004 15:24:47 -0000 *************** *** 25,30 **** --- 25,31 ---- import org.openide.filesystems.*; import org.openide.loaders.*; import org.openide.cookies.InstanceCookie; + import org.openide.util.KeyRemapper; import org.openide.util.actions.SystemAction; import org.openide.util.Lookup; import org.openide.util.Utilities; *************** *** 147,154 **** * It is used in ShortcutsPanel. */ static String getKeyStrokeName (KeyStroke stroke) { Keymap map = (Keymap)Lookup.getDefault().lookup(Keymap.class); ! Action action = map.getAction (stroke); if (action != null) { return getKeyText (stroke) + " [" + getActionBasicName(action) + "]"; // NOI18N } else { --- 148,159 ---- * It is used in ShortcutsPanel. */ static String getKeyStrokeName (KeyStroke stroke) { + if (KeyRemapper.isExplicitKeyStroke(stroke)) { + System.err.println("Transforming an explicit keystroke in getKeyStrokeName"); + stroke = KeyRemapper.getDefault().toStandardFormat(stroke); + } Keymap map = (Keymap)Lookup.getDefault().lookup(Keymap.class); ! Action action = map.getAction (KeyRemapper.getDefault().dereference(stroke)); if (action != null) { return getKeyText (stroke) + " [" + getActionBasicName(action) + "]"; // NOI18N } else { *************** *** 177,183 **** if (strokes.length > 0) { name = name + " ["; // NOI18N for (int i = 0; i < strokes.length; i++) { ! name = name + getKeyText (strokes[i]); if (i != strokes.length - 1) { name = name + ", "; // NOI18N } --- 182,193 ---- if (strokes.length > 0) { name = name + " ["; // NOI18N for (int i = 0; i < strokes.length; i++) { ! KeyStroke stroke = strokes[i]; ! if (KeyRemapper.getDefault().isExplicitKeyStroke(stroke)) { ! System.err.println(" FOUND AN EXPLICIT KEYSTROKE: " + stroke); ! stroke = KeyRemapper.getDefault().toStandardFormat(stroke); ! } ! name = name + getKeyText (stroke); if (i != strokes.length - 1) { name = name + ", "; // NOI18N } *************** *** 210,215 **** --- 220,231 ---- if (stroke == null) { return ""; // NOI18N } + if (KeyRemapper.getDefault().isExplicitKeyStroke(stroke)) { + stroke = KeyRemapper.getDefault().toStandardFormat(stroke); + } else { + stroke = KeyRemapper.getDefault().dereference(stroke); + } + String modifText = java.awt.event.KeyEvent.getKeyModifiersText(stroke.getModifiers ()); if ("".equals (modifText)) return java.awt.event.KeyEvent.getKeyText(stroke.getKeyCode ()); // NOI18N else return modifText + "+" + java.awt.event.KeyEvent.getKeyText(stroke.getKeyCode ()); // NOI18N *************** *** 374,379 **** --- 390,396 ---- act = nl.item (i).getAttributes ().getNamedItem (ATTR_BINDING_ACTION).getNodeValue (); KeyStroke stroke = Utilities.stringToKey (key); + System.err.println("Putting to map " + stroke); SystemAction action = SystemAction.get ( Class.forName(act, true, (ClassLoader)Lookup.getDefault().lookup(ClassLoader.class))); map.put (stroke, action); *************** *** 523,529 **** */ public boolean add; public ChangeRequest(KeyStroke key, Action action, boolean add) { ! super(Utilities.keyToString(key), action); this.add = add; } } --- 540,546 ---- */ public boolean add; public ChangeRequest(KeyStroke key, Action action, boolean add) { ! super(Utilities.keyToString(KeyRemapper.getDefault().toStorageFormat(key)), action); this.add = add; } } Index: core/src/org/netbeans/core/ShortcutsPanel.java =================================================================== RCS file: /cvs/core/src/org/netbeans/core/ShortcutsPanel.java,v retrieving revision 1.21 diff -c -r1.21 ShortcutsPanel.java *** core/src/org/netbeans/core/ShortcutsPanel.java 14 Nov 2003 13:56:49 -0000 1.21 --- core/src/org/netbeans/core/ShortcutsPanel.java 8 Feb 2004 15:24:48 -0000 *************** *** 23,28 **** --- 23,29 ---- import javax.swing.*; import org.openide.DialogDescriptor; + import org.openide.util.KeyRemapper; import org.openide.util.Lookup; import org.openide.util.NbBundle; *************** *** 43,48 **** --- 44,50 ---- public ShortcutsPanel(ShortcutsEditor shortcutsEditor) { ResourceBundle bundle; + System.err.println("CREATING A SHORTCUTS PANEL"); this.shortcutsEditor = shortcutsEditor; updateData (); *************** *** 155,163 **** addActionsPanel = new ActionsPanel (true, shortcutsEditor); addActionsPanel.setBorder(new javax.swing.border.EmptyBorder(12, 12, 0, 11)); } DialogDescriptor dd = new DialogDescriptor (addActionsPanel, org.openide.util.NbBundle.getBundle(ShortcutsPanel.class).getString("ShortcutsPanel.AddActionTitle")); java.awt.Dialog d = org.openide.DialogDisplayer.getDefault().createDialog(dd); ! d.show (); if (dd.getValue() == DialogDescriptor.OK_OPTION) { updateData (); shortcutsList.setListData(strokes); --- 157,173 ---- addActionsPanel = new ActionsPanel (true, shortcutsEditor); addActionsPanel.setBorder(new javax.swing.border.EmptyBorder(12, 12, 0, 11)); } + System.err.println("Add button action performed"); DialogDescriptor dd = new DialogDescriptor (addActionsPanel, org.openide.util.NbBundle.getBundle(ShortcutsPanel.class).getString("ShortcutsPanel.AddActionTitle")); java.awt.Dialog d = org.openide.DialogDisplayer.getDefault().createDialog(dd); ! try { ! System.err.println("Calling setSuspended true "); ! KeyRemapper.getDefault().setSuspended(true); ! d.show (); ! } finally { ! System.err.println("Calling setSuspended false "); ! KeyRemapper.getDefault().setSuspended(false); ! } if (dd.getValue() == DialogDescriptor.OK_OPTION) { updateData (); shortcutsList.setListData(strokes); Index: core/windows/src/org/netbeans/core/windows/ShortcutAndMenuKeyEventProcessor.java =================================================================== RCS file: /cvs/core/windows/src/org/netbeans/core/windows/ShortcutAndMenuKeyEventProcessor.java,v retrieving revision 1.2 diff -c -r1.2 ShortcutAndMenuKeyEventProcessor.java *** core/windows/src/org/netbeans/core/windows/ShortcutAndMenuKeyEventProcessor.java 12 Nov 2003 07:50:06 -0000 1.2 --- core/windows/src/org/netbeans/core/windows/ShortcutAndMenuKeyEventProcessor.java 8 Feb 2004 15:24:49 -0000 *************** *** 25,30 **** --- 25,31 ---- import org.openide.util.Lookup; import org.openide.util.Utilities; import org.openide.ErrorManager; + import org.openide.util.KeyRemapper; /** *************** *** 85,90 **** --- 86,93 ---- private KeyEvent lastKeyPressed; // which posted a popup from main menu public boolean postProcessKeyEvent(KeyEvent ev) { + ev = KeyRemapper.getDefault().remap(ev); + if (ev.isConsumed()) return false; *************** *** 113,121 **** --- 116,129 ---- } public boolean dispatchKeyEvent(KeyEvent ev) { + ev = KeyRemapper.getDefault().remap(ev); + // XXX(-ttran) Sun JDK 1.4 on Linux: pressing Alt key produces // KeyEvent.VK_ALT, but Alt+ produces Meta+ + + //XXX(-tim) The code below should be a custom KeyRemapper for + //Linux and moved out of here if (Utilities.getOperatingSystem() == Utilities.OS_LINUX) { int mods = ev.getModifiers(); if (mods == InputEvent.META_MASK) { *************** *** 189,194 **** --- 197,204 ---- private boolean processShortcut(KeyEvent ev) { KeyStroke ks = KeyStroke.getKeyStrokeForEvent(ev); + // System.err.println("ProcessShortcut: " + ks); + Window w = SwingUtilities.windowForComponent(ev.getComponent()); // don't let action keystrokes to propagate from both Index: openide/src/org/openide/util/KeyRemapper.java =================================================================== RCS file: openide/src/org/openide/util/KeyRemapper.java diff -N openide/src/org/openide/util/KeyRemapper.java *** /dev/null 1 Jan 1970 00:00:00 -0000 --- openide/src/org/openide/util/KeyRemapper.java 8 Feb 2004 15:24:51 -0000 *************** *** 0 **** --- 1,631 ---- + /* + * Sun Public License Notice + * + * The contents of this file are subject to the Sun Public License + * Version 1.0 (the "License"). You may not use this file except in + * compliance with the License. A copy of the License is available at + * http://www.sun.com/ + * + * The Original Code is NetBeans. The Initial Developer of the Original + * Code is Sun Microsystems, Inc. Portions Copyright 1997-2004 Sun + * Microsystems, Inc. All Rights Reserved. + */ + /* + * KeyRemapper.java + * + * Created on January 14, 2004, 9:32 PM + */ + + package org.openide.util; + + import javax.swing.KeyStroke; + import java.awt.event.InputEvent; + import java.awt.event.KeyEvent; + import java.util.Arrays; + import java.util.HashMap; + import java.util.HashSet; + import java.util.Iterator; + import java.util.Map; + import java.util.Set; + import javax.swing.KeyStroke; + + /** This class is responsible for remapping KeyStrokes. Some operating systems + * (particularly OS-X) consume certain KeyStrokes which NetBeans normally + * maps, or use different default accellerator keymasks. When KeyStrokes are + * processed and compared against registered shortcuts, a platform-specific + * instance of this class will perform the needed translation. + * + * @author Tim Boudreau + */ + public abstract class KeyRemapper { + /** A mask which is logically or'd with the key mask for KeyStrokes. If + * present, it indicates that a registered KeyStroke should be processed + * literally, with no remapping. This is used to mark user-entered KeyStrokes + * before they are stored, so that remapping (for example, on OS-X from + * CTRL to Command) is not performed. If modules wish to register + * platform-specific key mappings, they may include this mask to indicate + * that the KeyStrokes should not be remapped. */ + public static final int EXPLICIT_MASK = 1024; + + private static KeyRemapper defaultInstance = null; + protected KeyRemapper() { + } + + /** Get an instance which can correctly remap KeyStrokes on the current + * platform. */ + public static KeyRemapper getDefault() { + if (defaultInstance == null) { + if (Utilities.getOperatingSystem() == Utilities.OS_MAC && + !Boolean.getBoolean("netbeans.osx.defaultkeys")) { //NOI18N + System.err.println("Created OSX remapper"); + defaultInstance = new OsxRemapper(); + } else { + defaultInstance = new NoOpRemapper(); + } + } + return defaultInstance; + } + + /** Convert a KeyStroke registered in the system into the form + * in which it should be processed. When a KeyStroke that may be + * a shortcut is processed, it is passed to this method. The KeyStroke + * that is returned is what will be compared against the set of + * registered KeyStrokes. + *

+ * For example, the default accelerator key on Mac OS-X is the + * Command key, not CTRL. In order to make NetBeans comfortable + * for Macintosh users, this method should accept Command- KeyStrokes + * and return a CTRL KeyStroke that will match an InputMap key + * But also some Command- key combinations + * are reserved by the operating system. So for those cases, it will + * check the list of reserved KeyStrokes, and not perform any remapping. + * If the platform also uses some specific key combinations + * to perform common actions across applications, such as Find or Replace, + * it is also possible to remap them in this method (application documentation + * should also note this sort of thing). + *

+ * For key events that would be remapped, (i.e. you remap META-S to KeyStrokes + * registered against CTRL-S and you receive a real CTRL-S), the key should + * first be checked for any explicit remapping, and if none is present, + * return a KeyStroke with the key set to + * KeyEvent.VK_UNDEFINED. + * + * @param stroke A KeyStroke input by the user + * @return A KeyStroke as may be registered in an InputMap, for example, + * CTRL_DOWN_MASK (what a module would register) for META_DOWN_MASK + * (the KeyStroke that is the default accelerator on OS-X). + */ + public final KeyStroke remap (KeyStroke stroke) { + if (suspended) { + return stroke; + } else { + return remapImpl (stroke); + } + } + + protected abstract KeyStroke remapImpl (KeyStroke stroke); + + /** Determine if the passed KeyStroke is one that the operating system + * claims, or if it can be used to register a shortcut. */ + public abstract boolean isMappable (KeyStroke stroke); + + private boolean suspended = false; + + /** Suspend remapping of keystrokes. Do not call this method + * lightly, and only with strong try/finally checking to ensure it + * is reënabled. This method exists solely for use + * by key binding editors, where key remapping would + * make it impossible for some key combinations to be entered. */ + public final void setSuspended(boolean value) { + System.err.println("SET SUSPENDED: " + value); + suspended = value; + } + + /** Convert a KeyStroke into the form in which it should be stored. + * In particular, this method may be used to or in + * EXPLICIT_MASK, a synthetic keymask used to identify + * keystrokes that were explicitly entered by the user, or supplied + * by a module and markedd as not to be remapped. These keys will + * not be remapped by remap(). + */ + public KeyStroke toStorageFormat (KeyStroke stroke) { + int mods = stroke.getModifiers(); + mods |= EXPLICIT_MASK; + return KeyStroke.getKeyStroke (stroke.getKeyCode(), mods, + stroke.isOnKeyRelease()); + } + + public KeyStroke toStandardFormat (KeyStroke stroke) { + int mods = stroke.getModifiers(); + if ((mods & EXPLICIT_MASK) != 0) { + mods &= ~ EXPLICIT_MASK; + return KeyStroke.getKeyStroke (stroke.getKeyCode(), mods, + stroke.isOnKeyRelease()); + } else { + return stroke; + } + } + + /** Remap a key event in the same manner as remap(KeyStroke). The + * default implementation simply calls remap(KeyStroke.getKeyStrokeForEvent()), + * sets the parameters of the passed KeyEvent to match the return value. */ + public KeyEvent remap(KeyEvent ke) { + KeyStroke stroke = remap(KeyStroke.getKeyStrokeForEvent(ke)); + ke.setKeyChar(stroke.getKeyChar()); + ke.setKeyCode(stroke.getKeyCode()); + ke.setModifiers(stroke.getModifiers()); + return ke; + } + + public KeyEvent dereference(KeyEvent ke) { + KeyStroke stroke = dereference(KeyStroke.getKeyStrokeForEvent(ke)); + ke.setKeyChar(stroke.getKeyChar()); + ke.setKeyCode(stroke.getKeyCode()); + ke.setModifiers(stroke.getModifiers()); + return ke; + } + + /** Convert a remapped key even back into its original form */ + public abstract KeyStroke dereference(KeyStroke ke); + + /** Mask in/out modifiers and return an altered keystroke */ + private static KeyStroke replaceModifiers (KeyStroke stroke, int toRemove, int toAdd) { + int mods = stroke.getModifiers(); + mods &= ~ toRemove; + mods |= toAdd; + + KeyStroke result; + if (stroke.toString().indexOf("keyChar") != -1) { + result = KeyStroke.getKeyStroke (new Character(stroke.getKeyChar()), mods); + } else { + result = KeyStroke.getKeyStroke (stroke.getKeyCode(), mods, stroke.isOnKeyRelease()); + } + return result; + } + + private static KeyStroke replace (KeyStroke stroke, int toRemove, int toAdd, KeyStroke newKey) { + int mods = stroke.getModifiers(); + mods &= ~ toRemove; + mods |= toAdd; + + KeyStroke result; + if (stroke.toString().indexOf("keyChar") != -1) { + result = KeyStroke.getKeyStroke (new Character(newKey.getKeyChar()), mods); + } else { + result = KeyStroke.getKeyStroke (newKey.getKeyCode(), mods, stroke.isOnKeyRelease()); + } + + return result; + } + + /** Ors EXPLICIT_MASK into a keystroke */ + protected static KeyStroke createExplicitKeyStroke (KeyStroke stroke) { + KeyStroke result = replaceModifiers (stroke, 0, EXPLICIT_MASK); + return result; + } + + /** Masks out EXPLICIT_MASK from a keystroke */ + public static boolean isExplicitKeyStroke (KeyStroke stroke) { + return (stroke.getModifiers() & EXPLICIT_MASK) != 0; + } + + /** Default implementation of KeyRemapper for platforms that need no + * remapping, i.e. Windows */ + private static final class NoOpRemapper extends KeyRemapper { + + public KeyStroke toStorageFormat(final KeyStroke stroke) { + return stroke; + } + + public boolean isMappable(final KeyStroke stroke) { + return true; + } + + public KeyStroke remapImpl(final KeyStroke stroke) { + return stroke; + } + + public KeyStroke dereference(final KeyStroke stroke) { + return stroke; + } + + protected KeyEvent remapImpl(final KeyEvent ke) { + return ke; + } + + public KeyEvent dereference(final KeyEvent ke) { + return ke; + } + } + + /** Standard KeyRemapper implementation for Mac OS-X. Remaps CTRL->Command + * for default KeyStrokes unless they conflict with the ones the system + * claims. */ + private static final class OsxRemapper extends KeyRemapper { + + public KeyStroke toStorageFormat(KeyStroke stroke) { + KeyStroke result = stroke; + if (stroke.getKeyCode() == KeyEvent.VK_CONTROL || stroke.getKeyCode() == + KeyEvent.VK_META || isExplicitKeyStroke(stroke)) { + //Will never happen for a user-entered KeyStroke, but could + //be called programmaticaly + return stroke; + } + + if ((stroke.getModifiers() & KeyEvent.CTRL_DOWN_MASK) != 0 || + (stroke.getModifiers() & KeyEvent.META_DOWN_MASK) != 0) { + + //Or in the mask indicting this is an explicit KeyStroke + result = createExplicitKeyStroke(stroke); + } + return result; + } + + public boolean isMappable (KeyStroke stroke) { + return getSystemKeyStrokes().contains(stroke); + } + + protected KeyStroke remapImpl (KeyStroke stroke) { + KeyStroke result; + if (stroke.getKeyCode() == KeyEvent.VK_CONTROL || stroke.getKeyCode() == + KeyEvent.VK_META || isExplicitKeyStroke(stroke)) { + + return stroke; + } + if (isMouseButtonMask(stroke.getModifiers())) { + //Don't remap mouse button combinations, it will change multi- + //selection semantics + return stroke; + } + //See if we have an explicit remapping for this key combination + if (getExplicitRemappings().keySet().contains(stroke)) { + result = (KeyStroke) getExplicitRemappings().get(stroke); + return result; + } + + if ((stroke.getModifiers() & KeyEvent.META_DOWN_MASK) != 0) { + if ((stroke.getModifiers() & KeyEvent.CTRL_DOWN_MASK) != 0) { + //CTRL+META+SomeKey - if it's not explictly defined, + //we shouldn't mess with its modifiers + return stroke; + } + + int toRemove = KeyEvent.META_DOWN_MASK | KeyEvent.META_MASK;; + int toAdd = 0; + + int newModifiers = stroke.getModifiers(); + if (isLaptopFunctionKeys() && isFunctionKey(stroke.getKeyCode())) { + //remap F-n to Meta-F-n + newModifiers = stroke.getModifiers() & ~ (KeyEvent.META_DOWN_MASK | KeyEvent.META_MASK); + + } else { + toAdd = KeyEvent.CTRL_DOWN_MASK | KeyEvent.CTRL_MASK; + } + + result = replaceModifiers(stroke, toRemove, toAdd); + + if (!getSystemKeyStrokes().contains(result)) { + return result; + } else { + return stroke; + } + + } else if ((stroke.getModifiers() & KeyEvent.ALT_DOWN_MASK) != 0) { + //Remap Windows ctrl-arrow key combos to Option (alt) - arrow keys + if (isArrowKey(stroke.getKeyCode())) { + result = replaceModifiers(stroke, KeyEvent.ALT_DOWN_MASK | KeyEvent.ALT_MASK, + KeyEvent.CTRL_DOWN_MASK | KeyEvent.CTRL_MASK); + + if (!getSystemKeyStrokes().contains(result)) { + return result; + } else { + return stroke; + } + + } + + //Weed out KeyStrokes that would be remapped so both CTRL and + //Command don't do the same thing; modules can register actions + //explicitly to these + return KeyStroke.getKeyStroke(KeyEvent.VK_UNDEFINED, stroke.getModifiers(), + stroke.isOnKeyRelease()); + } + + return stroke; + } + + public KeyStroke dereference(KeyStroke stroke) { + // System.err.println("DEREFERENCE " + stroke); + + if (stroke.getKeyCode() == KeyEvent.VK_UNDEFINED) { + throw new IllegalArgumentException ( + "Cannot dereference VK_UNDEFINED"); //NOI18N + } + + if (isExplicitKeyStroke(stroke)) { + //just return the explicit key, stripping the mask + return toStandardFormat(stroke); + } + + if (stroke.getKeyCode() == KeyEvent.VK_CONTROL || stroke.getKeyCode() == + KeyEvent.VK_META) { + return stroke; + } + + + if (getInverseExplicitRemappings().keySet().contains(stroke)) { + //If it's one we explicitly remap, just return its complement + return (KeyStroke) getInverseExplicitRemappings().get(stroke); + } + + if ((stroke.getModifiers() & KeyEvent.CTRL_DOWN_MASK) != 0) { + if ((stroke.getModifiers() & KeyEvent.META_DOWN_MASK) != 0) { + //CTRL+META+SomeKey - if it's not explictly defined, + //we shouldn't mess with its modifiers + return stroke; + } + + //Strip CTRL and replace with META + KeyStroke result = replaceModifiers(stroke, (KeyEvent.CTRL_DOWN_MASK | KeyEvent.CTRL_MASK), + KeyEvent.META_DOWN_MASK); + + //See if it's one the system will consume + if (!getSystemKeyStrokes().contains(result)) { + //If not, return it + return result; + } + + } else if ((stroke.getModifiers() & KeyEvent.META_DOWN_MASK) != 0) { + //Weed out KeyStrokes that would be remapped + return KeyStroke.getKeyStroke(KeyEvent.VK_UNDEFINED, stroke.getModifiers(), + stroke.isOnKeyRelease()); + } + return stroke; + } + + /** Get the set of KeyStrokes that this platform will consume, which + * cannot be mapped to shortcuts */ + private Set getSystemKeyStrokes() { + Set result = new HashSet(); + populateSet (systemKeys, result); + if (isMultiLocale()) { + populateSet (internationalKeys, result); + } + + return result; + } + + /** Populate a set with keystrokes, where the input array is an + * int[] of keyCode-modifier mask pairs. */ + private static void populateSet (int[] keys, final Set set) { + for (int i=0; i < keys.length; i+=2) { + int keycode = keys[i]; + int modifiers = keys[i + 1]; + set.add (KeyStroke.getKeyStroke(keycode, modifiers)); + } + } + + private static void populateMap (int[] keys, final Map map) { + for (int i=0; i < keys.length; i+=4) { + int origKey = keys[i]; + int origMods = keys[i + 1]; + int newKey = keys[i + 2]; + int newMods = keys[i + 3]; + KeyStroke old = KeyStroke.getKeyStroke (origKey, origMods); + KeyStroke nue = KeyStroke.getKeyStroke (newKey, newMods); + map.put (old, nue); + } + } + + private boolean isMultiLocale() { + //Installations of OS-X which support multiple languages bind + //Ctrl-Space to the binding change (like Alt-Shift on Windows). + //XXX Some handy way to detect this situation? + return Boolean.getBoolean("netbeans.osx.multilocale"); //NOI18N + } + + private boolean isLaptopFunctionKeys() { + //Installations of OS-X on laptops will have the function keys + //bound to things like monitor brightness and sound volume, and + //the application will never receive them. If true, these should + //be remapped to command. + return Boolean.getBoolean("netbeans.osx.laptopkeys"); //NOI18N + } + + /** Allow for explict KeyStroke translation, for remapping full key + * combinations to platform-specific defaults. */ + private Map getExplicitRemappings() { + Map result = new HashMap(); + populateMap (explicitRemappings, result); + return result; + } + + private Map inverseMappings = null; + + /** Get an inverse map of explicit key mappings as registered -> + * KeyStrokes as typed */ + private Map getInverseExplicitRemappings() { + if (inverseMappings == null) { + Map m = getExplicitRemappings(); + inverseMappings = new HashMap (m.size()); + if (!m.isEmpty()) { + Iterator i = m.keySet().iterator(); + while (i.hasNext()) { + Object key = i.next(); + Object val = m.get(key); + inverseMappings.put (val, key); + } + } + } + return inverseMappings; + } + } + + private static boolean isArrowKey (int keyCode) { + boolean result = Arrays.binarySearch(arrowKeys, keyCode) != -1; + return result; + } + + private static boolean isFunctionKey (int keyCode) { + boolean result = Arrays.binarySearch(functionKeys, keyCode) != -1; + return result; + } + + private static boolean isMouseButtonMask (int mask) { + //XXX fixme + return false; + /* + return ((mask & KeyEvent.BUTTON1_DOWN_MASK) | (mask & KeyEvent.BUTTON1_MASK) | + (mask & KeyEvent.BUTTON2_DOWN_MASK) | (mask & KeyEvent.BUTTON2_MASK) | + (mask & KeyEvent.BUTTON3_DOWN_MASK) | (mask & KeyEvent.BUTTON3_MASK)) + != 0; + */ + } + + + /** List of all function key keycodes */ + private static final int[] functionKeys = new int[] { + KeyEvent.VK_F1, + KeyEvent.VK_F2, + KeyEvent.VK_F3, + KeyEvent.VK_F4, + KeyEvent.VK_F5, + KeyEvent.VK_F6, + KeyEvent.VK_F7, + KeyEvent.VK_F8, + KeyEvent.VK_F9, + KeyEvent.VK_F10, + KeyEvent.VK_F11, + KeyEvent.VK_F12, + KeyEvent.VK_F13, + KeyEvent.VK_F14, + KeyEvent.VK_F15, + KeyEvent.VK_F16, + KeyEvent.VK_F17, + KeyEvent.VK_F18, + KeyEvent.VK_F19, + KeyEvent.VK_F20, + KeyEvent.VK_F21, + KeyEvent.VK_F22, + KeyEvent.VK_F23, + KeyEvent.VK_F24 + }; + + /** List of all arrow key keycodes */ + private static final int[] arrowKeys = new int[] { + KeyEvent.VK_RIGHT, + KeyEvent.VK_LEFT, + KeyEvent.VK_DOWN, + KeyEvent.VK_UP + }; + + /** List explicit remappings for Mac in the format + * key-to-map, key-to-map-modifiers, standard-key, standard-key-modifiers + */ + private static final int[] explicitRemappings = new int[] { + + //Mac has no delete key, so simulate w/ Command-backspace + KeyEvent.VK_BACK_SPACE, KeyEvent.META_DOWN_MASK, + KeyEvent.VK_DELETE, 0, + + //convert Command-G (standard mac find-next) + //to NetBeans default F3 + KeyEvent.VK_G, KeyEvent.META_DOWN_MASK, + KeyEvent.VK_F3, 0, + + //Also remap the Goto command which is normally Ctrl-G to + //Meta-Shift-G, since Command-G will now be used for find-next + KeyEvent.VK_G, KeyEvent.META_DOWN_MASK | KeyEvent.SHIFT_DOWN_MASK, + KeyEvent.VK_G, KeyEvent.CTRL_DOWN_MASK, + + //Use Command-B for next bookmark + KeyEvent.VK_B, KeyEvent.META_DOWN_MASK, + KeyEvent.VK_F2, 0, + + + //and Command-Shift-B for create bookmark + KeyEvent.VK_B, KeyEvent.META_DOWN_MASK | KeyEvent.SHIFT_DOWN_MASK, + KeyEvent.VK_F2, KeyEvent.CTRL_DOWN_MASK + }; + + + /** Array of all of the system keys that should never be + * remapped, in the form keyCode, modifiers */ + private static final int[] systemKeys = new int[] { + + //Command-H, hide current app + KeyEvent.VK_H, KeyEvent.META_DOWN_MASK, + + //Command-Space, switch locale + KeyEvent.VK_SPACE, KeyEvent.META_DOWN_MASK, + + //command-option-d, show/hide dock + KeyEvent.VK_D, KeyEvent.META_DOWN_MASK | KeyEvent.ALT_DOWN_MASK, + + KeyEvent.VK_H, KeyEvent.META_MASK | KeyEvent.ALT_MASK, + + //command-shift-q, log out + KeyEvent.VK_Q, KeyEvent.META_MASK | KeyEvent.SHIFT_MASK, + + //command-shift-option-q, log out with no confirmation + KeyEvent.VK_Q, KeyEvent.META_MASK | KeyEvent.ALT_MASK | KeyEvent.SHIFT_MASK, + + //command-shift-ctrl-option-q, force log out with no confirmation + KeyEvent.VK_Q, KeyEvent.META_MASK | KeyEvent.ALT_MASK | KeyEvent.SHIFT_MASK | KeyEvent.CTRL_MASK + }; + + /** List of additional keys which multi-locale OS-X installs will + * also consume */ + private static final int[] internationalKeys = new int[] { + //command-space, switch script system + KeyEvent.VK_SPACE, KeyEvent.META_MASK, + + //command-right, change keyboard layout to Roman + KeyEvent.VK_RIGHT, KeyEvent.META_MASK, + + //command-left, change keyboard layout to system + KeyEvent.VK_LEFT, KeyEvent.META_MASK + }; + + /* + //main method for testing + public static void main (String[] args) { + KeyRemapper kr = new KeyRemapper.OsxRemapper(); + KeyStroke toTest = KeyStroke.getKeyStroke(KeyEvent.VK_M, KeyEvent.META_DOWN_MASK, + false); + KeyStroke out = kr.remap(toTest); + System.err.println(toTest + " -> " + out + " -> " + kr.dereference(out)); + + toTest = KeyStroke.getKeyStroke(KeyEvent.VK_D, KeyEvent.CTRL_DOWN_MASK); + out = kr.remap(toTest); + System.err.println(toTest + " -> " + out + " -> " + kr.dereference(out)); + + toTest = KeyStroke.getKeyStroke(KeyEvent.VK_G, KeyEvent.META_DOWN_MASK); + out = kr.remap(toTest); + System.err.println(toTest + " -> " + out + " -> " + kr.dereference(out)); + + toTest = KeyStroke.getKeyStroke(KeyEvent.VK_M, KeyEvent.CTRL_DOWN_MASK, + false); + out = kr.remap(toTest); + System.err.println(toTest + " -> " + out); + + toTest = KeyStroke.getKeyStroke(KeyEvent.VK_M, KeyEvent.CTRL_DOWN_MASK, + false); + out = kr.toStorageFormat(toTest); + System.err.println(toTest + " converted to " + out); + boolean hasMask = (out.getModifiers() & EXPLICIT_MASK) != 0; + System.err.println("Retained mask: " + hasMask); + + KeyStroke foo = KeyStroke.getKeyStroke(KeyEvent.VK_T, EXPLICIT_MASK); + hasMask = (foo.getModifiers() & EXPLICIT_MASK) != 0; + System.err.println("Synthetic mask explicit mask: " + hasMask + foo.getModifiers()); + + foo = KeyStroke.getKeyStroke(KeyEvent.VK_T, 0); + System.err.println("Synthetic mask explicit mask: " + hasMask + foo.getModifiers()); + + foo = createExplicitKeyStroke(foo); + System.err.println("Synthetic mask explicit mask: " + hasMask + foo.getModifiers()); + } + */ + + }