Introduces new configuration from to setup global shortcut entry. Allows also to use special key detection for headset button _for MS Windows only_.

cusax-fix
Sebastien Vincent 15 years ago
parent da7a976995
commit 957b8633ae

@ -1122,6 +1122,9 @@ plugin.keybindings.globalchooser.MUTE_CALLS=Mute calls
plugin.keybindings.globalchooser.SHORTCUT_NAME=Name
plugin.keybindings.globalchooser.SHORTCUT_PRIMARY=Primary shortcut
plugin.keybindings.globalchooser.SHORTCUT_SECOND=Second shortcut
plugin.keybindings.globalchooser.PRESS_BTN=Press to set shortcut
plugin.keybindings.globalchooser.PRESS_BTN_DOWN=Waiting
plugin.keybindings.globalchooser.ENABLE_SPECIAL=Enable special key detection
plugin.keybindings.PLUGIN_NAME=Keybindings
plugin.keybindings.CHAT=Chat
plugin.keybindings.MAIN=Main

@ -922,3 +922,31 @@ JNIEXPORT void JNICALL Java_net_java_sip_communicator_impl_globalshortcut_Native
}
}
JNIEXPORT jboolean JNICALL Java_net_java_sip_communicator_impl_globalshortcut_NativeKeyboardHook_registerSpecial
(JNIEnv* jniEnv, jclass clazz, jlong ptr, jint keycode)
{
(void)jniEnv;
(void)clazz;
(void)ptr;
(void)keycode;
return JNI_FALSE;
}
JNIEXPORT void JNICALL Java_net_java_sip_communicator_impl_globalshortcut_NativeKeyboardHook_unregisterSpecial
(JNIEnv* jniEnv, jclass clazz, jlong ptr, jint keycode)
{
(void)jniEnv;
(void)clazz;
(void)ptr;
(void)keycode;
}
JNIEXPORT void JNICALL Java_net_java_sip_communicator_impl_globalshortcut_NativeKeyboardHook_detectSpecialKeyPress
(JNIEnv* jniEnv, jclass clazz, jlong ptr, jboolean enable)
{
(void)jniEnv;
(void)clazz;
(void)ptr;
(void)enable;
}

@ -61,6 +61,8 @@ class keyboard_hook
HWND hwnd; /**< Handle of the window that will receive event. */
int hotkey_next_id; /**< Next ID to use for a new hotkey. */
std::list<keystrok> keystrokes; /**< List of keystrokes registered. */
std::list<int> specials; /**< List of special keystrokes registered. */
bool detect; /**< If we are in detection mode. */
};
/**
@ -653,13 +655,13 @@ static int convertWindowsKeycodeToJava(int keycode)
else if(keycode == VK_PERIOD)
ret = JVK_PERIOD;
else if(keycode == VK_DOLLAR)
ret = JVK_DOLLAR;
else if(keycode == VK_LESS)
ret = JVK_LESS;
else if(keycode == VK_GREATER)
ret = JVK_GREATER;
else if(keycode == VK_SLASH)
ret = JVK_SLASH;
ret = JVK_DOLLAR;
else if(keycode == VK_LESS)
ret = JVK_LESS;
else if(keycode == VK_GREATER)
ret = JVK_GREATER;
else if(keycode == VK_SLASH)
ret = JVK_SLASH;
}
return ret;
}
@ -872,6 +874,39 @@ static void RegisterWindowClassW(HINSTANCE hInstance)
}
}
/**
* \brief Callback handler for low-level keyboard press/release.
* \param nCode code
* \param wParam wParam
* \param lParam lParam
* \return CallNextHookEx return value
*/
LRESULT CALLBACK keyHandler(int nCode, WPARAM wParam, LPARAM lParam)
{
KBDLLHOOKSTRUCT* kbd = (KBDLLHOOKSTRUCT*)lParam;
int pressed = wParam == WM_KEYDOWN;
if(pressed && kbd->vkCode > 160)
{
if(g_keyboard_hook.detect)
{
notify(kbd->vkCode, 16367);
}
else
{
for(std::list<int>::const_iterator it = g_keyboard_hook.specials.begin() ; it != g_keyboard_hook.specials.end() ; ++it)
{
if((*it)== kbd->vkCode)
{
notify((*it), 16367);
break;
}
}
}
}
return CallNextHookEx(g_keyboard_hook.hook, nCode, wParam, lParam);
}
/**
* \brief Thread that create window and that monitor event related to it.
* \param pThreadParam thread parameter
@ -887,6 +922,7 @@ static unsigned WINAPI CreateWndThreadW(LPVOID pThreadParam)
HWND hWnd = CreateWindowW(WINDOW_SHORTCUT_NAME, NULL, WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
NULL, NULL, hInstance, NULL);
keyboard->hook = SetWindowsHookEx(WH_KEYBOARD_LL, keyHandler, NULL, 0);
if(hWnd == NULL)
{
@ -926,6 +962,7 @@ JNIEXPORT void JNICALL Java_net_java_sip_communicator_impl_globalshortcut_Native
HANDLE hThread = NULL;
UINT uThreadId = 0;
keyboard->detect = false;
hThread = (HANDLE)_beginthreadex(NULL, 0, &CreateWndThreadW, keyboard, 0, &uThreadId);
if(!hThread)
{
@ -940,6 +977,7 @@ JNIEXPORT void JNICALL Java_net_java_sip_communicator_impl_globalshortcut_Native
{
keyboard_hook* keyboard = (keyboard_hook*)ptr;
UnhookWindowsHookEx(keyboard->hook);
keyboard->hook = 0;
keyboard->running = 0;
}
@ -1017,3 +1055,50 @@ JNIEXPORT void JNICALL Java_net_java_sip_communicator_impl_globalshortcut_Native
}
}
JNIEXPORT jboolean JNICALL Java_net_java_sip_communicator_impl_globalshortcut_NativeKeyboardHook_registerSpecial
(JNIEnv* jniEnv, jclass clazz, jlong ptr, jint keycode)
{
struct keyboard_hook* keyboard = (struct keyboard_hook*)ptr;
if(keyboard && keyboard->hook != NULL)
{
keyboard->specials.push_back((int)keycode);
return JNI_TRUE;
}
return JNI_FALSE;
}
JNIEXPORT void JNICALL Java_net_java_sip_communicator_impl_globalshortcut_NativeKeyboardHook_unregisterSpecial
(JNIEnv* jniEnv, jclass clazz, jlong ptr, jint keycode)
{
struct keyboard_hook* keyboard = (struct keyboard_hook*)ptr;
if(keyboard)
{
for(std::list<int>::iterator it = keyboard->specials.begin() ;
it != keyboard->specials.end() ; ++it)
{
if((*it) == keycode)
{
keyboard->specials.erase(it);
return;
}
}
}
}
/*
* Class: net_java_sip_communicator_impl_globalshortcut_NativeKeyboardHook
* Method: detectSpecialKeyPress
* Signature: (JZ)V
*/
JNIEXPORT void JNICALL Java_net_java_sip_communicator_impl_globalshortcut_NativeKeyboardHook_detectSpecialKeyPress
(JNIEnv* jniEnv, jclass clazz, jlong ptr, jboolean enable)
{
struct keyboard_hook* keyboard = (struct keyboard_hook*)ptr;
if(keyboard)
{
keyboard->detect = (enable == JNI_TRUE) ? true : false;
}
}

@ -55,6 +55,30 @@ JNIEXPORT jboolean JNICALL Java_net_java_sip_communicator_impl_globalshortcut_Na
JNIEXPORT void JNICALL Java_net_java_sip_communicator_impl_globalshortcut_NativeKeyboardHook_unregisterShortcut
(JNIEnv *, jclass, jlong, jint, jint);
/*
* Class: net_java_sip_communicator_impl_globalshortcut_NativeKeyboardHook
* Method: registerSpecial
* Signature: (JI)Z
*/
JNIEXPORT jboolean JNICALL Java_net_java_sip_communicator_impl_globalshortcut_NativeKeyboardHook_registerSpecial
(JNIEnv *, jclass, jlong, jint);
/*
* Class: net_java_sip_communicator_impl_globalshortcut_NativeKeyboardHook
* Method: unregisterSpecial
* Signature: (JI)V
*/
JNIEXPORT void JNICALL Java_net_java_sip_communicator_impl_globalshortcut_NativeKeyboardHook_unregisterSpecial
(JNIEnv *, jclass, jlong, jint);
/*
* Class: net_java_sip_communicator_impl_globalshortcut_NativeKeyboardHook
* Method: detectSpecialKeyPress
* Signature: (JZ)V
*/
JNIEXPORT void JNICALL Java_net_java_sip_communicator_impl_globalshortcut_NativeKeyboardHook_detectSpecialKeyPress
(JNIEnv *, jclass, jlong, jboolean);
#ifdef __cplusplus
}
#endif

@ -938,3 +938,31 @@ JNIEXPORT void JNICALL Java_net_java_sip_communicator_impl_globalshortcut_Native
}
}
JNIEXPORT jboolean JNICALL Java_net_java_sip_communicator_impl_globalshortcut_NativeKeyboardHook_registerSpecial
(JNIEnv* jniEnv, jclass clazz, jlong ptr, jint keycode)
{
(void)jniEnv;
(void)clazz;
(void)ptr;
(void)keycode;
return JNI_FALSE;
}
JNIEXPORT void JNICALL Java_net_java_sip_communicator_impl_globalshortcut_NativeKeyboardHook_unregisterSpecial
(JNIEnv* jniEnv, jclass clazz, jlong ptr, jint keycode)
{
(void)jniEnv;
(void)clazz;
(void)ptr;
(void)keycode;
}
JNIEXPORT void JNICALL Java_net_java_sip_communicator_impl_globalshortcut_NativeKeyboardHook_detectSpecialKeyPress
(JNIEnv* jniEnv, jclass clazz, jlong ptr, jboolean enable)
{
(void)jniEnv;
(void)clazz;
(void)ptr;
(void)enable;
}

@ -38,6 +38,11 @@ public class GlobalShortcutServiceImpl
private final Map<GlobalShortcutListener, List<AWTKeyStroke>> mapActions =
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.
*/
@ -58,6 +63,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>.
*/
@ -90,6 +105,7 @@ public void registerShortcut(GlobalShortcutListener listener,
synchronized(mapActions)
{
List<AWTKeyStroke> keystrokes = mapActions.get(listener);
boolean ok = false;
if(keyStroke == null)
{
@ -98,21 +114,38 @@ public void registerShortcut(GlobalShortcutListener listener,
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 void unregisterShortcut(GlobalShortcutListener listener,
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 void stop()
*/
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 synchronized void receiveKey(int keycode, int modifiers)
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);
@ -381,6 +447,67 @@ public void setEnable(boolean enable)
}
}
/**
* 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.
*/

@ -34,6 +34,11 @@ public class NativeKeyboardHook
*/
private static long ptr = 0;
/**
* If the special key detection is enable.
*/
private boolean specialKeydetection = false;
/**
* Constructor.
*/
@ -105,6 +110,57 @@ public synchronized void unregisterShortcut(int keycode,
unregisterShortcut(ptr, keycode, modifiers);
}
/**
* 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>.
*
@ -141,6 +197,7 @@ private static native void setDelegate(long ptr,
* @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 @@ private static native boolean registerShortcut(long ptr, int keycode,
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

@ -183,16 +183,16 @@ synchronized void start(BundleContext bc)
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 @@ public Map<String, List<AWTKeyStroke>> getGlobalShortcutFromConfiguration()
{
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 @@ public void saveGlobalShortcutFromConfiguration()
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);
}
}

@ -61,16 +61,6 @@ public class GlobalShortcutConfigForm
private GlobalShortcutTableModel tableModel =
new GlobalShortcutTableModel();
/**
* Current selected row.
*/
private int currentRow = -1;
/**
* Current selected row.
*/
private int currentColumn = -1;
/**
* Constructor
*/
@ -97,152 +87,76 @@ private void initComponents()
@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);
}
}
}

@ -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();
}
}
}
}

@ -10,6 +10,8 @@
import java.awt.*;
import java.awt.event.*;
import net.java.sip.communicator.service.globalshortcut.*;
/**
* Entry for a global shortcut.
*
@ -104,6 +106,11 @@ public static String getShortcutText(AWTKeyStroke shortcut)
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;

@ -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

@ -16,6 +16,11 @@
*/
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.
*
@ -34,6 +39,22 @@ public void registerShortcut(GlobalShortcutListener l,
public void unregisterShortcut(GlobalShortcutListener l,
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.
*/

@ -144,7 +144,7 @@ private void initInputMap()
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 void actionPerformed(ActionEvent e)
/**
* 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 void actionPerformed(ActionEvent e)
protected void addKeyBinding(KeyStroke keyStroke, Action action)
{
String actionID = action.getClass().getName();
amap.put(actionID, action);
imap.put(keyStroke, actionID);
}
@ -268,8 +268,8 @@ private void setCenterLocation()
}
/**
* 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 void dispose()
*/
protected void close(boolean isEscaped)
{
}
}

@ -28,6 +28,11 @@ public class SIPCommTextField
KeyListener,
DocumentListener
{
/**
* Serial version UID.
*/
private static final long serialVersionUID = 0L;
/**
* The default text.
*/

Loading…
Cancel
Save