aboutsummaryrefslogtreecommitdiffstats
path: root/src/net/java/sip/communicator
diff options
context:
space:
mode:
authorSebastien Vincent <seb@jitsi.org>2012-03-06 09:47:38 +0000
committerSebastien Vincent <seb@jitsi.org>2012-03-06 09:47:38 +0000
commit957b8633ae93bc0efbb0b8eff1f40db6cb660485 (patch)
tree190e0f334664dfa71876f46aa7bd87d08469a613 /src/net/java/sip/communicator
parentda7a9769953d75d14f38dbf48c49893e82a3c6cb (diff)
downloadjitsi-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')
-rw-r--r--src/net/java/sip/communicator/impl/globalshortcut/GlobalShortcutServiceImpl.java149
-rw-r--r--src/net/java/sip/communicator/impl/globalshortcut/NativeKeyboardHook.java84
-rw-r--r--src/net/java/sip/communicator/impl/keybindings/KeybindingsServiceImpl.java22
-rw-r--r--src/net/java/sip/communicator/plugin/keybindingchooser/globalchooser/GlobalShortcutConfigForm.java180
-rw-r--r--src/net/java/sip/communicator/plugin/keybindingchooser/globalchooser/GlobalShortcutDialog.java475
-rw-r--r--src/net/java/sip/communicator/plugin/keybindingchooser/globalchooser/GlobalShortcutEntry.java7
-rw-r--r--src/net/java/sip/communicator/plugin/keybindingchooser/keybindingChooser.manifest.mf3
-rw-r--r--src/net/java/sip/communicator/service/globalshortcut/GlobalShortcutService.java21
-rw-r--r--src/net/java/sip/communicator/util/swing/SIPCommDialog.java14
-rw-r--r--src/net/java/sip/communicator/util/swing/SIPCommTextField.java5
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;