diff options
author | Sebastien Vincent <seb@jitsi.org> | 2012-03-06 09:47:38 +0000 |
---|---|---|
committer | Sebastien Vincent <seb@jitsi.org> | 2012-03-06 09:47:38 +0000 |
commit | 957b8633ae93bc0efbb0b8eff1f40db6cb660485 (patch) | |
tree | 190e0f334664dfa71876f46aa7bd87d08469a613 /src/net/java/sip/communicator | |
parent | da7a9769953d75d14f38dbf48c49893e82a3c6cb (diff) | |
download | jitsi-957b8633ae93bc0efbb0b8eff1f40db6cb660485.zip jitsi-957b8633ae93bc0efbb0b8eff1f40db6cb660485.tar.gz jitsi-957b8633ae93bc0efbb0b8eff1f40db6cb660485.tar.bz2 |
Introduces new configuration from to setup global shortcut entry. Allows also to use special key detection for headset button _for MS Windows only_.
Diffstat (limited to 'src/net/java/sip/communicator')
10 files changed, 802 insertions, 158 deletions
diff --git a/src/net/java/sip/communicator/impl/globalshortcut/GlobalShortcutServiceImpl.java b/src/net/java/sip/communicator/impl/globalshortcut/GlobalShortcutServiceImpl.java index d1cda56..9268dc3 100644 --- a/src/net/java/sip/communicator/impl/globalshortcut/GlobalShortcutServiceImpl.java +++ b/src/net/java/sip/communicator/impl/globalshortcut/GlobalShortcutServiceImpl.java @@ -39,6 +39,11 @@ public class GlobalShortcutServiceImpl new HashMap<GlobalShortcutListener, List<AWTKeyStroke>>(); /** + * List of notifiers when special key detection is enabled. + */ + private final List<GlobalShortcutListener> specialKeyNotifiers = + new ArrayList<GlobalShortcutListener>(); + /** * If the service is running or not. */ private boolean isRunning = false; @@ -59,6 +64,16 @@ public class GlobalShortcutServiceImpl private final UIShortcut uiShortcut = new UIShortcut(); /** + * Last special key detected. + */ + private AWTKeyStroke specialKeyDetected = null; + + /** + * Object to synchronize special key detection. + */ + private final Object specialKeySyncRoot = new Object(); + + /** * Initializes the <tt>GlobalShortcutServiceImpl</tt>. */ public GlobalShortcutServiceImpl() @@ -90,6 +105,7 @@ public class GlobalShortcutServiceImpl synchronized(mapActions) { List<AWTKeyStroke> keystrokes = mapActions.get(listener); + boolean ok = false; if(keyStroke == null) { @@ -98,21 +114,38 @@ public class GlobalShortcutServiceImpl if(keystrokes != null) { - if(keyboardHook.registerShortcut(keyStroke.getKeyCode(), - getModifiers(keyStroke))) + if(keyStroke.getModifiers() != SPECIAL_KEY_MODIFIERS) + { + ok = keyboardHook.registerShortcut(keyStroke.getKeyCode(), + getModifiers(keyStroke)); + } + else { - if(add) - keystrokes.add(keyStroke); + ok = keyboardHook.registerSpecial(keyStroke.getKeyCode()); + } + + if(ok && add) + { + keystrokes.add(keyStroke); } } else { keystrokes = new ArrayList<AWTKeyStroke>(); - if(keyboardHook.registerShortcut(keyStroke.getKeyCode(), - getModifiers(keyStroke))) + + if(keyStroke.getModifiers() != SPECIAL_KEY_MODIFIERS) { - if(add) - keystrokes.add(keyStroke); + ok = keyboardHook.registerShortcut(keyStroke.getKeyCode(), + getModifiers(keyStroke)); + } + else + { + ok = keyboardHook.registerSpecial(keyStroke.getKeyCode()); + } + + if(ok && add) + { + keystrokes.add(keyStroke); } } @@ -160,8 +193,15 @@ public class GlobalShortcutServiceImpl ks = l; } - keyboardHook.unregisterShortcut(keyStroke.getKeyCode(), - getModifiers(keyStroke)); + if(modifiers != SPECIAL_KEY_MODIFIERS) + { + keyboardHook.unregisterShortcut(keyStroke.getKeyCode(), + getModifiers(keyStroke)); + } + else + { + keyboardHook.unregisterSpecial(keyStroke.getKeyCode()); + } if(remove) { @@ -228,6 +268,30 @@ public class GlobalShortcutServiceImpl */ public synchronized void receiveKey(int keycode, int modifiers) { + if(keyboardHook.isSpecialKeyDetection()) + { + specialKeyDetected = AWTKeyStroke.getAWTKeyStroke(keycode, + modifiers); + + synchronized(specialKeySyncRoot) + { + specialKeySyncRoot.notify(); + } + + GlobalShortcutEvent evt = new GlobalShortcutEvent( + specialKeyDetected); + List<GlobalShortcutListener> copyListeners = + new ArrayList<GlobalShortcutListener>(specialKeyNotifiers); + + for(GlobalShortcutListener l : copyListeners) + { + l.shortcutReceived(evt); + } + + // if special key detection is enabled, disable all other shortcuts + return; + } + synchronized(mapActions) { // compare keycode/modifiers to keystroke @@ -239,7 +303,9 @@ public class GlobalShortcutServiceImpl for(AWTKeyStroke l : lst) { if(l.getKeyCode() == keycode && - getModifiers(l) == modifiers) + (getModifiers(l) == modifiers || + (modifiers == SPECIAL_KEY_MODIFIERS && + l.getModifiers() == modifiers))) { // notify corresponding listeners GlobalShortcutEvent evt = new GlobalShortcutEvent(l); @@ -382,6 +448,67 @@ public class GlobalShortcutServiceImpl } /** + * Enable or disable special key detection. + * + * @param enable enable or not special key detection. + * @param callback object to be notified + */ + public synchronized void setSpecialKeyDetection(boolean enable, + GlobalShortcutListener callback) + { + keyboardHook.detectSpecialKeyPress(enable); + + if(specialKeyNotifiers.contains(callback) == enable) + { + return; + } + + if(enable) + { + specialKeyNotifiers.add(callback); + } + else + { + specialKeyNotifiers.remove(callback); + } + } + + /** + * Get special keystroke or null if not supported or user cancels. If no + * special key is detected for 5 seconds, it returns null + * + * @return special keystroke or null if not supported or user cancels + */ + public AWTKeyStroke getSpecialKey() + { + AWTKeyStroke ret = null; + + specialKeyDetected = null; + keyboardHook.detectSpecialKeyPress(true); + + // Windows only for the moment + if(OSUtils.IS_WINDOWS) + { + synchronized(specialKeySyncRoot) + { + try + { + specialKeySyncRoot.wait(5000); + } + catch(InterruptedException e) + { + } + } + + ret = specialKeyDetected; + specialKeyDetected = null; + } + + keyboardHook.detectSpecialKeyPress(false); + return ret; + } + + /** * Simple test. */ public void test() diff --git a/src/net/java/sip/communicator/impl/globalshortcut/NativeKeyboardHook.java b/src/net/java/sip/communicator/impl/globalshortcut/NativeKeyboardHook.java index e2101c4..b61971b 100644 --- a/src/net/java/sip/communicator/impl/globalshortcut/NativeKeyboardHook.java +++ b/src/net/java/sip/communicator/impl/globalshortcut/NativeKeyboardHook.java @@ -35,6 +35,11 @@ public class NativeKeyboardHook private static long ptr = 0; /** + * If the special key detection is enable. + */ + private boolean specialKeydetection = false; + + /** * Constructor. */ public NativeKeyboardHook() @@ -106,6 +111,57 @@ public class NativeKeyboardHook } /** + * Register a special key shortcut (for example key coming from headset). + * + * @param keycode keycode of the shortcut + * @param modifiers modifiers (CTRL, ALT, ...) + * @return true if success, false otherwise + */ + public synchronized boolean registerSpecial(int keycode) + { + if(ptr != 0) + return registerSpecial(ptr, keycode); + + return false; + } + + /** + * Unregister a special key shortcut (for example key coming from headset). + * + * @param keycode keycode of the shortcut + * @param modifiers modifiers (CTRL, ALT, ...) + */ + public synchronized void unregisterSpecial(int keycode) + { + if(ptr != 0) + unregisterSpecial(ptr, keycode); + } + + /** + * Detect special key press. + * + * @param enable enable or not the special key press detection. + */ + public synchronized void detectSpecialKeyPress(boolean enable) + { + if(ptr != 0) + { + detectSpecialKeyPress(ptr, enable); + this.specialKeydetection = enable; + } + } + + /** + * Returns whether or not special key detection is enabled. + * + * @return true if special key detection is enabled, false otherwise + */ + public boolean isSpecialKeyDetection() + { + return specialKeydetection; + } + + /** * Native method to initialize <tt>NativeKeyboardHook</tt>. * * @return native pointer. @@ -141,6 +197,7 @@ public class NativeKeyboardHook * @param ptr native pointer * @param keycode keycode of the shortcut * @param modifiers modifiers (CTRL, ALT, ...) + * @return true if registration is successful, false otherwise */ private static native boolean registerShortcut(long ptr, int keycode, int modifiers); @@ -155,6 +212,33 @@ public class NativeKeyboardHook private static native void unregisterShortcut(long ptr, int keycode, int modifiers); + /** + * Native method to register a special key shortcut (for example key coming + * from a headset). + * + * @param ptr native pointer + * @param keycode keycode of the shortcut + * @return true if registration is successful, false otherwise + */ + private static native boolean registerSpecial(long ptr, int keycode); + + /** + * Native method to unregister a special key shortcut (for example key + * coming from a headset). + * + * @param ptr native pointer + * @param keycode keycode of the shortcut + */ + private static native void unregisterSpecial(long ptr, int keycode); + + /** + * Native method to ook for special key press. + * + * @param ptr native pointer + * @param enable enable or not the special key press detection. + */ + private static native void detectSpecialKeyPress(long ptr, boolean enable); + static { try diff --git a/src/net/java/sip/communicator/impl/keybindings/KeybindingsServiceImpl.java b/src/net/java/sip/communicator/impl/keybindings/KeybindingsServiceImpl.java index a7a7a4f..1089051 100644 --- a/src/net/java/sip/communicator/impl/keybindings/KeybindingsServiceImpl.java +++ b/src/net/java/sip/communicator/impl/keybindings/KeybindingsServiceImpl.java @@ -183,16 +183,16 @@ class KeybindingsServiceImpl Map<KeyStroke, String> customTmp = new LinkedHashMap<KeyStroke, String>(customBindings); - for (Map.Entry<KeyStroke, String> shortcut2action : defaultBindings - .entrySet()) + for (Map.Entry<KeyStroke, String> shortcut2action : + defaultBindings.entrySet()) { String action = shortcut2action.getValue(); if (customTmp.containsValue(action)) { KeyStroke custom = null; - for (Map.Entry<KeyStroke, String> customShortcut2action : customTmp - .entrySet()) + for(Map.Entry<KeyStroke, String> customShortcut2action : + customTmp.entrySet()) { if (customShortcut2action.getValue().equals(action)) { @@ -383,9 +383,15 @@ class KeybindingsServiceImpl { kss.add(AWTKeyStroke.getAWTKeyStroke(shortcut)); } + + // second shortcut is always "special" if(shortcut2 != null) { - kss.add(AWTKeyStroke.getAWTKeyStroke(shortcut2)); + //16367 is the combination of all possible modifiers + //(shift ctrl meta alt altGraph button1 button2 button3 pressed) + //it is used to distinguish special key (headset) and others + int nb = Integer.parseInt(shortcut2); + kss.add(AWTKeyStroke.getAWTKeyStroke(nb, 16367)); } gBindings.put(name, kss); } @@ -417,8 +423,10 @@ class KeybindingsServiceImpl configService.setProperty(shortcut, kss.size() > 0 ? kss.get(0) : null); - configService.setProperty(shortcut2, kss.size() > 1 ? - kss.get(1) : null); + // second shortcut is special + configService.setProperty(shortcut2, + (kss.size() > 1 && kss.get(1) != null) ? + kss.get(1).getKeyCode() : null); } } diff --git a/src/net/java/sip/communicator/plugin/keybindingchooser/globalchooser/GlobalShortcutConfigForm.java b/src/net/java/sip/communicator/plugin/keybindingchooser/globalchooser/GlobalShortcutConfigForm.java index 65e955d..034d6d8 100644 --- a/src/net/java/sip/communicator/plugin/keybindingchooser/globalchooser/GlobalShortcutConfigForm.java +++ b/src/net/java/sip/communicator/plugin/keybindingchooser/globalchooser/GlobalShortcutConfigForm.java @@ -62,16 +62,6 @@ public class GlobalShortcutConfigForm new GlobalShortcutTableModel(); /** - * Current selected row. - */ - private int currentRow = -1; - - /** - * Current selected row. - */ - private int currentColumn = -1; - - /** * Constructor */ public GlobalShortcutConfigForm() @@ -97,152 +87,76 @@ public class GlobalShortcutConfigForm @Override public void mouseClicked(MouseEvent e) { - if(e.getClickCount() >= 1) + if(e.getClickCount() >= 2) { int row = GlobalShortcutConfigForm.this.shortcutsTable. getSelectedRow(); - int column = GlobalShortcutConfigForm.this.shortcutsTable. - getSelectedColumn(); - - if(currentRow != -1 && currentColumn != -1) - return; - - if(row >= 0 && column >= 1) - { - currentRow = row; - currentColumn = column; - - if(column == 1) - GlobalShortcutConfigForm.this.tableModel.getEntryAt( - row).setEditShortcut1(true); - else if(column == 2) - GlobalShortcutConfigForm.this.tableModel.getEntryAt( - row).setEditShortcut2(true); - else - return; - - KeybindingChooserActivator.getGlobalShortcutService(). - setEnable(false); - refresh(); - shortcutsTable.setRowSelectionInterval(row, row); - } - } - } - }); - - shortcutsTable.addKeyListener(new KeyAdapter() - { - private KeyEvent buffer = null; - - @Override - public void keyPressed(KeyEvent event) - { - if(currentRow == -1 || currentColumn == -1) - return; - - // delete shortcut - if(event.getKeyCode() == KeyEvent.VK_BACK_SPACE) - { GlobalShortcutEntry en = - GlobalShortcutConfigForm.this.tableModel.getEntryAt( - currentRow); - List<AWTKeyStroke> kss = new ArrayList<AWTKeyStroke>(); - if(currentColumn == 1) - { - kss.add(null); - kss.add(en.getShortcut2()); - } - else if(currentColumn == 2) - { - kss.add(en.getShortcut()); - kss.add(null); - } - - currentRow = -1; - currentColumn = -1; - en.setShortcuts(kss); - en.setEditShortcut1(false); - en.setEditShortcut2(false); - GlobalShortcutConfigForm.this.saveConfig(); - GlobalShortcutConfigForm.this.refresh(); - } - else - { - // Reports KEY_PRESSED events on release to support - // modifiers - this.buffer = event; - } - } + GlobalShortcutConfigForm.this.tableModel. + getEntryAt(row); + List<AWTKeyStroke> kss = new ArrayList<AWTKeyStroke>(); - @Override - public void keyReleased(KeyEvent event) - { - if (buffer != null) - { - AWTKeyStroke input = KeyStroke.getKeyStrokeForEvent(buffer); - buffer = null; - - if(currentRow != -1) - { - GlobalShortcutEntry en = - GlobalShortcutConfigForm.this.tableModel.getEntryAt( - currentRow); - List<AWTKeyStroke> kss = new ArrayList<AWTKeyStroke>(); + GlobalShortcutDialog dialog = + new GlobalShortcutDialog((Dialog) + GlobalShortcutConfigForm.this.getTopLevelAncestor(), + en); - if(currentColumn == 1) // shortcut 1 - { - kss.add(input); - kss.add(en.getShortcut2()); - } - else if(currentColumn == 2) // shortcut 2 - { - kss.add(en.getShortcut()); - kss.add(input); - } - else - { - return; - } + kss.add(en.getShortcut()); + kss.add(en.getShortcut2()); - en.setShortcuts(kss); - en.setEditShortcut1(false); - en.setEditShortcut2(false); + KeybindingChooserActivator.getGlobalShortcutService(). + setEnable(false); + int ret = dialog.showDialog(); + if(ret == 1) + { + // ok button clicked kss = new ArrayList<AWTKeyStroke>(); - List<GlobalShortcutEntry> lst = tableModel.getEntries(); + List<GlobalShortcutEntry> lst = + tableModel.getEntries(); - for(GlobalShortcutEntry e : lst) + for(GlobalShortcutEntry ee : lst) { - boolean isEntry = (e == en); - AWTKeyStroke s1 = isEntry && - currentColumn == 1 ? null : e.getShortcut(); - AWTKeyStroke s2 = isEntry && - currentColumn == 2 ? null : e.getShortcut2(); - - if(s1 != null && - s1.getKeyCode() == input.getKeyCode() && - s1.getModifiers() == input.getModifiers()) + boolean isEntry = (ee == en); + AWTKeyStroke s1 = isEntry ? null : + ee.getShortcut(); + AWTKeyStroke s2 = isEntry ? null : + ee.getShortcut2(); + + if(s1 != null && en.getShortcut() != null && + s1.getKeyCode() == en.getShortcut(). + getKeyCode() && + s1.getModifiers() == en.getShortcut(). + getModifiers()) { kss.add(null); - kss.add(e.getShortcut2()); - e.setShortcuts(kss); + kss.add(ee.getShortcut2()); + ee.setShortcuts(kss); break; } - else if(s2 != null && - s2.getKeyCode() == input.getKeyCode() && - s2.getModifiers() == input.getModifiers()) + else if(s2 != null && en.getShortcut2() != null && + s2.getKeyCode() == en.getShortcut2(). + getKeyCode() && + s2.getModifiers() == en.getShortcut2(). + getModifiers()) { - kss.add(e.getShortcut()); + kss.add(ee.getShortcut()); kss.add(null); - e.setShortcuts(kss); + ee.setShortcuts(kss); break; } } - currentRow = -1; - currentColumn = -1; + KeybindingChooserActivator.getGlobalShortcutService(). + setEnable(true); GlobalShortcutConfigForm.this.saveConfig(); GlobalShortcutConfigForm.this.refresh(); + KeybindingChooserActivator.getGlobalShortcutService(). + setEnable(true); + } + else + { + en.setShortcuts(kss); } } } diff --git a/src/net/java/sip/communicator/plugin/keybindingchooser/globalchooser/GlobalShortcutDialog.java b/src/net/java/sip/communicator/plugin/keybindingchooser/globalchooser/GlobalShortcutDialog.java new file mode 100644 index 0000000..12a8727 --- /dev/null +++ b/src/net/java/sip/communicator/plugin/keybindingchooser/globalchooser/GlobalShortcutDialog.java @@ -0,0 +1,475 @@ +/*
+ * Jitsi, the OpenSource Java VoIP and Instant Messaging client.
+ *
+ * Distributable under LGPL license.
+ * See terms of license at gnu.org.
+ */
+package net.java.sip.communicator.plugin.keybindingchooser.globalchooser;
+
+import java.awt.*;
+import java.awt.event.*;
+import java.util.*;
+import java.util.List; // disambiguation
+
+import javax.swing.*;
+
+import net.java.sip.communicator.plugin.keybindingchooser.*;
+import net.java.sip.communicator.service.globalshortcut.*;
+import net.java.sip.communicator.util.*;
+import net.java.sip.communicator.util.skin.*;
+import net.java.sip.communicator.util.swing.*;
+import net.java.sip.communicator.util.swing.plaf.*;
+
+/**
+ * Dialog to choose the shortcut.
+ *
+ * @author Sebastien Vincent
+ */
+public class GlobalShortcutDialog
+ extends SIPCommDialog
+ implements ActionListener,
+ GlobalShortcutListener
+{
+ /**
+ * Serial version UID.
+ */
+ private static final long serialVersionUID = 0L;
+
+ /**
+ * Text displayed when no shortcut is configured.
+ */
+ private static final String PRESS_TO_SETUP_SHORTCUT =
+ Resources.getString("plugin.keybindings.globalchooser.PRESS_BTN");
+
+ /**
+ * The global shortcut entry.
+ */
+ private final GlobalShortcutEntry entry;
+
+ /**
+ * OK button.
+ */
+ private final JButton btnOK = new JButton(
+ Resources.getString("service.gui.OK"));
+
+ /**
+ * Cancel button.
+ */
+ private final JButton btnCancel = new JButton(
+ Resources.getString("service.gui.CANCEL"));
+
+ /**
+ * Enable or not special key for shortcut.
+ */
+ private final JCheckBox specialBox = new SIPCommCheckBox(
+ Resources.getString("plugin.keybindings.globalchooser.ENABLE_SPECIAL"));
+
+ /**
+ * First shortcut field.
+ */
+ private final ShortcutField fldShortcut = new ShortcutField(
+ PRESS_TO_SETUP_SHORTCUT);
+
+ /**
+ * Secondary shortcut field.
+ */
+ private final ShortcutField fldShortcut2 = new ShortcutField(
+ PRESS_TO_SETUP_SHORTCUT);
+
+ /**
+ * Return code.
+ */
+ private int retCode = 0;
+
+ /**
+ * Constructor.
+ *
+ * @param dialog root dialog
+ * @param entry the global shortcut entry
+ */
+ public GlobalShortcutDialog(Dialog dialog, GlobalShortcutEntry entry)
+ {
+ super(dialog);
+
+ setModal(true);
+ setTitle("Global shortcut: " + entry.getAction());
+ this.entry = entry;
+ init();
+ }
+
+ /**
+ * Initialize components.
+ */
+ private void init()
+ {
+ TransparentPanel mainPanel = new TransparentPanel(new BorderLayout());
+ JPanel btnPanel = new TransparentPanel(
+ new FlowLayout(FlowLayout.RIGHT));
+ JPanel shortcutPanel = new TransparentPanel(
+ new GridLayout(0, 2, 0, 10));
+
+ btnOK.addActionListener(this);
+ btnCancel.addActionListener(this);
+
+ KeyAdapter keyAdapter = new KeyAdapter()
+ {
+ private KeyEvent buffer = null;
+
+ @Override
+ public void keyPressed(KeyEvent event)
+ {
+ if(event.getKeyCode() == KeyEvent.VK_ESCAPE)
+ {
+ SIPCommTextField field =
+ (SIPCommTextField)event.getSource();
+
+ AWTKeyStroke ks = null;
+
+ if(field == fldShortcut)
+ {
+ ks = entry.getShortcut();
+ }
+ else if(field == fldShortcut2)
+ {
+ ks = entry.getShortcut2();
+ }
+
+ if(ks == null)
+ field.setText(PRESS_TO_SETUP_SHORTCUT);
+ else
+ {
+
+ if(ks.getModifiers() ==
+ GlobalShortcutService.SPECIAL_KEY_MODIFIERS)
+ {
+ field.setText("Special");
+ }
+ else
+ {
+ field.setText(GlobalShortcutEntry.getShortcutText(
+ entry.getShortcut()));
+ }
+ }
+ btnOK.requestFocusInWindow();
+ return;
+ }
+
+ if(event.getKeyCode() == 0)
+ return;
+
+ // Reports KEY_PRESSED events on release to support modifiers
+ this.buffer = event;
+ }
+
+ @Override
+ public void keyReleased(KeyEvent event)
+ {
+ if (buffer != null)
+ {
+ SIPCommTextField field =
+ (SIPCommTextField)event.getSource();
+ AWTKeyStroke input = KeyStroke.getKeyStrokeForEvent(buffer);
+ buffer = null;
+
+ GlobalShortcutEntry en = entry;
+ List<AWTKeyStroke> kss = new ArrayList<AWTKeyStroke>();
+
+ kss.add(input);
+ kss.add(en.getShortcut2());
+
+ en.setShortcuts(kss);
+ en.setEditShortcut1(false);
+ en.setEditShortcut2(false);
+ field.setText(GlobalShortcutEntry.getShortcutText(
+ input));
+ btnOK.requestFocus();
+ }
+ }
+ };
+
+ AWTKeyStroke ks = entry.getShortcut();
+ AWTKeyStroke ks2 = entry.getShortcut2();
+
+ if(ks != null)
+ {
+ if(ks.getModifiers() != GlobalShortcutService.SPECIAL_KEY_MODIFIERS)
+ {
+ fldShortcut.setText(GlobalShortcutEntry.getShortcutText(ks));
+ }
+ else
+ {
+ fldShortcut.setText("Special");
+ }
+ }
+
+ if(ks2 != null)
+ {
+
+ if(ks2.getModifiers() != GlobalShortcutService.SPECIAL_KEY_MODIFIERS)
+ {
+ fldShortcut2.setText(GlobalShortcutEntry.getShortcutText(ks2));
+ }
+ else
+ {
+ fldShortcut2.setText("Special");
+ }
+ }
+
+ fldShortcut.addKeyListener(keyAdapter);
+ fldShortcut2.addKeyListener(keyAdapter);
+
+ specialBox.addItemListener(new ItemListener()
+ {
+ public void itemStateChanged(ItemEvent evt)
+ {
+ KeybindingChooserActivator.getGlobalShortcutService().
+ setSpecialKeyDetection(
+ (evt.getStateChange() == ItemEvent.SELECTED),
+ GlobalShortcutDialog.this);
+ }
+ });
+
+ shortcutPanel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10,
+ 10));
+ shortcutPanel.add(new JLabel("Primary shortcut"));
+ shortcutPanel.add(fldShortcut);
+ shortcutPanel.add(new JLabel("Secondary shortcut"));
+ shortcutPanel.add(fldShortcut2);
+
+ if(OSUtils.IS_WINDOWS)
+ {
+ shortcutPanel.add(new TransparentPanel());
+ shortcutPanel.add(specialBox);
+ }
+
+ mainPanel.add(shortcutPanel, BorderLayout.CENTER);
+
+ btnPanel.add(btnOK);
+ btnPanel.add(btnCancel);
+ mainPanel.add(btnPanel, BorderLayout.SOUTH);
+
+ btnOK.requestFocus();
+
+ getContentPane().add(mainPanel);
+ pack();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ protected void close(boolean isEscaped)
+ {
+ super.close(isEscaped);
+ KeybindingChooserActivator.getGlobalShortcutService().
+ setSpecialKeyDetection(false, this);
+ }
+
+ /**
+ * Show the dialog and returns if the user has modified something (create
+ * or modify entry).
+ *
+ * @return true if the user has modified something (create
+ * or modify entry), false otherwise.
+ */
+ public int showDialog()
+ {
+ setVisible(true);
+
+ // as the dialog is modal, wait for OK/Cancel button retCode
+ setVisible(false);
+ return retCode;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void actionPerformed(ActionEvent evt)
+ {
+ Object obj = evt.getSource();
+
+ if(obj == btnOK)
+ {
+ retCode = 1;
+ dispose();
+ }
+ else if(obj == btnCancel)
+ {
+ retCode = 0;
+ dispose();
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void shortcutReceived(GlobalShortcutEvent evt)
+ {
+ AWTKeyStroke ksr = evt.getKeyStroke();
+
+ if(ksr.getModifiers() != GlobalShortcutService.SPECIAL_KEY_MODIFIERS)
+ {
+ return;
+ }
+
+ if(!fldShortcut.isFocusOwner() && !fldShortcut2.isFocusOwner())
+ return;
+
+ List<AWTKeyStroke> kss = new ArrayList<AWTKeyStroke>();
+
+ if(fldShortcut.isFocusOwner())
+ {
+ kss.add(ksr);
+ kss.add(entry.getShortcut2());
+ fldShortcut.setText("Special");
+ }
+ else if(fldShortcut2.isFocusOwner())
+ {
+ kss.add(entry.getShortcut());
+ kss.add(ksr);
+ fldShortcut2.setText("Special");
+ }
+ entry.setShortcuts(kss);
+ KeybindingChooserActivator.getGlobalShortcutService().
+ setSpecialKeyDetection(false, this);
+ }
+
+ /**
+ * Clear the text field.
+ *
+ * @param ui <tt>TextFieldUI</tt> to clear
+ */
+ public void clearTextField(SIPCommTextFieldUI ui)
+ {
+ List<AWTKeyStroke> kss = new ArrayList<AWTKeyStroke>();
+
+ if(ui == fldShortcut.getUI())
+ {
+ kss.add(null);
+ kss.add(entry.getShortcut2());
+ entry.setShortcuts(kss);
+ btnOK.requestFocusInWindow();
+ }
+ else if(ui == fldShortcut2.getUI())
+ {
+ kss.add(entry.getShortcut());
+ kss.add(null);
+ entry.setShortcuts(kss);
+ btnOK.requestFocusInWindow();
+ }
+ }
+
+ /**
+ * A custom call field.
+ */
+ private class ShortcutField
+ extends SIPCommTextField
+ implements Skinnable
+ {
+ /**
+ * Serial version UID.
+ */
+ private static final long serialVersionUID = 0L;
+
+ /**
+ * The text field ui.
+ */
+ private ShortcutFieldUI textFieldUI;
+
+ /**
+ * Creates an instance of the <tt>CallField</tt>.
+ *
+ * @param text
+ */
+ public ShortcutField(String text)
+ {
+ super(text);
+
+ textFieldUI = new ShortcutFieldUI();
+ textFieldUI.setDeleteButtonEnabled(true);
+
+ this.setPreferredSize(new Dimension(200, 23));
+ this.setUI(textFieldUI);
+ this.setBorder(null);
+ this.setOpaque(false);
+
+ this.setDragEnabled(true);
+
+ loadSkin();
+ }
+
+ /**
+ * Reloads text field UI defs.
+ */
+ public void loadSkin()
+ {
+ textFieldUI.loadSkin();
+ }
+ }
+
+ public class ShortcutFieldUI
+ extends SIPCommTextFieldUI
+ implements Skinnable
+ {
+ /**
+ * Creates a <tt>SIPCommTextFieldUI</tt>.
+ */
+ public ShortcutFieldUI()
+ {
+ loadSkin();
+ }
+
+ /**
+ * Adds the custom mouse listeners defined in this class to the installed
+ * listeners.
+ */
+ protected void installListeners()
+ {
+ super.installListeners();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ protected void updateDeleteIcon(MouseEvent evt)
+ {
+ super.updateDeleteIcon(evt);
+
+ Rectangle deleteRect = getDeleteButtonRect();
+ if(deleteRect.contains(evt.getX(), evt.getY()) &&
+ evt.getID() == MouseEvent.MOUSE_CLICKED)
+ {
+ clearTextField(this);
+ }
+ }
+
+ /**
+ * Implements parent paintSafely method and enables antialiasing.
+ * @param g the <tt>Graphics</tt> object that notified us
+ */
+ protected void paintSafely(Graphics g)
+ {
+ customPaintBackground(g);
+ super.paintSafely(g);
+ }
+
+ /**
+ * Paints the background of the associated component.
+ * @param g the <tt>Graphics</tt> object used for painting
+ */
+ protected void customPaintBackground(Graphics g)
+ {
+ Graphics2D g2 = (Graphics2D) g.create();
+
+ try
+ {
+ AntialiasingManager.activateAntialiasing(g2);
+ super.customPaintBackground(g2);
+ }
+ finally
+ {
+ g2.dispose();
+ }
+ }
+ }
+}
diff --git a/src/net/java/sip/communicator/plugin/keybindingchooser/globalchooser/GlobalShortcutEntry.java b/src/net/java/sip/communicator/plugin/keybindingchooser/globalchooser/GlobalShortcutEntry.java index f68c31f..bcb8510 100644 --- a/src/net/java/sip/communicator/plugin/keybindingchooser/globalchooser/GlobalShortcutEntry.java +++ b/src/net/java/sip/communicator/plugin/keybindingchooser/globalchooser/GlobalShortcutEntry.java @@ -10,6 +10,8 @@ import java.util.List; //disambiguation import java.awt.*; import java.awt.event.*; +import net.java.sip.communicator.service.globalshortcut.*; + /** * Entry for a global shortcut. * @@ -104,6 +106,11 @@ public class GlobalShortcutEntry int keycode = shortcut.getKeyCode(); int modifiers = shortcut.getModifiers(); + if(modifiers == GlobalShortcutService.SPECIAL_KEY_MODIFIERS) + { + return "Special"; + } + // Indicates modifiers of the keystroke boolean shiftMask = (modifiers & InputEvent.SHIFT_MASK) != 0; boolean ctrlMask = (modifiers & InputEvent.CTRL_MASK) != 0; diff --git a/src/net/java/sip/communicator/plugin/keybindingchooser/keybindingChooser.manifest.mf b/src/net/java/sip/communicator/plugin/keybindingchooser/keybindingChooser.manifest.mf index 1bb8e90..85d2bcb 100644 --- a/src/net/java/sip/communicator/plugin/keybindingchooser/keybindingChooser.manifest.mf +++ b/src/net/java/sip/communicator/plugin/keybindingchooser/keybindingChooser.manifest.mf @@ -12,6 +12,9 @@ Import-Package: org.osgi.framework, net.java.sip.communicator.service.globalshortcut, net.java.sip.communicator.util, net.java.sip.communicator.util.swing, + net.java.sip.communicator.util.swing.plaf, + net.java.sip.communicator.util.skin, javax.swing, + javax.swing.plaf, javax.swing.event, javax.swing.table diff --git a/src/net/java/sip/communicator/service/globalshortcut/GlobalShortcutService.java b/src/net/java/sip/communicator/service/globalshortcut/GlobalShortcutService.java index 2753c92..f9f2cf3 100644 --- a/src/net/java/sip/communicator/service/globalshortcut/GlobalShortcutService.java +++ b/src/net/java/sip/communicator/service/globalshortcut/GlobalShortcutService.java @@ -17,6 +17,11 @@ import java.awt.*; public interface GlobalShortcutService { /** + * Value for AWTKeyStroke's modifiers that specify a "special" key shortcut. + */ + public static final int SPECIAL_KEY_MODIFIERS = 16367; + + /** * Registers an action to execute when the keystroke is typed. * * @param l listener to notify when keystroke is typed @@ -35,6 +40,22 @@ public interface GlobalShortcutService AWTKeyStroke keyStroke); /** + * Enable or disable special key detection. + * + * @param enable enable or not special key detection. + * @param callback object to be notified + */ + public void setSpecialKeyDetection(boolean enable, + GlobalShortcutListener callback); + + /** + * Get special keystroke or null if not supported or user cancels. + * + * @return special keystroke or null if not supported or user cancels + */ + public AWTKeyStroke getSpecialKey(); + + /** * Reload global shortcuts. */ public void reloadGlobalShortcuts(); diff --git a/src/net/java/sip/communicator/util/swing/SIPCommDialog.java b/src/net/java/sip/communicator/util/swing/SIPCommDialog.java index 511df08..e1b0128 100644 --- a/src/net/java/sip/communicator/util/swing/SIPCommDialog.java +++ b/src/net/java/sip/communicator/util/swing/SIPCommDialog.java @@ -144,7 +144,7 @@ public class SIPCommDialog amap.put("close", new CloseAction()); imap = this.getRootPane().getInputMap( - JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT); + JComponent.WHEN_IN_FOCUSED_WINDOW); imap.put(KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), "close"); @@ -176,7 +176,7 @@ public class SIPCommDialog /** * Adds a key - action pair for this frame. - * + * * @param keyStroke the key combination * @param action the action which will be executed when user presses the * given key combination @@ -184,9 +184,9 @@ public class SIPCommDialog protected void addKeyBinding(KeyStroke keyStroke, Action action) { String actionID = action.getClass().getName(); - + amap.put(actionID, action); - + imap.put(keyStroke, actionID); } @@ -268,8 +268,8 @@ public class SIPCommDialog } /** - * Checks whether the current component will - * exceeds the screen size and if it do will set a default size + * Checks whether the current component will + * exceeds the screen size and if it do will set a default size */ private void ensureOnScreenLocationAndSize() { @@ -429,6 +429,6 @@ public class SIPCommDialog */ protected void close(boolean isEscaped) { - + } } diff --git a/src/net/java/sip/communicator/util/swing/SIPCommTextField.java b/src/net/java/sip/communicator/util/swing/SIPCommTextField.java index 5ce51d0..151dc7c 100644 --- a/src/net/java/sip/communicator/util/swing/SIPCommTextField.java +++ b/src/net/java/sip/communicator/util/swing/SIPCommTextField.java @@ -29,6 +29,11 @@ public class SIPCommTextField DocumentListener { /** + * Serial version UID. + */ + private static final long serialVersionUID = 0L; + + /** * The default text. */ private String defaultText; |