Adds global shortcut support.

cusax-fix
Sebastien Vincent 14 years ago
parent 2b1ed218df
commit 6ea38de334

@ -932,7 +932,8 @@
bundle-plugin-loggingutils,bundle-plugin-dnsconfig,
bundle-provdisc,bundle-provdisc-dhcp,bundle-provdisc-mdns,
bundle-provisioning,bundle-addrbook,bundle-plugin-ldap,
bundle-plugin-contactsourceconfig,bundle-plugin-certconfig"/>
bundle-plugin-contactsourceconfig,bundle-plugin-certconfig,
bundle-globalshortcut"/>
<!--BUNDLE-SC-LAUNCHER-->
<target name="bundle-sc-launcher">
@ -2753,4 +2754,14 @@ javax.swing.event, javax.swing.border"/>
prefix="net/java/sip/communicator/plugin/certconfig" />
</jar>
</target>
<target name="bundle-globalshortcut">
<jar compress="false" destfile="${bundles.dest}/globalshortcut.jar"
manifest="${src}/net/java/sip/communicator/impl/globalshortcut/globalshortcut.manifest.mf">
<zipfileset dir="${dest}/net/java/sip/communicator/impl/globalshortcut"
prefix="net/java/sip/communicator/impl/globalshortcut" />
<zipfileset dir="${dest}/net/java/sip/communicator/service/globalshortcut"
prefix="net/java/sip/communicator/service/globalshortcut"/>
</jar>
</target>
</project>

@ -120,6 +120,7 @@ felix.auto.start.66= \
reference:file:sc-bundles/replacement.jar
felix.auto.start.67= \
reference:file:sc-bundles/globalshortcut.jar \
reference:file:sc-bundles/pluginmanager.jar \
reference:file:sc-bundles/skinmanager.jar \
reference:file:sc-bundles/icqaccregwizz.jar \

@ -1067,6 +1067,12 @@ plugin.keybindings.MAIN_PREVIOUS_TAB=Previous tab
plugin.keybindings.MAIN_RENAME=Rename contact
plugin.keybindings.OPEN_HISTORY=Show History
plugin.keybindings.OPEN_SMILIES=Show Smileys
plugin.keybindings.globalchooser.ANSWER_CALL=Answer call
plugin.keybindings.globalchooser.HANGUP_CALL=Hangup call
plugin.keybindings.globalchooser.SHOW_CONTACTLIST=Show contact list
plugin.keybindings.globalchooser.SHORTCUT_NAME=Name
plugin.keybindings.globalchooser.SHORTCUT_PRIMARY=Primary shortcut
plugin.keybindings.globalchooser.SHORTCUT_SECOND=Second shortcut
plugin.keybindings.PLUGIN_NAME=Keybindings
# Notification Configuration Form

@ -895,6 +895,92 @@
</cc>
</target>
<target name="globalshortcut" description="Build globalshortcut"
depends="init-native,globalshortcut-windows" unless="is.running.windows">
<cc outtype="shared" name="gcc" outfile="${native_install_dir}/globalshortcut"
objdir="${obj}">
<!-- common compiler flags -->
<compilerarg value="-Wall" />
<compilerarg value="-Wextra" />
<compilerarg value="-m32" if="cross_32" unless="is.running.macos" />
<compilerarg value="-m64" if="cross_64" unless="is.running.macos" />
<linkerarg value="-m32" if="cross_32" unless="is.running.macos" />
<linkerarg value="-m64" if="cross_64" unless="is.running.macos" />
<!-- Mac OS X specific flags -->
<compilerarg value="-mmacosx-version-min=10.5" if="is.running.macos"/>
<compilerarg value="-O2" if="is.running.macos"/>
<compilerarg value="-arch" if="is.running.macos"/>
<compilerarg value="x86_64" if="is.running.macos"/>
<compilerarg value="-arch" if="is.running.macos"/>
<compilerarg value="i386" if="is.running.macos"/>
<compilerarg value="-arch" if="is.running.macos"/>
<compilerarg value="ppc" if="is.running.macos"/>
<compilerarg value="-I/System/Library/Frameworks/JavaVM.framework/Headers"
if="is.running.macos"/>
<linkerarg value="-o" location="end" if="is.running.macos"/>
<linkerarg value="libglobalshortcut.jnilib" location="end"
if="is.running.macos"/>
<linkerarg value="-arch" if="is.running.macos"/>
<linkerarg value="x86_64" if="is.running.macos"/>
<linkerarg value="-arch" if="is.running.macos"/>
<linkerarg value="i386" if="is.running.macos"/>
<linkerarg value="-arch" if="is.running.macos"/>
<linkerarg value="ppc" if="is.running.macos"/>
<linkerarg value="-framework" if="is.running.macos"/>
<linkerarg value="Foundation" if="is.running.macos"/>
<linkerarg value="-framework" if="is.running.macos"/>
<linkerarg value="Carbon" if="is.running.macos"/>
<linkerarg value="-framework" if="is.running.macos"/>
<linkerarg value="Cocoa" if="is.running.macos"/>
<fileset dir="${src}/native/globalshortcut"
includes="*.m" if="is.running.macos"/>
<!-- Linux specific flags -->
<fileset dir="${src}/native/globalshortcut" includes="*.cc" if="is.running.linux" />
<compilerarg value="-m32" if="cross_32" unless="is.running.macos" />
<compilerarg value="-m64" if="cross_64" unless="is.running.macos" />
<compilerarg value="-I${system.JAVA_HOME}/include" if="is.running.linux" />
<compilerarg value="-I${system.JAVA_HOME}/include/linux" if="is.running.linux" />
<linkerarg value="-m32" if="cross_32" unless="is.running.macos" />
<linkerarg value="-m64" if="cross_64" unless="is.running.macos" />
<linkerarg value="-lX11" if="is.running.linux" />
<linkerarg value="-lstdc++" if="is.running.linux" />
</cc>
</target>
<!-- compile globalshortcut library for Windows
"C:\Program Files\Microsoft SDKs\Windows\v7.1\Bin\SetEnv.Cmd" /x86 /xp /Release
-->
<target name="globalshortcut-windows"
description="Build globalshortcut shared library for Windows"
if="is.running.windows"
depends="init-native">
<cc outtype="shared" name="msvc"
outfile="${native_install_dir}/globalshortcut"
objdir="${obj}">
<compilerarg value="/O2" />
<compilerarg value="/GS" if="cross_64" />
<compilerarg value="/EHsc" />
<compilerarg value="/MT" location="end" />
<compilerarg value="-I${system.JAVA_HOME}/include" />
<compilerarg value="-I${system.JAVA_HOME}/include/win32" />
<linkerarg value="/LIBPATH:${system.JAVA_HOME}\\lib" />
<linkerarg value="iphlpapi.lib" location="end" />
<linkerarg value="user32.lib" location="end" />
<linkerarg value="jawt.lib" location="end" />
<fileset dir="${src}/native/globalshortcut" includes="*.cpp" />
</cc>
</target>
<!-- Cleanup object file and shared libraries -->
<target name="clean-native" description="Clean all object file and libraries.">
<delete failonerror="false" includeemptydirs="true">

@ -0,0 +1,86 @@
/*
DDHotKey -- DDHotKeyCenter.h
Copyright (c) 2010, Dave DeLong <http://www.davedelong.com>
Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies.
The software is provided "as is", without warranty of any kind, including all implied warranties of merchantability and fitness. In no event shall the author(s) or copyright holder(s) be liable for any claim, damages, or other liability, whether in an action of contract, tort, or otherwise, arising from, out of, or in connection with the software or the use or other dealings in the software.
*/
#import <Cocoa/Cocoa.h>
#if NS_BLOCKS_AVAILABLE
//a convenient typedef for the required signature of a hotkey block callback
typedef void (^DDHotKeyTask)(NSEvent*);
#endif
@interface DDHotKey : NSObject
@property (nonatomic, readonly, retain) id target;
@property (nonatomic, readonly) SEL action;
@property (nonatomic, readonly, retain) id object;
#if NS_BLOCKS_AVAILABLE
@property (nonatomic, readonly, copy) DDHotKeyTask task;
#endif
@property (nonatomic, readonly) unsigned short keyCode;
@property (nonatomic, readonly) NSUInteger modifierFlags;
@end
#pragma mark -
@interface DDHotKeyCenter : NSObject {
}
/**
Register a target/action hotkey.
The modifierFlags must be a bitwise OR of NSCommandKeyMask, NSAlternateKeyMask, NSControlKeyMask, or NSShiftKeyMask;
Returns YES if the hotkey was registered; NO otherwise.
*/
- (BOOL) registerHotKeyWithKeyCode:(unsigned short)keyCode modifierFlags:(NSUInteger)flags target:(id)target action:(SEL)action object:(id)object;
#if NS_BLOCKS_AVAILABLE
/**
Register a block callback hotkey.
The modifierFlags must be a bitwise OR of NSCommandKeyMask, NSAlternateKeyMask, NSControlKeyMask, or NSShiftKeyMask;
Returns YES if the hotkey was registered; NO otherwise.
*/
- (BOOL) registerHotKeyWithKeyCode:(unsigned short)keyCode modifierFlags:(NSUInteger)flags task:(DDHotKeyTask)task;
#endif
/**
See if a hotkey exists with the specified keycode and modifier flags.
NOTE: this will only check among hotkeys you have explicitly registered with DDHotKeyCenter. This does not check all globally registered hotkeys.
*/
- (BOOL) hasRegisteredHotKeyWithKeyCode:(unsigned short)keyCode modifierFlags:(NSUInteger)flags;
/**
Unregister a specific hotkey
*/
- (void) unregisterHotKey:(DDHotKey *)hotKey;
/**
Unregister all hotkeys with a specific target
*/
- (void) unregisterHotKeysWithTarget:(id)target;
/**
Unregister all hotkeys with a specific target and action
*/
- (void) unregisterHotKeysWithTarget:(id)target action:(SEL)action;
/**
Unregister a hotkey with a specific keycode and modifier flags
*/
- (void) unregisterHotKeyWithKeyCode:(unsigned short)keyCode modifierFlags:(NSUInteger)flags;
/**
Returns a set of currently registered hotkeys
**/
- (NSSet *) registeredHotKeys;
@end

@ -0,0 +1,313 @@
/*
DDHotKey -- DDHotKeyCenter.m
Copyright (c) 2010, Dave DeLong <http://www.davedelong.com>
Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies.
The software is provided "as is", without warranty of any kind, including all implied warranties of merchantability and fitness. In no event shall the author(s) or copyright holder(s) be liable for any claim, damages, or other liability, whether in an action of contract, tort, or otherwise, arising from, out of, or in connection with the software or the use or other dealings in the software.
*/
#import "DDHotKeyCenter.h"
#import <Carbon/Carbon.h>
#import <objc/runtime.h>
#pragma mark Private Global Declarations
static NSMutableSet * _registeredHotKeys = nil;
static UInt32 _nextHotKeyID = 1;
OSStatus dd_hotKeyHandler(EventHandlerCallRef nextHandler, EventRef theEvent, void * userData);
UInt32 dd_translateModifierFlags(NSUInteger flags);
NSString* dd_stringifyModifierFlags(NSUInteger flags);
#pragma mark DDHotKey
@implementation DDHotKey
- (id) target { return nil; }
- (SEL) action { return nil; }
- (id) object { return nil; }
- (unsigned short) keyCode { return 0; }
- (NSUInteger) modifierFlags { return 0; }
#if NS_BLOCKS_AVAILABLE
- (DDHotKeyTask) task { return nil; }
#endif
- (NSUInteger) hash {
return [self keyCode] + [self modifierFlags];
}
- (BOOL) isEqual:(id)object {
BOOL equal = NO;
if ([object isKindOfClass:[DDHotKey class]]) {
equal = ([object keyCode] == [self keyCode]);
equal &= ([object modifierFlags] == [self modifierFlags]);
}
return equal;
}
- (NSString *) description {
NSString * flags = dd_stringifyModifierFlags([self modifierFlags]);
NSString * invokes = @"(block)";
if ([self target] != nil && [self action] != nil) {
invokes = [NSString stringWithFormat:@"[%@ %@]", [self target], NSStringFromSelector([self action])];
}
return [NSString stringWithFormat:@"%@\n\t(key: %hu\n\tflags: %@\n\tinvokes: %@)", [super description], [self keyCode], flags, invokes];
}
@end
@interface _DDHotKey : DDHotKey {
@private
id target;
SEL action;
id object;
#if NS_BLOCKS_AVAILABLE
DDHotKeyTask task;
#endif
unsigned short keyCode;
NSUInteger modifierFlags;
UInt32 hotKeyID;
NSValue * hotKeyRef;
}
@property (nonatomic, retain) id target;
@property (nonatomic) SEL action;
@property (nonatomic, retain) id object;
@property (nonatomic) unsigned short keyCode;
@property (nonatomic) NSUInteger modifierFlags;
@property (nonatomic) UInt32 hotKeyID;
@property (nonatomic, retain) NSValue * hotKeyRef;
#if NS_BLOCKS_AVAILABLE
@property (nonatomic, copy) DDHotKeyTask task;
#endif
- (void) invokeWithEvent:(NSEvent *)event;
- (BOOL) registerHotKey;
- (void) unregisterHotKey;
@end
@implementation _DDHotKey
@synthesize target, action, object, keyCode, modifierFlags, hotKeyID, hotKeyRef;
#if NS_BLOCKS_AVAILABLE
@synthesize task;
#endif
- (Class) class { return [DDHotKey class]; }
- (void) invokeWithEvent:(NSEvent *)event {
if (target != nil && action != nil && [target respondsToSelector:action]) {
[target performSelector:action withObject:event withObject:object];
}
#if NS_BLOCKS_AVAILABLE
else if (task != nil) {
task(event);
}
#endif
}
- (NSString *) actionString {
return NSStringFromSelector(action);
}
- (BOOL) registerHotKey {
EventHotKeyID keyID;
keyID.signature = 'htk1';
keyID.id = _nextHotKeyID;
EventHotKeyRef carbonHotKey;
UInt32 flags = dd_translateModifierFlags(modifierFlags);
OSStatus err = RegisterEventHotKey(keyCode, flags, keyID, GetEventDispatcherTarget(), 0, &carbonHotKey);
//error registering hot key
if (err != 0) { return NO; }
NSValue * refValue = [NSValue valueWithPointer:carbonHotKey];
[self setHotKeyRef:refValue];
[self setHotKeyID:_nextHotKeyID];
_nextHotKeyID++;
return YES;
}
- (void) unregisterHotKey {
EventHotKeyRef carbonHotKey = (EventHotKeyRef)[hotKeyRef pointerValue];
UnregisterEventHotKey(carbonHotKey);
[self setHotKeyRef:nil];
}
- (void) dealloc {
[target release], target = nil;
[object release], object = nil;
if (hotKeyRef != nil) {
[self unregisterHotKey];
[hotKeyRef release], hotKeyRef = nil;
}
[super dealloc];
}
@end
#pragma mark DDHotKeyCenter
@implementation DDHotKeyCenter
+ (void) initialize {
if (self == [DDHotKeyCenter class] && _registeredHotKeys == nil) {
_registeredHotKeys = [[NSMutableSet alloc] init];
_nextHotKeyID = 1;
EventTypeSpec eventSpec;
eventSpec.eventClass = kEventClassKeyboard;
eventSpec.eventKind = kEventHotKeyReleased;
InstallApplicationEventHandler(&dd_hotKeyHandler, 1, &eventSpec, NULL, NULL);
}
}
- (NSSet *) hotKeysMatchingPredicate:(NSPredicate *)predicate {
return [_registeredHotKeys filteredSetUsingPredicate:predicate];
}
- (BOOL) hasRegisteredHotKeyWithKeyCode:(unsigned short)keyCode modifierFlags:(NSUInteger)flags {
NSPredicate * predicate = [NSPredicate predicateWithFormat:@"keyCode = %hu AND modifierFlags = %lu", keyCode, flags];
return ([[self hotKeysMatchingPredicate:predicate] count] > 0);
}
#if NS_BLOCKS_AVAILABLE
- (BOOL) registerHotKeyWithKeyCode:(unsigned short)keyCode modifierFlags:(NSUInteger)flags task:(DDHotKeyTask)task {
//we can't add a new hotkey if something already has this combo
if ([self hasRegisteredHotKeyWithKeyCode:keyCode modifierFlags:flags]) { return NO; }
_DDHotKey * newHotKey = [[_DDHotKey alloc] init];
[newHotKey setTask:task];
[newHotKey setKeyCode:keyCode];
[newHotKey setModifierFlags:flags];
BOOL success = [newHotKey registerHotKey];
if (success) {
[_registeredHotKeys addObject:newHotKey];
}
[newHotKey release];
return success;
}
#endif
- (BOOL) registerHotKeyWithKeyCode:(unsigned short)keyCode modifierFlags:(NSUInteger)flags target:(id)target action:(SEL)action object:(id)object {
//we can't add a new hotkey if something already has this combo
if ([self hasRegisteredHotKeyWithKeyCode:keyCode modifierFlags:flags]) { return NO; }
//build the hotkey object:
_DDHotKey * newHotKey = [[_DDHotKey alloc] init];
[newHotKey setTarget:target];
[newHotKey setAction:action];
[newHotKey setObject:object];
[newHotKey setKeyCode:keyCode];
[newHotKey setModifierFlags:flags];
BOOL success = [newHotKey registerHotKey];
if (success) {
[_registeredHotKeys addObject:newHotKey];
}
[newHotKey release];
return success;
}
- (void) unregisterHotKeysMatchingPredicate:(NSPredicate *)predicate {
//explicitly unregister the hotkey, since relying on the unregistration in -dealloc can be problematic
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
NSSet * matches = [self hotKeysMatchingPredicate:predicate];
[_registeredHotKeys minusSet:matches];
[matches makeObjectsPerformSelector:@selector(unregisterHotKey)];
[pool release];
}
- (void) unregisterHotKey:(DDHotKey *)hotKey {
if (object_getClass(hotKey) == [_DDHotKey class]) {
_DDHotKey * key = (_DDHotKey *)hotKey;
[_registeredHotKeys removeObject:key];
[key unregisterHotKey];
} else {
[NSException raise:NSInvalidArgumentException format:@"Invalid hotkey"];
}
}
- (void) unregisterHotKeysWithTarget:(id)target {
NSPredicate * predicate = [NSPredicate predicateWithFormat:@"target = %@", target];
[self unregisterHotKeysMatchingPredicate:predicate];
}
- (void) unregisterHotKeysWithTarget:(id)target action:(SEL)action {
NSPredicate * predicate = [NSPredicate predicateWithFormat:@"target = %@ AND actionString = %@", target, NSStringFromSelector(action)];
[self unregisterHotKeysMatchingPredicate:predicate];
}
- (void) unregisterHotKeyWithKeyCode:(unsigned short)keyCode modifierFlags:(NSUInteger)flags {
NSPredicate * predicate = [NSPredicate predicateWithFormat:@"keyCode = %hu AND modifierFlags = %lu", keyCode, flags];
[self unregisterHotKeysMatchingPredicate:predicate];
}
- (NSSet *) registeredHotKeys {
return [self hotKeysMatchingPredicate:[NSPredicate predicateWithFormat:@"hotKeyRef != NULL"]];
}
@end
OSStatus dd_hotKeyHandler(EventHandlerCallRef nextHandler, EventRef theEvent, void * userData) {
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
EventHotKeyID hotKeyID;
GetEventParameter(theEvent, kEventParamDirectObject, typeEventHotKeyID, NULL, sizeof(hotKeyID),NULL,&hotKeyID);
UInt32 keyID = hotKeyID.id;
NSSet * matchingHotKeys = [_registeredHotKeys filteredSetUsingPredicate:[NSPredicate predicateWithFormat:@"hotKeyID = %u", keyID]];
if ([matchingHotKeys count] > 1) { NSLog(@"ERROR!"); }
_DDHotKey * matchingHotKey = [matchingHotKeys anyObject];
NSEvent * event = [NSEvent eventWithEventRef:theEvent];
NSEvent * keyEvent = [NSEvent keyEventWithType:NSKeyUp
location:[event locationInWindow]
modifierFlags:[event modifierFlags]
timestamp:[event timestamp]
windowNumber:-1
context:nil
characters:@""
charactersIgnoringModifiers:@""
isARepeat:NO
keyCode:[matchingHotKey keyCode]];
[matchingHotKey invokeWithEvent:keyEvent];
[pool release];
return noErr;
}
UInt32 dd_translateModifierFlags(NSUInteger flags) {
UInt32 newFlags = 0;
if ((flags & NSControlKeyMask) > 0) { newFlags |= controlKey; }
if ((flags & NSCommandKeyMask) > 0) { newFlags |= cmdKey; }
if ((flags & NSShiftKeyMask) > 0) { newFlags |= shiftKey; }
if ((flags & NSAlternateKeyMask) > 0) { newFlags |= optionKey; }
return newFlags;
}
NSString* dd_stringifyModifierFlags(NSUInteger flags) {
NSMutableArray * bits = [NSMutableArray array];
if ((flags & NSControlKeyMask) > 0) { [bits addObject:@"NSControlKeyMask"]; }
if ((flags & NSCommandKeyMask) > 0) { [bits addObject:@"NSCommandKeyMask"]; }
if ((flags & NSShiftKeyMask) > 0) { [bits addObject:@"NSShiftKeyMask"]; }
if ((flags & NSAlternateKeyMask) > 0) { [bits addObject:@"NSAlternateKeyMask"]; }
if ([bits count] > 0) {
return [NSString stringWithFormat:@"(%@)", [bits componentsJoinedByString:@" | "]];
}
return @"ERROR: No valid flags";
}

@ -0,0 +1,163 @@
/*
* Jitsi Communicator, the OpenSource Java VoIP and Instant Messaging client.
*
* Distributable under LGPL license.
* See terms of license at gnu.org.
*/
#ifndef JAVAKEY_H
#define JAVAKEY_H
/**
* \enum java_keycode
* \brief Java keycode
*/
enum java_keycode
{
JVK_PAUSE = 0x13,
JVK_CAPS_LOCK = 0x14,
JVK_ESCAPE = 0x1B,
JVK_SPACE =0x20,
JVK_UP = 0x26,
JVK_DOWN = 0x28,
JVK_LEFT = 0x25,
JVK_RIGHT = 0x27,
JVK_COMMA = 0x2C,
JVK_MINUS = 0x2D,
JVK_PERIOD = 0x2E,
JVK_SLASH = 0x2F,
JVK_0 = 0x30,
JVK_1 = 0x31,
JVK_2 = 0x32,
JVK_3 = 0x33,
JVK_4 = 0x34,
JVK_5 = 0x35,
JVK_6 = 0x36,
JVK_7 = 0x37,
JVK_8 = 0x38,
JVK_9 = 0x39,
JVK_SEMICOLON = 0x3B,
JVK_EQUALS = 0x3D,
JVK_A = 0x41,
JVK_B = 0x42,
JVK_C = 0x43,
JVK_D = 0x44,
JVK_E = 0x45,
JVK_F = 0x46,
JVK_G = 0x47,
JVK_H = 0x48,
JVK_I = 0x49,
JVK_J = 0x4A,
JVK_K = 0x4B,
JVK_L = 0x4C,
JVK_M = 0x4D,
JVK_N = 0x4E,
JVK_O = 0x4F,
JVK_P = 0x50,
JVK_Q = 0x51,
JVK_R = 0x52,
JVK_S = 0x53,
JVK_T = 0x54,
JVK_U = 0x55,
JVK_V = 0x56,
JVK_W = 0x57,
JVK_X = 0x58,
JVK_Y = 0x59,
JVK_Z = 0x5A,
JVK_OPEN_BRACKET = 0x5B,
JVK_BACK_SLASH = 0x5C,
JVK_CLOSE_BRACKET = 0x5D,
JVK_NUMPAD0 = 0x60,
JVK_NUMPAD1 = 0x61,
JVK_NUMPAD2 = 0x62,
JVK_NUMPAD3 = 0x63,
JVK_NUMPAD4 = 0x64,
JVK_NUMPAD5 = 0x65,
JVK_NUMPAD6 = 0x66,
JVK_NUMPAD7 = 0x67,
JVK_NUMPAD8 = 0x68,
JVK_NUMPAD9 = 0x69,
JVK_KP_UP = 0xE0,
JVK_KP_DOWN = 0xE1,
JVK_KP_LEFT = 0xE2,
JVK_KP_RIGHT = 0xE3,
JVK_MULTIPLY = 0x6A,
JVK_ADD = 0x6B,
JVK_SEPARATOR = 0x6C,
JVK_SUBTRACT = 0x6D,
JVK_DECIMAL = 0x6E,
JVK_DIVIDE = 0x6F,
JVK_DELETE = 0x7F,
JVK_NUM_LOCK = 0x90,
JVK_CLEAR = 0x03,
JVK_SCROLL_LOCK = 0x91,
JVK_F1 = 0x70,
JVK_F2 = 0x71,
JVK_F3 = 0x72,
JVK_F4 = 0x73,
JVK_F5 = 0x74,
JVK_F6= 0x75,
JVK_F7 = 0x76,
JVK_F8 = 0x77,
JVK_F9 = 0x78,
JVK_F10 = 0x79,
JVK_F11 = 0x7A,
JVK_F12 = 0x7B,
JVK_F13 = 0xF000,
JVK_F14 = 0xF001,
JVK_F15 = 0xF002,
JVK_F16 = 0xF003,
JVK_F17 = 0xF004,
JVK_F18 = 0xF005,
JVK_F19 = 0xF006,
JVK_F20 = 0xF007,
JVK_F21 = 0xF008,
JVK_F22 = 0xF009,
JVK_F23 = 0xF00A,
JVK_F24 = 0xF00B,
JVK_PRINTSCREEN = 0x9A,
JVK_INSERT = 0x9B,
JVK_HELP = 0x9C,
JVK_PAGE_UP = 0x21,
JVK_PAGE_DOWN = 0x22,
JVK_HOME = 0x24,
JVK_END = 0x23,
JVK_BACK_QUOTE = 0xC0,
JVK_QUOTE = 0xDE,
JVK_DEAD_GRAVE = 0x80,
JVK_DEAD_ACUTE = 0x81,
JVK_DEAD_CIRCUMFLEX = 0x82,
JVK_DEAD_TILDE = 0x83,
JVK_DEAD_MACRON = 0x84,
JVK_DEAD_BREVE = 0x85,
JVK_DEAD_ABOVEDOT = 0x86,
JVK_DEAD_DIAERESIS = 0x87,
JVK_DEAD_ABOVERING = 0x88,
JVK_DEAD_DOUBLEACUTE = 0x89,
JVK_DEAD_CARON = 0x8A,
JVK_DEAD_CEDILLA = 0x8B,
JVK_DEAD_OGONEK = 0x8C,
JVK_DEAD_IOTA = 0x8D,
JVK_AMPERSAND = 0x96,
JVK_ASTERISK = 0x97,
JVK_QUOTEDBL = 0x98,
JVK_LESS = 0x99,
JVK_GREATER = 0xA0,
JVK_BRACELEFT = 0xA1,
JVK_BRACERIGHT = 0xA2,
JVK_AT = 0x0200,
JVK_COLON = 0x0201,
JVK_CIRCUMFLEX = 0x0202,
JVK_DOLLAR = 0x0203,
JVK_EURO_SIGN = 0x0204,
JVK_EXCLAMATION_MARK = 0x0205,
JVK_INVERTED_EXCLAMATION_MARK = 0x0206,
JVK_LEFT_PARENTHESIS = 0x0207,
JVK_NUMBER_SIGN = 0x0208,
JVK_PLUS = 0x0209,
JVK_RIGHT_PARENTHESIS = 0x020A,
JVK_UNDERSCORE = 0x020B,
};
#endif /* JAVAKEY_H */

@ -0,0 +1,924 @@
/*
* Jitsi Communicator, the OpenSource Java VoIP and Instant Messaging client.
*
* Distributable under LGPL license.
* See terms of license at gnu.org.
*/
#include "net_java_sip_communicator_impl_globalshortcut_NativeKeyboardHook.h"
/* Linux specific code */
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <list>
#include <unistd.h>
#include <pthread.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/keysymdef.h>
#include "javakey.h"
/**
* \class keystrok
* \brief keystrok class.
*/
class keystrok
{
public:
int vkcode; /**< Virtual keycode. */
int modifiers; /**< Modifiers (ALT, CTLR, ...). */
int active; /**< If the hotkey is active. */
};
/**
* \class keyboard_hook
* \brief keyboard_hook structure.
*/
class keyboard_hook
{
public:
Display* display; /**< X11 display. */
Window root; /**< X11 root window. */
jobject delegate; /**< Java object delegate. */
JavaVM* jvm; /**< Java VM. */
volatile int running; /**< Running state. */
std::list<keystrok> keystrokes; /**< List of keystrokes registered. */
};
/**
* \brief Convert Java keycode to native ones.
* \param keycode Java keycode
* \return native X11 keycode
*/
static int convertJavaKeycodeToX11(int keycode)
{
int ret = -1;
switch(keycode)
{
/* 0 - 9 */
case JVK_0:
ret = XK_0;
break;
case JVK_1:
ret = XK_1;
break;
case JVK_2:
ret = XK_2;
break;
case JVK_3:
ret = XK_3;
break;
case JVK_4:
ret = XK_4;
break;
case JVK_5:
ret = XK_5;
break;
case JVK_6:
ret = XK_6;
break;
case JVK_7:
ret = XK_7;
break;
case JVK_8:
ret = XK_8;
break;
case JVK_9:
ret = XK_9;
break;
/* A - Z */
case JVK_A:
ret = XK_A;
break;
case JVK_B:
ret = XK_B;
break;
case JVK_C:
ret = XK_C;
break;
case JVK_D:
ret = XK_D;
break;
case JVK_E:
ret = XK_E;
break;
case JVK_F:
ret = XK_F;
break;
case JVK_G:
ret = XK_G;
break;
case JVK_H:
ret = XK_H;
break;
case JVK_I:
ret = XK_I;
break;
case JVK_J:
ret = XK_J;
break;
case JVK_K:
ret = XK_K;
break;
case JVK_L:
ret = XK_L;
break;
case JVK_M:
ret = XK_M;
break;
case JVK_N:
ret = XK_N;
break;
case JVK_O:
ret = XK_O;
break;
case JVK_P:
ret = XK_P;
break;
case JVK_Q:
ret = XK_Q;
break;
case JVK_R:
ret = XK_R;
break;
case JVK_S:
ret = XK_S;
break;
case JVK_T:
ret = XK_T;
break;
case JVK_U:
ret = XK_U;
break;
case JVK_V:
ret = XK_V;
break;
case JVK_W:
ret = XK_W;
break;
case JVK_X:
ret = XK_X;
break;
case JVK_Y:
ret = XK_Y;
break;
case JVK_Z:
ret = XK_Z;
break;
/* F1 - F12 */
case JVK_F1:
ret = XK_F1;
break;
case JVK_F2:
ret = XK_F2;
break;
case JVK_F3:
ret = XK_F3;
break;
case JVK_F4:
ret = XK_F4;
break;
case JVK_F5:
ret = XK_F5;
break;
case JVK_F6:
ret = XK_F6;
break;
case JVK_F7:
ret = XK_F7;
break;
case JVK_F8:
ret = XK_F8;
break;
case JVK_F9:
ret = XK_F9;
break;
case JVK_F10:
ret = XK_F10;
break;
case JVK_F11:
ret = XK_F11;
break;
case JVK_F12:
ret = XK_F12;
break;
/* arrows (left, right, up, down) */
case JVK_LEFT:
ret = XK_Left;
break;
case JVK_RIGHT:
ret = XK_Right;
break;
case JVK_UP:
ret = XK_Up;
break;
case JVK_DOWN:
ret = XK_Down;
break;
case JVK_COMMA:
ret = XK_comma;
break;
case JVK_MINUS:
ret = XK_minus;
break;
case JVK_PLUS:
ret = XK_plus;
break;
case JVK_PERIOD:
ret = XK_period;
break;
case JVK_SLASH:
ret = XK_slash;
break;
case JVK_BACK_SLASH:
ret = XK_backslash;
break;
case JVK_SEMICOLON:
ret = XK_semicolon;
break;
case JVK_EQUALS:
ret = XK_equal;
break;
case JVK_COLON:
ret = XK_colon;
break;
case JVK_UNDERSCORE:
ret = XK_underscore;
break;
case JVK_DOLLAR:
ret = XK_dollar;
break;
case JVK_EXCLAMATION_MARK:
ret = XK_exclam;
break;
case JVK_GREATER:
ret = XK_greater;
break;
case JVK_LESS:
ret = XK_less;
break;
case JVK_QUOTE:
ret = XK_quoteleft;
break;
case JVK_BACK_QUOTE:
ret = XK_quoteright;
break;
case JVK_INSERT:
ret = XK_Insert;
break;
case JVK_HELP:
ret = XK_Help;
break;
case JVK_HOME:
ret = XK_Home;
break;
case JVK_END:
ret = XK_End;
break;
case JVK_PAGE_UP:
ret = XK_Page_Up;
break;
case JVK_PAGE_DOWN:
ret = XK_Page_Down;
break;
case JVK_OPEN_BRACKET:
ret = XK_bracketleft;
break;
case JVK_CLOSE_BRACKET:
ret = XK_bracketright;
break;
case JVK_LEFT_PARENTHESIS:
ret = XK_parenleft;
break;
case JVK_RIGHT_PARENTHESIS:
ret = XK_parenright;
break;
case 0x08:
ret = XK_Delete;
break;
default:
break;
}
return ret;
}
/**
* \brief Convert X11 keycode to Java ones.
* \param keycode X11 keycode
* \return Java keycode
*/
static int convertX11KeycodeToJava(int keycode)
{
int ret = -1;
switch(keycode)
{
/* 0 - 9 */
case XK_0:
ret = JVK_0;
break;
case XK_1:
ret = JVK_1;
break;
case XK_2:
ret = JVK_2;
break;
case XK_3:
ret = JVK_3;
break;
case XK_4:
ret = JVK_4;
break;
case XK_5:
ret = JVK_5;
break;
case XK_6:
ret = JVK_6;
break;
case XK_7:
ret = JVK_7;
break;
case XK_8:
ret = JVK_8;
break;
case XK_9:
ret = JVK_9;
break;
/* A - Z */
case XK_A:
case XK_a:
ret = JVK_A;
break;
case XK_B:
case XK_b:
ret = JVK_B;
break;
case XK_C:
case XK_c:
ret = JVK_C;
break;
case XK_D:
case XK_d:
ret = JVK_D;
break;
case XK_E:
case XK_e:
ret = JVK_E;
break;
case XK_F:
case XK_f:
ret = JVK_F;
break;
case XK_G:
case XK_g:
ret = JVK_G;
break;
case XK_H:
case XK_h:
ret = JVK_H;
break;
case XK_I:
case XK_i:
ret = JVK_I;
break;
case XK_J:
case XK_j:
ret = JVK_J;
break;
case XK_K:
case XK_k:
ret = JVK_K;
break;
case XK_L:
case XK_l:
ret = JVK_L;
break;
case XK_M:
case XK_m:
ret = JVK_M;
break;
case XK_N:
case XK_n:
ret = JVK_N;
break;
case XK_O:
case XK_o:
ret = JVK_O;
break;
case XK_P:
case XK_p:
ret = JVK_P;
break;
case XK_Q:
case XK_q:
ret = JVK_Q;
break;
case XK_R:
case XK_r:
ret = JVK_R;
break;
case XK_S:
case XK_s:
ret = JVK_S;
break;
case XK_T:
case XK_t:
ret = JVK_T;
break;
case XK_U:
case XK_u:
ret = JVK_U;
break;
case XK_V:
case XK_v:
ret = JVK_V;
break;
case XK_W:
case XK_w:
ret = JVK_W;
break;
case XK_X:
case XK_x:
ret = JVK_X;
break;
case XK_Y:
case XK_y:
ret = JVK_Y;
break;
case XK_Z:
case XK_z:
ret = JVK_Z;
break;
/* F1 - F12 */
case XK_F1:
ret = JVK_F1;
break;
case XK_F2:
ret = JVK_F2;
break;
case XK_F3:
ret = JVK_F3;
break;
case XK_F4:
ret = JVK_F4;
break;
case XK_F5:
ret = JVK_F5;
break;
case XK_F6:
ret = JVK_F6;
break;
case XK_F7:
ret = JVK_F7;
break;
case XK_F8:
ret = JVK_F8;
break;
case XK_F9:
ret = JVK_F9;
break;
case XK_F10:
ret = JVK_F10;
break;
case XK_F11:
ret = JVK_F11;
break;
case XK_F12:
ret = JVK_F12;
break;
/* arrows (left, right, up, down) */
case XK_Left:
ret = JVK_LEFT;
break;
case XK_Right:
ret = JVK_RIGHT;
break;
case XK_Up:
ret = JVK_UP;
break;
case XK_Down:
ret = JVK_DOWN;
break;
case XK_comma:
ret = JVK_COMMA;
break;
case XK_minus:
ret = JVK_MINUS;
break;
case XK_plus:
ret = JVK_PLUS;
break;
case XK_period:
ret = JVK_PERIOD;
break;
case XK_slash:
ret = JVK_SLASH;
break;
case XK_backslash:
ret = JVK_BACK_SLASH;
break;
case XK_semicolon:
ret = JVK_SEMICOLON;
break;
case XK_equal:
ret = JVK_EQUALS;
break;
case XK_colon:
ret = JVK_COLON;
break;
case XK_underscore:
ret = JVK_UNDERSCORE;
break;
case XK_dollar:
ret = JVK_DOLLAR;
break;
case XK_exclam:
ret = JVK_EXCLAMATION_MARK;
break;
case XK_greater:
ret = JVK_GREATER;
break;
case XK_less:
ret = JVK_LESS;
break;
case XK_quoteleft:
ret = JVK_QUOTE;
break;
case XK_quoteright:
ret = JVK_BACK_QUOTE;
break;
case XK_Insert:
ret = JVK_INSERT;
break;
case XK_Help:
ret = JVK_HELP;
break;
case XK_Home:
ret = JVK_HOME;
break;
case XK_End:
ret = JVK_END;
break;
case XK_Page_Up:
ret = JVK_PAGE_UP;
break;
case XK_Page_Down:
ret = JVK_PAGE_DOWN;
break;
case JVK_OPEN_BRACKET:
ret = XK_bracketleft;
break;
case XK_bracketright:
ret = JVK_CLOSE_BRACKET;
break;
case XK_parenleft:
ret = JVK_LEFT_PARENTHESIS;
break;
case XK_parenright:
ret = JVK_RIGHT_PARENTHESIS;
break;
case XK_Delete:
ret = 0x08;
break;
default:
break;
}
return ret;
}
/**
* \brief Convert X11 modifiers to Java user-defined ones.
* \param modfiers X11 modifiers
* \return Java user-defined modifiers
*/
static int X11ModifiersToJavaUserDefined(int modifiers)
{
int javaModifiers = 0;
if(modifiers & ControlMask)
{
javaModifiers |= 0x01;
}
if(modifiers & Mod1Mask)
{
/* Alt */
javaModifiers |= 0x02;
}
if(modifiers & ShiftMask)
{
javaModifiers |= 0x04;
}
if(modifiers & Mod4Mask)
{
/* Super */
javaModifiers |= 0x08;
}
return javaModifiers;
}
/**
* \brief Convert Java user-defined modifiers to X11 ones.
* \param modifiers Java user-defined modifiers
* \return X11 modifiers
*/
static int JavaUserDefinedModifiersToX11(int modifiers)
{
int x11Modifiers = 0;
if(modifiers & 0x01)
{
x11Modifiers |= ControlMask;
}
if(modifiers & 0x02)
{
/* Alt */
x11Modifiers |= Mod1Mask;
}
if(modifiers & 0x04)
{
x11Modifiers |= ShiftMask;
}
if(modifiers & 0x08)
{
/* Super */
x11Modifiers |= Mod4Mask;
}
return x11Modifiers;
}
/**
* \brief Notify Java side about key pressed (keycode + modifiers).
* \param keycode keycode
* \param modifiers modifiers used (SHIFT, CTRL, ALT, LOGO)
*/
static void notify(struct keyboard_hook* keyboard, jint keycode, jint modifiers)
{
JNIEnv *jniEnv = NULL;
jclass delegateClass = NULL;
if(!keyboard->delegate)
{
return;
}
if(0 != keyboard->jvm->AttachCurrentThreadAsDaemon((void **)&jniEnv, NULL))
{
return;
}
delegateClass = jniEnv->GetObjectClass(keyboard->delegate);
if(delegateClass)
{
jmethodID methodid = NULL;
methodid = jniEnv->GetMethodID(delegateClass, "receiveKey", "(II)V");
if(methodid)
{
jniEnv->CallVoidMethod(keyboard->delegate, methodid, keycode, modifiers);
}
}
jniEnv->ExceptionClear();
}
/**
* \brief X11 event loop thread entry point.
* \param arg thread argument
* \return NULL
*/
static void* x11_event_loop_thread(void* arg)
{
struct keyboard_hook* keyboard = (struct keyboard_hook*)arg;
XSelectInput(keyboard->display, keyboard->root, KeyPressMask);
while(keyboard->running)
{
XEvent ev;
while(XCheckMaskEvent(keyboard->display, 0xFFFFFFFF, &ev))
{
switch (ev.type)
{
case KeyPress:
for(std::list<keystrok>::iterator it = keyboard->keystrokes.begin() ; it != keyboard->keystrokes.end() ; ++it)
{
keystrok& ks = (*it);
XKeyEvent* keyEvent = (XKeyEvent*)&ev.xkey;
unsigned long keycode = -1;
//XKeycodeToKeysym(keyboard->display, keyEvent->keycode, 1);
XLookupString(keyEvent, NULL, 0, &keycode, NULL);
keycode = convertX11KeycodeToJava(keycode);
int modifiers = X11ModifiersToJavaUserDefined(keyEvent->state);
if(ks.vkcode == keycode && ks.modifiers == modifiers)
{
notify(keyboard, ks.vkcode, ks.modifiers);
}
}
break;
default:
break;
}
}
for(std::list<keystrok>::iterator it = keyboard->keystrokes.begin() ; it != keyboard->keystrokes.end() ; ++it)
{
keystrok& ks = (*it);
if(ks.active == 0)
{
/* hotkey to add */
int x11Keycode = convertJavaKeycodeToX11(ks.vkcode);
if(x11Keycode != -1)
{
x11Keycode = XKeysymToKeycode(keyboard->display, x11Keycode);
}
else
{
printf("failed\n");fflush(stdout);
ks.active = -1;
continue;
}
int x11Modifiers = JavaUserDefinedModifiersToX11(ks.modifiers);
ks.active = 1;
if(XGrabKey(keyboard->display, x11Keycode, x11Modifiers, keyboard->root, False, GrabModeAsync, GrabModeAsync) > 1)
{
fprintf(stderr, "[LOOP] Error when XGrabKey\n");fflush(stderr);
ks.active = -1;
}
}
else if(ks.active == -1)
{
/* hotkey to remove */
int x11Keycode = XKeysymToKeycode(keyboard->display,
convertJavaKeycodeToX11(ks.vkcode));
int x11Modifiers = JavaUserDefinedModifiersToX11(ks.modifiers);
if(XUngrabKey(keyboard->display, x11Keycode, x11Modifiers, keyboard->root) > 1)
{
fprintf(stderr, "[LOOP] Error when XUngrabKey\n");fflush(stderr);
}
it = keyboard->keystrokes.erase(it)--;
}
}
usleep(1000 * 1000);
pthread_yield();
}
return NULL;
}
JNIEXPORT jlong JNICALL Java_net_java_sip_communicator_impl_globalshortcut_NativeKeyboardHook_init
(JNIEnv* jniEnv, jclass clazz)
{
struct keyboard_hook* keyboard = NULL;
(void)jniEnv;
(void)clazz;
keyboard = new keyboard_hook();
if(!keyboard)
{
return (jlong)NULL;
}
keyboard->display = XOpenDisplay(NULL);
if(!keyboard->display)
{
free(keyboard);
return (jlong)NULL;
}
keyboard->root = DefaultRootWindow(keyboard->display);
return (jlong)keyboard;
}
/* XXX release JNI method */
JNIEXPORT void JNICALL Java_net_java_sip_communicator_impl_globalshortcut_NativeKeyboardHook_start
(JNIEnv* jniEnv, jclass clazz, jlong ptr)
{
struct keyboard_hook* keyboard = (struct keyboard_hook*)ptr;
pthread_t id;
pthread_attr_t attr;
(void)jniEnv;
(void)clazz;
if(keyboard->running)
{
return;
}
pthread_attr_init(&attr);
if(pthread_attr_setdetachstate(&attr, 1) != 0)
{
perror("pthread_attr_setdetachstate");fflush(stderr);
return;
}
keyboard->running = 1;
if(pthread_create(&id, &attr, x11_event_loop_thread, keyboard) != 0)
{
perror("pthread_create");fflush(stderr);
return;
}
}
JNIEXPORT void JNICALL Java_net_java_sip_communicator_impl_globalshortcut_NativeKeyboardHook_stop
(JNIEnv* jniEnv, jclass clazz, jlong ptr)
{
struct keyboard_hook* keyboard = (struct keyboard_hook*)ptr;
(void)jniEnv;
(void)clazz;
keyboard->running = 0;
}
JNIEXPORT void JNICALL Java_net_java_sip_communicator_impl_globalshortcut_NativeKeyboardHook_setDelegate
(JNIEnv* jniEnv, jclass clazz, jlong ptr, jobject delegate)
{
struct keyboard_hook* keyboard = (struct keyboard_hook*)ptr;
(void)clazz;
if(keyboard->delegate)
{
jniEnv->DeleteGlobalRef(keyboard->delegate);
keyboard->delegate = NULL;
}
if(delegate)
{
jobject delegate2 = jniEnv->NewGlobalRef(delegate);
if(delegate2)
{
jniEnv->GetJavaVM(&keyboard->jvm);
keyboard->delegate = delegate2;
}
}
}
JNIEXPORT jboolean JNICALL Java_net_java_sip_communicator_impl_globalshortcut_NativeKeyboardHook_registerShortcut
(JNIEnv* jniEnv, jclass clazz, jlong ptr, jint keycode, jint modifiers)
{
struct keyboard_hook* keyboard = (struct keyboard_hook*)ptr;
(void)jniEnv;
(void)clazz;
if(keyboard)
{
keystrok ks;
ks.vkcode = keycode;
ks.modifiers = modifiers;
ks.active = 0;
keyboard->keystrokes.push_back(ks);
return JNI_TRUE;
}
return JNI_FALSE;
}
JNIEXPORT void JNICALL Java_net_java_sip_communicator_impl_globalshortcut_NativeKeyboardHook_unregisterShortcut
(JNIEnv* jniEnv, jclass clazz, jlong ptr, jint keycode, jint modifiers)
{
struct keyboard_hook* keyboard = (struct keyboard_hook*)ptr;
(void)jniEnv;
(void)clazz;
if(keyboard)
{
for(std::list<keystrok>::iterator it = keyboard->keystrokes.begin() ; it != keyboard->keystrokes.end() ; ++it)
{
keystrok& ks = (*it);
if(ks.vkcode == keycode && ks.modifiers == modifiers)
{
ks.active = -1;
}
}
}
}

@ -0,0 +1,61 @@
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class net_java_sip_communicator_impl_globalshortcut_NativeKeyboardHook */
#ifndef _Included_net_java_sip_communicator_impl_globalshortcut_NativeKeyboardHook
#define _Included_net_java_sip_communicator_impl_globalshortcut_NativeKeyboardHook
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: net_java_sip_communicator_impl_globalshortcut_NativeKeyboardHook
* Method: init
* Signature: ()J
*/
JNIEXPORT jlong JNICALL Java_net_java_sip_communicator_impl_globalshortcut_NativeKeyboardHook_init
(JNIEnv *, jclass);
/*
* Class: net_java_sip_communicator_impl_globalshortcut_NativeKeyboardHook
* Method: start
* Signature: (J)V
*/
JNIEXPORT void JNICALL Java_net_java_sip_communicator_impl_globalshortcut_NativeKeyboardHook_start
(JNIEnv *, jclass, jlong);
/*
* Class: net_java_sip_communicator_impl_globalshortcut_NativeKeyboardHook
* Method: stop
* Signature: (J)V
*/
JNIEXPORT void JNICALL Java_net_java_sip_communicator_impl_globalshortcut_NativeKeyboardHook_stop
(JNIEnv *, jclass, jlong);
/*
* Class: net_java_sip_communicator_impl_globalshortcut_NativeKeyboardHook
* Method: setDelegate
* Signature: (JLnet/java/sip/communicator/impl/globalshortcut/NativeKeyboardHookDelegate;)V
*/
JNIEXPORT void JNICALL Java_net_java_sip_communicator_impl_globalshortcut_NativeKeyboardHook_setDelegate
(JNIEnv *, jclass, jlong, jobject);
/*
* Class: net_java_sip_communicator_impl_globalshortcut_NativeKeyboardHook
* Method: registerShortcut
* Signature: (JII)Z
*/
JNIEXPORT jboolean JNICALL Java_net_java_sip_communicator_impl_globalshortcut_NativeKeyboardHook_registerShortcut
(JNIEnv *, jclass, jlong, jint, jint);
/*
* Class: net_java_sip_communicator_impl_globalshortcut_NativeKeyboardHook
* Method: unregisterShortcut
* Signature: (JII)V
*/
JNIEXPORT void JNICALL Java_net_java_sip_communicator_impl_globalshortcut_NativeKeyboardHook_unregisterShortcut
(JNIEnv *, jclass, jlong, jint, jint);
#ifdef __cplusplus
}
#endif
#endif

@ -0,0 +1,940 @@
/*
* Jitsi Communicator, the OpenSource Java VoIP and Instant Messaging client.
*
/bin/bash: 2: command not found
* See terms of license at gnu.org.
*/
#include "net_java_sip_communicator_impl_globalshortcut_NativeKeyboardHook.h"
/* Mac OS X specific code */
#import <Foundation/NSAutoreleasePool.h>
#import <Foundation/Foundation.h>
#import <Foundation/NSObject.h>
#import "DDHotKeyCenter.h"
#import "javakey.h"
/* following enum comes from http://snipplr.com/view/42797/ */
enum
{
kVK_ANSI_A = 0x00,
kVK_ANSI_S = 0x01,
kVK_ANSI_D = 0x02,
kVK_ANSI_F = 0x03,
kVK_ANSI_H = 0x04,
kVK_ANSI_G = 0x05,
kVK_ANSI_Z = 0x06,
kVK_ANSI_X = 0x07,
kVK_ANSI_C = 0x08,
kVK_ANSI_V = 0x09,
kVK_ANSI_B = 0x0B,
kVK_ANSI_Q = 0x0C,
kVK_ANSI_W = 0x0D,
kVK_ANSI_E = 0x0E,
kVK_ANSI_R = 0x0F,
kVK_ANSI_Y = 0x10,
kVK_ANSI_T = 0x11,
kVK_ANSI_1 = 0x12,
kVK_ANSI_2 = 0x13,
kVK_ANSI_3 = 0x14,
kVK_ANSI_4 = 0x15,
kVK_ANSI_6 = 0x16,
kVK_ANSI_5 = 0x17,
kVK_ANSI_Equal = 0x18,
kVK_ANSI_9 = 0x19,
kVK_ANSI_7 = 0x1A,
kVK_ANSI_Minus = 0x1B,
kVK_ANSI_8 = 0x1C,
kVK_ANSI_0 = 0x1D,
kVK_ANSI_RightBracket = 0x1E,
kVK_ANSI_O = 0x1F,
kVK_ANSI_U = 0x20,
kVK_ANSI_LeftBracket = 0x21,
kVK_ANSI_I = 0x22,
kVK_ANSI_P = 0x23,
kVK_ANSI_L = 0x25,
kVK_ANSI_J = 0x26,
kVK_ANSI_Quote = 0x27,
kVK_ANSI_K = 0x28,
kVK_ANSI_Semicolon = 0x29,
kVK_ANSI_Backslash = 0x2A,
kVK_ANSI_Comma = 0x2B,
kVK_ANSI_Slash = 0x2C,
kVK_ANSI_N = 0x2D,
kVK_ANSI_M = 0x2E,
kVK_ANSI_Period = 0x2F,
kVK_ANSI_Grave =JVK_2,
kVK_ANSI_KeypadDecimal = 0x41,
kVK_ANSI_KeypadMultiply = 0x43,
kVK_ANSI_KeypadPlus = 0x45,
kVK_ANSI_KeypadClear = 0x47,
kVK_ANSI_KeypadDivide = 0x4B,
kVK_ANSI_KeypadEnter = 0x4C,
kVK_ANSI_KeypadMinus = 0x4E,
kVK_ANSI_KeypadEquals = 0x51,
kVK_ANSI_Keypad0 = 0x52,
kVK_ANSI_Keypad1 = 0x53,
kVK_ANSI_Keypad2 = 0x54,
kVK_ANSI_Keypad3 = 0x55,
kVK_ANSI_Keypad4 = 0x56,
kVK_ANSI_Keypad5 = 0x57,
kVK_ANSI_Keypad6 = 0x58,
kVK_ANSI_Keypad7 = 0x59,
kVK_ANSI_Keypad8 = 0x5B,
kVK_ANSI_Keypad9 = 0x5C
};
/* keycodes for keys that are independent of keyboard layout*/
enum
{
kVK_Return = 0x24,
kVK_Tab =JVK_0,
kVK_Space =JVK_1,
kVK_Delete =JVK_3,
kVK_Escape =JVK_5,
kVK_Command =JVK_7,
kVK_Shift =JVK_8,
kVK_CapsLock =JVK_9,
kVK_Option =JVK_A,
kVK_Control =JVK_B,
kVK_RightShift =JVK_C,
kVK_RightOption =JVK_D,
kVK_RightControl =JVK_E,
kVK_Function =JVK_F,
kVK_F17 = 0x40,
kVK_VolumeUp = 0x48,
kVK_VolumeDown = 0x49,
kVK_Mute = 0x4A,
kVK_F18 = 0x4F,
kVK_F19 = 0x50,
kVK_F20 = 0x5A,
kVK_F5 = 0x60,
kVK_F6 = 0x61,
kVK_F7 = 0x62,
kVK_F3 = 0x63,
kVK_F8 = 0x64,
kVK_F9 = 0x65,
kVK_F11 = 0x67,
kVK_F13 = 0x69,
kVK_F16 = 0x6A,
kVK_F14 = 0x6B,
kVK_F10 = 0x6D,
kVK_F12 = 0x6F,
kVK_F15 = 0x71,
kVK_Help = 0x72,
kVK_Home = 0x73,
kVK_PageUp = 0x74,
kVK_ForwardDelete = 0x75,
kVK_F4 = 0x76,
kVK_End = 0x77,
kVK_F2 = 0x78,
kVK_PageDown = 0x79,
kVK_F1 = 0x7A,
kVK_LeftArrow = 0x7B,
kVK_RightArrow = 0x7C,
kVK_DownArrow = 0x7D,
kVK_UpArrow = 0x7E
};
/* ISO keyboards only*/
enum
{
kVK_ISO_Section = 0x0A
};
/* JIS keyboards only*/
enum
{
kVK_JIS_Yen = 0x5D,
kVK_JIS_Underscore = 0x5E,
kVK_JIS_KeypadComma = 0x5F,
kVK_JIS_Eisu = 0x66,
kVK_JIS_Kana = 0x68
};
/**
* \brief Convert Java keycode to native ones.
* Reference: http://boredzo.org/blog/wp-content/uploads/2007/05/imtx-virtual-keycodes.png
* \param keycode Java keycode
* \return native Mac OS X keycode
*/
static int convertJavaKeycodeToMac(int keycode)
{
int ret = -1;
switch(keycode)
{
/* 0 - 9 */
case JVK_0:
ret = kVK_ANSI_0;
break;
case JVK_1:
ret = kVK_ANSI_1;
break;
case JVK_2:
ret = kVK_ANSI_2;
break;
case JVK_3:
ret = kVK_ANSI_3;
break;
case JVK_4:
ret = kVK_ANSI_4;
break;
case JVK_5:
ret = kVK_ANSI_5;
break;
case JVK_6:
ret = kVK_ANSI_6;
break;
case JVK_7:
ret = kVK_ANSI_7;
break;
case JVK_8:
ret = kVK_ANSI_8;
break;
case JVK_9:
ret = kVK_ANSI_9;
break;
/* A - Z */
case JVK_A:
ret = kVK_ANSI_A;
break;
case JVK_B:
ret = kVK_ANSI_B;
break;
case JVK_C:
ret = kVK_ANSI_C;
break;
case JVK_D:
ret = kVK_ANSI_D;
break;
case JVK_E:
ret = kVK_ANSI_E;
break;
case JVK_F:
ret = kVK_ANSI_F;
break;
case JVK_G:
ret = kVK_ANSI_G;
break;
case JVK_H:
ret = kVK_ANSI_H;
break;
case JVK_I:
ret = kVK_ANSI_I;
break;
case JVK_J:
ret = kVK_ANSI_J;
break;
case JVK_K:
ret = kVK_ANSI_K;
break;
case JVK_L:
ret = kVK_ANSI_L;
break;
case JVK_M:
ret = kVK_ANSI_M;
break;
case JVK_N:
ret = kVK_ANSI_N;
break;
case JVK_O:
ret = kVK_ANSI_O;
break;
case JVK_P:
ret = kVK_ANSI_P;
break;
case JVK_Q:
ret = kVK_ANSI_Q;
break;
case JVK_R:
ret = kVK_ANSI_R;
break;
case JVK_S:
ret = kVK_ANSI_S;
break;
case JVK_T:
ret = kVK_ANSI_T;
break;
case JVK_U:
ret = kVK_ANSI_U;
break;
case JVK_V:
ret = kVK_ANSI_V;
break;
case JVK_W:
ret = kVK_ANSI_W;
break;
case JVK_X:
ret = kVK_ANSI_X;
break;
case JVK_Y:
ret = kVK_ANSI_Y;
break;
case JVK_Z:
ret = kVK_ANSI_Z;
break;
/* F1 - F12 */
case JVK_F1:
ret = kVK_F1;
break;
case JVK_F2:
ret = kVK_F2;
break;
case JVK_F3:
ret = kVK_F3;
break;
case JVK_F4:
ret = kVK_F4;
break;
case JVK_F5:
ret = kVK_F5;
break;
case JVK_F6:
ret = kVK_F6;
break;
case JVK_F7:
ret = kVK_F7;
break;
case JVK_F8:
ret = kVK_F8;
break;
case JVK_F9:
ret = kVK_F9;
break;
case JVK_F10:
ret = kVK_F10;
break;
case JVK_F11:
ret = kVK_F11;
break;
case JVK_F12:
ret = kVK_F12;
break;
/* arrows (left, right, up, down) */
case JVK_LEFT:
ret = kVK_LeftArrow;
break;
case JVK_RIGHT:
ret = kVK_RightArrow;
break;
case JVK_UP:
ret = kVK_UpArrow;
break;
case JVK_DOWN:
ret = kVK_DownArrow;
break;
case JVK_COMMA:
ret = kVK_ANSI_Comma;
break;
case JVK_MINUS:
ret = kVK_ANSI_Minus;
break;
case JVK_PLUS:
ret = kVK_ANSI_KeypadPlus;
break;
case JVK_PERIOD:
ret = kVK_ANSI_Period;
break;
case JVK_SLASH:
ret = kVK_ANSI_Slash;
break;
case JVK_BACK_SLASH:
ret = kVK_ANSI_Backslash;
break;
case JVK_SEMICOLON:
ret = kVK_ANSI_Semicolon;
break;
case JVK_EQUALS:
ret = kVK_ANSI_Equal;
break;
case JVK_COLON:
ret = -1;
break;
case JVK_UNDERSCORE:
ret = kVK_JIS_Underscore;
break;
case JVK_DOLLAR:
ret = -1;
break;
case JVK_EXCLAMATION_MARK:
ret = -1;
break;
case JVK_GREATER:
ret = -1;
break;
case JVK_LESS:
ret = -1;
break;
case JVK_QUOTE:
ret = kVK_ANSI_Quote;
break;
case JVK_BACK_QUOTE:
ret = -1;
break;
case JVK_INSERT:
ret = -1;
break;
case JVK_HELP:
ret = kVK_Help;
break;
case JVK_HOME:
ret = kVK_Home;
break;
case JVK_END:
ret = kVK_End;
break;
case JVK_PAGE_UP:
ret = kVK_PageUp;
break;
case JVK_PAGE_DOWN:
ret = kVK_PageDown;
break;
case JVK_OPEN_BRACKET:
ret = kVK_ANSI_LeftBracket;
break;
case JVK_CLOSE_BRACKET:
ret = kVK_ANSI_RightBracket;
break;
case 0x08:
ret = kVK_Delete;
break;
default:
break;
}
return ret;
}
/**
* \brief Convert Mac OS X keycode to Java ones.
* Reference: http://boredzo.org/blog/wp-content/uploads/2007/05/imtx-virtual-keycodes.png
* \param keycode Mac OS X keycode
* \return Java keycode
*/
static int convertMacKeycodeToJava(int keycode)
{
int ret = -1;
switch(keycode)
{
/* 0 - 9 */
case kVK_ANSI_0:
ret =JVK_0;
break;
case kVK_ANSI_1:
ret =JVK_1;
break;
case kVK_ANSI_2:
ret =JVK_2;
break;
case kVK_ANSI_3:
ret =JVK_3;
break;
case kVK_ANSI_4:
ret =JVK_4;
break;
case kVK_ANSI_5:
ret =JVK_5;
break;
case kVK_ANSI_6:
ret =JVK_6;
break;
case kVK_ANSI_7:
ret =JVK_7;
break;
case kVK_ANSI_8:
ret =JVK_8;
break;
case kVK_ANSI_9:
ret =JVK_9;
break;
/* A - Z */
case kVK_ANSI_A:
ret =JVK_A;
break;
case kVK_ANSI_B:
ret =JVK_B;
break;
case kVK_ANSI_C:
ret =JVK_C;
break;
case kVK_ANSI_D:
ret =JVK_D;
break;
case kVK_ANSI_E:
ret =JVK_E;
break;
case kVK_ANSI_F:
ret =JVK_F;
break;
case kVK_ANSI_G:
ret =JVK_G;
break;
case kVK_ANSI_H:
ret =JVK_H;
break;
case kVK_ANSI_I:
ret =JVK_I;
break;
case kVK_ANSI_J:
ret =JVK_J;
break;
case kVK_ANSI_K:
ret =JVK_K;
break;
case kVK_ANSI_L:
ret =JVK_L;
break;
case kVK_ANSI_M:
ret =JVK_M;
break;
case kVK_ANSI_N:
ret =JVK_N;
break;
case kVK_ANSI_O:
ret =JVK_O;
break;
case kVK_ANSI_P:
ret =JVK_P;
break;
case kVK_ANSI_Q:
ret =JVK_Q;
break;
case kVK_ANSI_R:
ret =JVK_R;
break;
case kVK_ANSI_S:
ret =JVK_S;
break;
case kVK_ANSI_T:
ret =JVK_T;
break;
case kVK_ANSI_U:
ret =JVK_U;
break;
case kVK_ANSI_V:
ret =JVK_V;
break;
case kVK_ANSI_W:
ret =JVK_W;
break;
case kVK_ANSI_X:
ret =JVK_X;
break;
case kVK_ANSI_Y:
ret =JVK_Y;
break;
case kVK_ANSI_Z:
ret =JVK_Z;
break;
/* F1 - F12 */
case kVK_F1:
ret =JVK_F1;
break;
case kVK_F2:
ret =JVK_F2;
break;
case kVK_F3:
ret =JVK_F3;
break;
case kVK_F4:
ret =JVK_F4;
break;
case kVK_F5:
ret =JVK_F5;
break;
case kVK_F6:
ret =JVK_F6;
break;
case kVK_F7:
ret =JVK_F7;
break;
case kVK_F8:
ret =JVK_F8;
break;
case kVK_F9:
ret =JVK_F9;
break;
case kVK_F10:
ret =JVK_F10;
break;
case kVK_F11:
ret =JVK_F11;
break;
case kVK_F12:
ret =JVK_F12;
break;
/* arrows (left, right, up, down) */
case kVK_LeftArrow:
ret =JVK_LEFT;
break;
case kVK_RightArrow:
ret =JVK_RIGHT;
break;
case kVK_UpArrow:
ret =JVK_UP;
break;
case kVK_DownArrow:
ret =JVK_DOWN;
break;
case kVK_ANSI_Comma:
ret =JVK_COMMA;
break;
case kVK_ANSI_Minus:
ret =JVK_MINUS;
break;
case kVK_ANSI_KeypadPlus:
ret =JVK_PLUS;
break;
case kVK_ANSI_Period:
ret =JVK_PERIOD;
break;
case kVK_ANSI_Slash:
ret =JVK_SLASH;
break;
case kVK_ANSI_Backslash:
ret =JVK_BACK_SLASH;
break;
case kVK_ANSI_Semicolon:
ret =JVK_SEMICOLON;
break;
case kVK_ANSI_Equal:
ret =JVK_EQUALS;
break;
case kVK_JIS_Underscore:
ret =JVK_UNDERSCORE;
break;
case kVK_ANSI_Quote:
ret =JVK_QUOTE;
break;
case kVK_Help:
ret =JVK_HELP;
break;
case kVK_Home:
ret =JVK_HOME;
break;
case kVK_End:
ret =JVK_END;
break;
case kVK_PageUp:
ret =JVK_PAGE_UP;
break;
case kVK_PageDown:
ret =JVK_PAGE_DOWN;
break;
case kVK_ANSI_LeftBracket:
ret =JVK_OPEN_BRACKET;
break;
case kVK_ANSI_RightBracket:
ret =JVK_CLOSE_BRACKET;
break;
case kVK_Delete:
ret = 0x08;
break;
default:
break;
}
return ret;
}
/**
* \brief Convert Java user-defined modifiers to Mac OS X ones.
* \param modifiers Java user-defined modifiers
* \return Mac OS X modifiers
*/
static int convertJavaUserDefinedModifiersToMac(int modifiers)
{
int macModifiers = 0;
if(modifiers & 0x01)
{
/* CTRL */
macModifiers |= NSControlKeyMask;
}
if(modifiers & 0x02)
{
/* ALT */
macModifiers |= NSAlternateKeyMask;
}
if(modifiers & 0x04)
{
/* SHIFT */
macModifiers |= NSShiftKeyMask;
}
if(modifiers & 0x08)
{
/* LOGO */
macModifiers |= NSCommandKeyMask;
}
return macModifiers;
}
/**
* \brief Convert Mac modifiers to our Java user-defined ones.
* \param modifiers Mac modifiers (MOD_CONTROL, ...)
* \return Java user-defined modifiers
*/
static int convertMacModifiersToJavaUserDefined(int modifiers)
{
int javaModifiers = 0;
if(modifiers & NSControlKeyMask)
{
javaModifiers |= 0x01;
}
if(modifiers & NSAlternateKeyMask)
{
javaModifiers |= 0x02;
}
if(modifiers & NSShiftKeyMask)
{
javaModifiers |= 0x04;
}
if(modifiers & NSCommandKeyMask)
{
javaModifiers |= 0x08;
}
return javaModifiers;
}
@interface KeyboardHook: NSObject
{
@private
jobject delegateObject;
JavaVM* vm;
}
-(void)dealloc;
-(id)init;
-(void) setDelegate:(jobject)delegate inJNIEnv:(JNIEnv* )jniEnv;
-(void) hotkeyAction:(NSEvent*)hotKeyEvent inObject:(id)anObject;
-(void) notify:(int) keyCode inModifiers:(int)modifiers;
-(int) registerKey:(int)keycode inModifiers:(int)modifiers;
-(void) unregisterKey:(int)keycode inModifiers:(int)modifiers;
@end
@implementation KeyboardHook
-(void) notify:(int) keyCode inModifiers:(int)modifiers;
{
jobject delegate;
JNIEnv* jniEnv = NULL;
jclass delegateClass = NULL;
delegate = self->delegateObject;
if(!delegate)
return;
vm = self->vm;
if(0 != (*vm)->AttachCurrentThreadAsDaemon(vm, (void**)&jniEnv, NULL))
return;
delegateClass = (*jniEnv)->GetObjectClass(jniEnv, delegate);
if(delegateClass)
{
jmethodID methodid = NULL;
methodid = (*jniEnv)->GetMethodID(jniEnv, delegateClass,"receiveKey", "(II)V");
if(methodid)
{
(*jniEnv)->CallVoidMethod(jniEnv, delegate, methodid, keyCode, modifiers);
}
}
(*jniEnv)->ExceptionClear(jniEnv);
}
- (void)setDelegate:(jobject) delegate inJNIEnv:(JNIEnv*)jniEnv
{
if(self->delegateObject)
{
if(!jniEnv)
(*(self->vm))->AttachCurrentThread(self->vm, (void**)&jniEnv, NULL);
(*jniEnv)->DeleteGlobalRef(jniEnv, self->delegateObject);
self->delegateObject = NULL;
self->vm = NULL;
}
if(delegate)
{
delegate = (*jniEnv)->NewGlobalRef(jniEnv, delegate);
if(delegate)
{
(*jniEnv)->GetJavaVM(jniEnv, &(self->vm));
self->delegateObject = delegate;
}
}
}
-(void) hotkeyAction:(NSEvent*)hotKeyEvent inObject:(id)anObject;
{
int modifiers = [hotKeyEvent modifierFlags];
int keycode = [hotKeyEvent keyCode];
int javaModifiers = 0;
int javaKeycode = 0;
(void)anObject;
javaKeycode = convertMacKeycodeToJava(keycode);
javaModifiers = convertMacModifiersToJavaUserDefined(modifiers);
[self notify:javaKeycode inModifiers:javaModifiers];
}
- (id)init
{
if((self = [super init]))
{
self->delegateObject = NULL;
self->vm = NULL;
}
return self;
}
- (void)dealloc
{
[self setDelegate:NULL inJNIEnv:NULL];
[super dealloc];
}
- (int) registerKey:(int)keycode inModifiers:(int)modifiers
{
NSAutoreleasePool* autoreleasePool = NULL;
DDHotKeyCenter* c = NULL;
autoreleasePool = [[NSAutoreleasePool alloc] init];
c = [[DDHotKeyCenter alloc] init];
/*
DDHotKeyTask task = ^(NSEvent* hkEvent)
{
printf("hot task\n");fflush(stdout);
};
*/
if(![c registerHotKeyWithKeyCode:keycode modifierFlags:(modifiers) target:self action:@selector(hotkeyAction:inObject:) object:nil])
{
[c release];
[autoreleasePool release];
return 0;
}
[c release];
[autoreleasePool release];
return 1;
}
- (void) unregisterKey:(int)keycode inModifiers:(int)modifiers
{
NSAutoreleasePool* autoreleasePool;
DDHotKeyCenter* c = NULL;
autoreleasePool = [[NSAutoreleasePool alloc] init];
c = [[DDHotKeyCenter alloc] init];
[c unregisterHotKeyWithKeyCode:keycode modifierFlags:(modifiers)];
[c release];
[autoreleasePool release];
}
@end
JNIEXPORT jlong JNICALL Java_net_java_sip_communicator_impl_globalshortcut_NativeKeyboardHook_init
(JNIEnv* jniEnv, jclass clazz)
{
KeyboardHook* keyboard = NULL;
NSAutoreleasePool* autoreleasePool;
(void)jniEnv;
(void)clazz;
autoreleasePool = [[NSAutoreleasePool alloc] init];
keyboard = [[KeyboardHook alloc] init];
[autoreleasePool release];
return (jlong)keyboard;
}
JNIEXPORT void JNICALL Java_net_java_sip_communicator_impl_globalshortcut_NativeKeyboardHook_start
(JNIEnv* jniEnv, jclass clazz, jlong ptr)
{
(void)jniEnv;
(void)clazz;
(void)ptr;
/* do nothing */
}
JNIEXPORT void JNICALL Java_net_java_sip_communicator_impl_globalshortcut_NativeKeyboardHook_stop
(JNIEnv* jniEnv, jclass clazz, jlong ptr)
{
(void)jniEnv;
(void)clazz;
(void)ptr;
/* do nothing */
}
JNIEXPORT void JNICALL Java_net_java_sip_communicator_impl_globalshortcut_NativeKeyboardHook_setDelegate
(JNIEnv* jniEnv, jclass clazz, jlong ptr, jobject delegate)
{
KeyboardHook* keyboard = NULL;
(void)clazz;
if(delegate)
{
keyboard = (KeyboardHook*)ptr;
[keyboard setDelegate:delegate inJNIEnv:jniEnv];
}
else
{
keyboard = NULL;
}
}
JNIEXPORT jboolean JNICALL Java_net_java_sip_communicator_impl_globalshortcut_NativeKeyboardHook_registerShortcut
(JNIEnv* jniEnv, jclass clazz, jlong ptr, jint keycode, jint modifiers)
{
KeyboardHook* keyboard = (KeyboardHook*)ptr;
(void)jniEnv;
(void)clazz;
if(keyboard)
{
int macKeycode = convertJavaKeycodeToMac(keycode);
int macModifiers = convertJavaUserDefinedModifiersToMac(modifiers);
if(macKeycode == -1)
{
return JNI_FALSE;
}
if([keyboard registerKey:macKeycode inModifiers:macModifiers])
{
return JNI_TRUE;
}
}
return JNI_FALSE;
}
JNIEXPORT void JNICALL Java_net_java_sip_communicator_impl_globalshortcut_NativeKeyboardHook_unregisterShortcut
(JNIEnv* jniEnv, jclass clazz, jlong ptr, jint keycode, jint modifiers)
{
KeyboardHook* keyboard = (KeyboardHook*)ptr;
(void)jniEnv;
(void)clazz;
if(keyboard)
{
int macKeycode = convertJavaKeycodeToMac(keycode);
int macModifiers = convertJavaUserDefinedModifiersToMac(modifiers);
if(macModifiers == -1)
{
return;
}
[keyboard unregisterKey:macKeycode inModifiers:macModifiers];
}
}

@ -10,6 +10,7 @@
/**
*
* @param <T> the hashtable extension
* @author Lyubomir Marinov
*/
public abstract class HashtableConfigurationStore<T extends Hashtable>

@ -0,0 +1,195 @@
/*
* 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.impl.globalshortcut;
import java.awt.*;
import java.util.*;
import java.util.List; // disambiguation
import net.java.sip.communicator.service.globalshortcut.*;
import net.java.sip.communicator.service.keybindings.*;
import net.java.sip.communicator.service.protocol.*;
import net.java.sip.communicator.service.protocol.event.*;
import net.java.sip.communicator.util.*;
/**
* Shortcut for call (take the call, hang up, ...).
*
* @author Sebastien Vincent
*/
public class CallShortcut
implements GlobalShortcutListener,
CallListener
{
/**
* The <tt>Logger</tt> used by the <tt>CallShortcut</tt> class
* and its instances for logging output.
*/
private static final Logger logger = Logger.getLogger(CallShortcut.class);
/**
* Keybindings service.
*/
private KeybindingsService keybindingsService =
GlobalShortcutActivator.getKeybindingsService();
/**
* List of incoming calls.
*/
private ArrayList<Call> incomingCalls = new ArrayList<Call>();
/**
* Constructor.
*/
public CallShortcut()
{
}
/**
* Callback when an shortcut is typed
*
* @param evt <tt>GlobalShortcutEvent</tt>
*/
public void shortcutReceived(GlobalShortcutEvent evt)
{
AWTKeyStroke keystroke = evt.getKeyStroke();
GlobalKeybindingSet set = keybindingsService.getGlobalBindings();
Call choosenCall = null;
for(Map.Entry<String, List<AWTKeyStroke>> entry :
set.getBindings().entrySet())
{
for(AWTKeyStroke ks : entry.getValue())
{
if(entry.getKey().equals("answer") &&
keystroke.getKeyCode() == ks.getKeyCode() &&
keystroke.getModifiers() == ks.getModifiers())
{
synchronized(incomingCalls)
{
int size = incomingCalls.size();
for(int i = 0 ; i < size ; i++)
{
Call c = incomingCalls.get(i);
if(c.getCallPeers().next().getState() ==
CallPeerState.INCOMING_CALL)
{
choosenCall = c;
break;
}
}
}
if(choosenCall == null)
return;
final OperationSetBasicTelephony<?> opSet =
choosenCall.getProtocolProvider().getOperationSet(
OperationSetBasicTelephony.class);
final Call cCall = choosenCall;
new Thread()
{
public void run()
{
try
{
opSet.answerCallPeer(cCall.getCallPeers().next());
}
catch(OperationFailedException e)
{
logger.info("Failed to answer call via global shortcut", e);
}
}
}.start();
}
else if(entry.getKey().equals("hangup") &&
keystroke.getKeyCode() == ks.getKeyCode() &&
keystroke.getModifiers() == ks.getModifiers())
{
Call incomingCall = null;
synchronized(incomingCalls)
{
int size = incomingCalls.size();
for(int i = 0 ; i < size ; i++)
{
Call c = incomingCalls.get(i);
if(c.getCallPeers().next().getState() ==
CallPeerState.CONNECTED)
{
choosenCall = c;
break;
}
else if(c.getCallPeers().next().getState() ==
CallPeerState.INCOMING_CALL && incomingCall == null)
{
incomingCall = c;
}
}
}
if(choosenCall == null && incomingCall != null)
{
// maybe we just want to hangup (refuse) incoming call
choosenCall = incomingCall;
}
if(choosenCall == null)
return;
final OperationSetBasicTelephony<?> opSet =
choosenCall.getProtocolProvider().getOperationSet(
OperationSetBasicTelephony.class);
final Call cCall = choosenCall;
new Thread()
{
public void run()
{
try
{
opSet.hangupCallPeer(cCall.getCallPeers().next());
}
catch(OperationFailedException e)
{
logger.info("Failed to answer call via global shortcut", e);
}
}
}.start();
}
}
}
}
public void incomingCallReceived(CallEvent event)
{
Call sourceCall = event.getSourceCall();
synchronized(incomingCalls)
{
incomingCalls.add(sourceCall);
}
}
public void outgoingCallCreated(CallEvent event)
{
/* do nothing */
}
public void callEnded(CallEvent event)
{
Call sourceCall = event.getSourceCall();
incomingCalls.remove(sourceCall);
}
}

@ -0,0 +1,241 @@
/*
* 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.impl.globalshortcut;
import net.java.sip.communicator.service.globalshortcut.*;
import net.java.sip.communicator.service.gui.*;
import net.java.sip.communicator.service.keybindings.*;
import net.java.sip.communicator.service.protocol.*;
import net.java.sip.communicator.util.*;
import org.osgi.framework.*;
/**
* OSGi Activator for global shortcut.
*
* @author Sebastien Vincent
*/
public class GlobalShortcutActivator
implements BundleActivator
{
/**
* The <tt>Logger</tt> used by the <tt>GlobalShortcutActivator</tt> class
* and its instances for logging output.
*/
private static final Logger logger =
Logger.getLogger(GlobalShortcutActivator.class);
/**
* The OSGi <tt>ServiceRegistration</tt> of <tt>GlobalShortcut</tt>.
*/
private ServiceRegistration serviceRegistration;
/**
* The <tt>GlobalShortcutServiceImpl</tt>.
*/
protected static GlobalShortcutServiceImpl globalShortcutService = null;
/**
* OSGi bundle context.
*/
private static BundleContext bundleContext = null;
/**
* Keybindings service reference.
*/
private static KeybindingsService keybindingsService = null;
/**
* UI service reference.
*/
private static UIService uiService = null;
/**
* Returns the <tt>KeybindingsService</tt> obtained from the bundle context.
*
* @return the <tt>KeybindingsService</tt> obtained from the bundle context
*/
public static KeybindingsService getKeybindingsService()
{
if (keybindingsService == null)
{
keybindingsService
= ServiceUtils.getService(
bundleContext,
KeybindingsService.class);
}
return keybindingsService;
}
/**
* Returns the <tt>UIService</tt> obtained from the bundle context.
*
* @return the <tt>UIService</tt> obtained from the bundle context
*/
public static UIService getUIService()
{
if (uiService == null)
{
uiService
= ServiceUtils.getService(
bundleContext,
UIService.class);
}
return uiService;
}
/**
* Starts the execution of this service bundle in the specified context.
*
* @param bundleContext the context in which the service bundle is to
* start executing
* @throws Exception if an error occurs while starting the execution of the
* service bundle in the specified context
*/
public void start(BundleContext bundleContext)
throws Exception
{
GlobalShortcutActivator.bundleContext = bundleContext;
serviceRegistration = null;
globalShortcutService = new GlobalShortcutServiceImpl();
globalShortcutService.start();
bundleContext.registerService(GlobalShortcutService.class.getName(),
globalShortcutService, null);
globalShortcutService.reloadGlobalShortcuts();
registerListenerWithProtocolProviderService();
bundleContext.addServiceListener(new ServiceListener()
{
public void serviceChanged(ServiceEvent serviceEvent)
{
GlobalShortcutActivator.this.serviceChanged(serviceEvent);
}
});
if (logger.isDebugEnabled())
logger.debug("GlobalShortcut Service ... [REGISTERED]");
}
/**
* Stops the execution of this service bundle in the specified context.
*
* @param bundleContext the context in which this service bundle is to
* stop executing
* @throws Exception if an error occurs while stopping the execution of the
* service bundle in the specified context
*/
public void stop(BundleContext bundleContext)
throws Exception
{
if (serviceRegistration != null)
{
globalShortcutService.stop();
serviceRegistration.unregister();
serviceRegistration = null;
if (logger.isDebugEnabled())
logger.debug("GlobalShortcut Service ... [UNREGISTERED]");
}
bundleContext = null;
}
/**
* Implements the <tt>ServiceListener</tt> method. Verifies whether the
* passed event concerns a <tt>ProtocolProviderService</tt> and adds the
* corresponding UI controls.
*
* @param event The <tt>ServiceEvent</tt> object.
*/
private void serviceChanged(ServiceEvent event)
{
ServiceReference serviceRef = event.getServiceReference();
// if the event is caused by a bundle being stopped, we don't want to
// know
if (serviceRef.getBundle().getState() == Bundle.STOPPING)
{
return;
}
Object service = bundleContext.getService(serviceRef);
// we don't care if the source service is not a protocol provider
if (!(service instanceof ProtocolProviderService))
{
return;
}
switch (event.getType())
{
case ServiceEvent.REGISTERED:
this.handleProviderAdded((ProtocolProviderService) service);
break;
case ServiceEvent.UNREGISTERING:
this.handleProviderRemoved((ProtocolProviderService) service);
break;
}
}
/**
* Get all registered <tt>ProtocolProviderService</tt> and set our listener.
*/
public void registerListenerWithProtocolProviderService()
{
ServiceReference refs[] = null;
try
{
refs = bundleContext.getServiceReferences(
ProtocolProviderService.class.getName(), null);
}
catch (InvalidSyntaxException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
for(ServiceReference ref : refs)
{
ProtocolProviderService service =
(ProtocolProviderService)bundleContext.getService(ref);
OperationSetBasicTelephony<?> opSet =
service.getOperationSet(OperationSetBasicTelephony.class);
opSet.addCallListener(globalShortcutService.getCallShortcut());
}
}
/**
* Notifies this manager that a specific
* <tt>ProtocolProviderService</tt> has been registered as a service.
*
* @param provider the <tt>ProtocolProviderService</tt> which has been
* registered as a service.
*/
private void handleProviderAdded(final ProtocolProviderService provider)
{
OperationSetBasicTelephony<?> opSet =
provider.getOperationSet(OperationSetBasicTelephony.class);
opSet.addCallListener(globalShortcutService.getCallShortcut());
}
/**
* Notifies this manager that a specific
* <tt>ProtocolProviderService</tt> has been unregistered as a service.
*
* @param provider the <tt>ProtocolProviderService</tt> which has been
* unregistered as a service.
*/
private void handleProviderRemoved(ProtocolProviderService provider)
{
OperationSetBasicTelephony<?> opSet =
provider.getOperationSet(OperationSetBasicTelephony.class);
opSet.removeCallListener(globalShortcutService.getCallShortcut());
}
}

@ -0,0 +1,358 @@
/*
* 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.impl.globalshortcut;
import java.util.*;
import java.util.List; // disambiguation
import java.awt.*;
import net.java.sip.communicator.service.globalshortcut.*;
import net.java.sip.communicator.service.keybindings.*;
import net.java.sip.communicator.util.*;
/**
* This global shortcut service permits to register listeners for global
* shortcut (i.e. keystroke even if application is not foreground).
*
* @author Sebastien Vincent
*/
public class GlobalShortcutServiceImpl
implements GlobalShortcutService,
NativeKeyboardHookDelegate
{
/**
* The <tt>Logger</tt> used by the <tt>GlobalShortcutServiceImpl</tt> class
* and its instances for logging output.
*/
private static final Logger logger = Logger.getLogger(
GlobalShortcutServiceImpl.class);
/**
* List of action and its corresponding shortcut.
*/
private final Map<GlobalShortcutListener, List<AWTKeyStroke>> mapActions =
new HashMap<GlobalShortcutListener, List<AWTKeyStroke>>();
/**
* If the service is running or not.
*/
private boolean isRunning = false;
/**
* The <tt>NativeKeyboardHook</tt> that will notify us key press event.
*/
private NativeKeyboardHook keyboardHook = new NativeKeyboardHook();
/**
* Call shortcut to answer/hang up a call.
*/
private final CallShortcut callShortcut = new CallShortcut();
/**
* UI shortcut to display GUI.
*/
private final UIShortcut uiShortcut = new UIShortcut();
/**
* Initializes the <tt>GlobalShortcutServiceImpl</tt>.
*/
public GlobalShortcutServiceImpl()
{
}
/**
* Registers an action to execute when the keystroke is typed.
*
* @param listener listener to notify when keystroke is typed
* @param keyStroke keystroke that will trigger the action
*/
public void registerShortcut(GlobalShortcutListener listener,
AWTKeyStroke keyStroke)
{
List<AWTKeyStroke> keystrokes = mapActions.get(listener);
if(keyStroke == null)
{
return;
}
if(keystrokes != null)
{
if(keyboardHook.registerShortcut(keyStroke.getKeyCode(),
getModifiers(keyStroke)))
{
keystrokes.add(keyStroke);
}
}
else
{
keystrokes = new ArrayList<AWTKeyStroke>();
if(keyboardHook.registerShortcut(keyStroke.getKeyCode(),
getModifiers(keyStroke)))
{
keystrokes.add(keyStroke);
}
}
synchronized(mapActions)
{
mapActions.put(listener, keystrokes);
}
}
/**
* Unregisters an action to execute when the keystroke is typed.
*
* @param listener listener to remove
* @param keyStroke keystroke that will trigger the action
*/
public void unregisterShortcut(GlobalShortcutListener listener,
AWTKeyStroke keyStroke)
{
List<AWTKeyStroke> keystrokes = mapActions.get(listener);
if(keystrokes != null && keyStroke != null)
{
int keycode = keyStroke.getKeyCode();
int modifiers = keyStroke.getModifiers();
AWTKeyStroke ks = null;
for(AWTKeyStroke l : keystrokes)
{
if(l.getKeyCode() == keycode && l.getModifiers() == modifiers)
ks = l;
}
if(ks != null)
{
keystrokes.remove(ks);
}
keyboardHook.unregisterShortcut(keyStroke.getKeyCode(),
getModifiers(keyStroke));
synchronized(mapActions)
{
if(keystrokes.size() == 0)
{
mapActions.remove(listener);
}
else
{
mapActions.put(listener, keystrokes);
}
}
}
}
/**
* Start the service.
*/
public void start()
{
if(!isRunning)
{
keyboardHook.setDelegate(this);
keyboardHook.start();
isRunning = true;
}
}
/**
* Stop the service.
*/
public void stop()
{
Collection<List<AWTKeyStroke>> lst = mapActions.values();
isRunning = false;
for(List<AWTKeyStroke> kss : lst)
{
for(AWTKeyStroke e : kss)
{
unregisterShortcut(callShortcut, e);
unregisterShortcut(uiShortcut, e);
}
}
if(keyboardHook != null)
{
keyboardHook.setDelegate(null);
keyboardHook.stop();
}
}
/**
* Receive a key press event.
*
* @param keycode keycode received
* @param modifiers modifiers received (ALT or CTRL + letter, ...)
*/
public void receiveKey(int keycode, int modifiers)
{
synchronized(mapActions)
{
// compare keycode/modifiers to keystroke
for(Map.Entry<GlobalShortcutListener, List<AWTKeyStroke>> entry :
mapActions.entrySet())
{
List<AWTKeyStroke> lst = entry.getValue();
for(AWTKeyStroke l : lst)
{
if(l.getKeyCode() == keycode &&
getModifiers(l) == modifiers)
{
// notify corresponding listeners
GlobalShortcutEvent evt = new GlobalShortcutEvent(l);
entry.getKey().shortcutReceived(evt);
return;
}
}
}
}
}
/**
* Get our user-defined modifiers.
*
* @param keystroke keystroke
* @return user-defined modifiers
*/
private static int getModifiers(AWTKeyStroke keystroke)
{
int modifiers = keystroke.getModifiers();
int ret = 0;
if((modifiers & java.awt.event.InputEvent.CTRL_DOWN_MASK) > 0)
{
ret |= NativeKeyboardHookDelegate.MODIFIERS_CTRL;
}
if((modifiers & java.awt.event.InputEvent.ALT_DOWN_MASK) > 0)
{
ret |= NativeKeyboardHookDelegate.MODIFIERS_ALT;
}
if((modifiers & java.awt.event.InputEvent.SHIFT_DOWN_MASK) > 0)
{
ret |= NativeKeyboardHookDelegate.MODIFIERS_SHIFT;
}
if((modifiers & java.awt.event.InputEvent.META_DOWN_MASK) > 0)
{
ret |= NativeKeyboardHookDelegate.MODIFIERS_LOGO;
}
return ret;
}
/**
* Reload global shortcuts.
*/
public synchronized void reloadGlobalShortcuts()
{
// unregister all shortcuts
GlobalKeybindingSet set =
GlobalShortcutActivator.getKeybindingsService().getGlobalBindings();
for(Map.Entry<String, List<AWTKeyStroke>> entry :
set.getBindings().entrySet())
{
for(AWTKeyStroke e : entry.getValue())
{
unregisterShortcut(callShortcut, e);
unregisterShortcut(uiShortcut, e);
}
}
// add shortcuts from configuration
for(Map.Entry<String, List<AWTKeyStroke>> entry :
set.getBindings().entrySet())
{
if(entry.getKey().equals("answer") ||
entry.getKey().equals("hangup"))
{
for(AWTKeyStroke e : entry.getValue())
{
registerShortcut(callShortcut, e);
}
}
else if(entry.getKey().equals("contactlist"))
{
for(AWTKeyStroke e : entry.getValue())
{
registerShortcut(uiShortcut, e);
}
}
}
}
/**
* Returns CallShortcut object.
*
* @return CallShortcut object
*/
public CallShortcut getCallShortcut()
{
return callShortcut;
}
/**
* Returns UIShortcut object.
*
* @return UIShortcut object
*/
public UIShortcut getUIShortcut()
{
return uiShortcut;
}
/**
* Simple test.
*/
public void test()
{
GlobalShortcutListener l = new GlobalShortcutListener()
{
public void shortcutReceived(GlobalShortcutEvent evt)
{
System.out.println("global shortcut event");
}
};
AWTKeyStroke ks = AWTKeyStroke.getAWTKeyStroke("control B");
AWTKeyStroke ks2 = AWTKeyStroke.getAWTKeyStroke("control E");
if(ks == null)
{
logger.info("Failed to register keystroke");
System.out.println("failed to register keystroke");
return;
}
this.registerShortcut(l, ks);
this.registerShortcut(l, ks2);
try{Thread.sleep(30000);}catch(InterruptedException e){}
this.unregisterShortcut(l, ks);
try{Thread.sleep(5000);}catch(InterruptedException e){}
this.unregisterShortcut(l, ks2);
/*
boolean ret = keyboardHook.registerShortcut(ks.getKeyCode(),
getModifiers(ks));
System.out.println("finally " + ret);
System.out.println("registered");
try{Thread.sleep(30000);}catch(InterruptedException e){}
System.out.println("unregistered1");
keyboardHook.unregisterShortcut(ks.getKeyCode(),
getModifiers(ks));
System.out.println("unregistered2");
*/
}
}

@ -0,0 +1,171 @@
/*
* 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.impl.globalshortcut;
import net.java.sip.communicator.util.*;
/**
* Native hook for keyboard. It is used to notify a
* <tt>NativeKeyboardHookDelegate</tt> for key (even if key are pressed when
* application is not in foreground).
*
* @author Sebastien Vincent
*/
public class NativeKeyboardHook
{
/**
* The <tt>Logger</tt> used by the <tt>NativeKeyboardHook</tt> class and its
* instances for logging output.
*/
private static final Logger logger = Logger.getLogger(
NativeKeyboardHook.class);
/**
* If it is started.
*/
private boolean isStarted = false;
/**
* Native pointer.
*/
private static long ptr = 0;
/**
* Constructor.
*/
public NativeKeyboardHook()
{
}
/**
* Start the <tt>NativeKeyboardHook</tt>.
*/
public synchronized void start()
{
if(!isStarted && ptr != 0)
{
isStarted = true;
start(ptr);
}
}
/**
* Stop the <tt>NativeKeyboardHook</tt>.
*/
public synchronized void stop()
{
if(isStarted && ptr != 0)
{
isStarted = false;
stop(ptr);
}
}
/**
* Set delegate object for event notification.
*
* @param delegate delegate object
*/
public synchronized void setDelegate(NativeKeyboardHookDelegate delegate)
{
if(ptr != 0)
setDelegate(ptr, delegate);
}
/**
* Register a shortcut.
*
* @param keycode keycode of the shortcut
* @param modifiers modifiers (CTRL, ALT, ...)
* @return true if success, false otherwise
*/
public synchronized boolean registerShortcut(int keycode,
int modifiers)
{
if(ptr != 0)
return registerShortcut(ptr, keycode, modifiers);
return false;
}
/**
* Unregister a shortcut.
*
* @param keycode keycode of the shortcut
* @param modifiers modifiers (CTRL, ALT, ...)
*/
public synchronized void unregisterShortcut(int keycode,
int modifiers)
{
if(ptr != 0)
unregisterShortcut(ptr, keycode, modifiers);
}
/**
* Native method to initialize <tt>NativeKeyboardHook</tt>.
*
* @return native pointer.
*/
private static native long init();
/**
* Native method to start <tt>NativeKeyboardHook</tt>.
*
* @param ptr native pointer
*/
private static native void start(long ptr);
/**
* Native method to stop <tt>NativeKeyboardHook</tt>.
*
* @param ptr native pointer
*/
private static native void stop(long ptr);
/**
* Native method to set the delegate object.
*
* @param ptr native pointer
* @param delegate delegate object to set
*/
private static native void setDelegate(long ptr,
NativeKeyboardHookDelegate delegate);
/**
* Native method to register a shortcut.
*
* @param ptr native pointer
* @param keycode keycode of the shortcut
* @param modifiers modifiers (CTRL, ALT, ...)
*/
private static native boolean registerShortcut(long ptr, int keycode,
int modifiers);
/**
* Native method to unregister a shortcut.
*
* @param ptr native pointer
* @param keycode keycode of the shortcut
* @param modifiers modifiers (CTRL, ALT, ...)
*/
private static native void unregisterShortcut(long ptr, int keycode,
int modifiers);
static
{
try
{
System.loadLibrary("globalshortcut");
ptr = init();
}
catch(Exception e)
{
logger.warn("Failed to load globalshortcut", e);
ptr = 0;
}
}
}

@ -0,0 +1,44 @@
/*
* 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.impl.globalshortcut;
/**
* NativeKeyboardHookDelegate interface.
*
* @author Sebastien Vincent
*/
public interface NativeKeyboardHookDelegate
{
/**
* CTRL modifier.
*/
public static final int MODIFIERS_CTRL = 1;
/**
* ALT modifier.
*/
public static final int MODIFIERS_ALT = 2;
/**
* SHIFT modifier.
*/
public static final int MODIFIERS_SHIFT = 4;
/**
* Logo modifier (i.e. CMD/Apple key on Mac OS X, Windows key on
* MS Windows).
*/
public static final int MODIFIERS_LOGO = 8;
/**
* Receive a key press event.
*
* @param keycode keycode received
* @param modifiers modifiers received (ALT or CTRL + letter, ...)
*/
public void receiveKey(int keycode, int modifiers);
}

@ -0,0 +1,68 @@
/*
* 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.impl.globalshortcut;
import java.awt.*;
import java.util.*;
import java.util.List; // disambiguation
import net.java.sip.communicator.service.globalshortcut.*;
import net.java.sip.communicator.service.gui.*;
import net.java.sip.communicator.service.keybindings.*;
/**
* UI shortcut.
*
* @author Sebastien Vincent
*/
public class UIShortcut
implements GlobalShortcutListener
{
/**
* Keybindings service.
*/
private KeybindingsService keybindingsService =
GlobalShortcutActivator.getKeybindingsService();
/**
* Callback when an shortcut is typed
*
* @param evt <tt>GlobalShortcutEvent</tt>
*/
public void shortcutReceived(GlobalShortcutEvent evt)
{
AWTKeyStroke keystroke = evt.getKeyStroke();
GlobalKeybindingSet set = keybindingsService.getGlobalBindings();
for(Map.Entry<String, List<AWTKeyStroke>> entry :
set.getBindings().entrySet())
{
for(AWTKeyStroke ks : entry.getValue())
{
if(entry.getKey().equals("contactlist") &&
keystroke.getKeyCode() == ks.getKeyCode() &&
keystroke.getModifiers() == ks.getModifiers())
{
ExportedWindow window =
GlobalShortcutActivator.getUIService().
getExportedWindow(ExportedWindow.MAIN_WINDOW);
if(window == null)
return;
window.bringToFront();
window.setVisible(true);
if(window instanceof Window)
{
((Window)window).setAlwaysOnTop(true);
((Window)window).setAlwaysOnTop(false);
}
}
}
}
}
}

@ -0,0 +1,13 @@
Bundle-Activator: net.java.sip.communicator.impl.globalshortcut.GlobalShortcutActivator
Bundle-Name: Global shortcut
Bundle-Description: A bundle which implements global shortcut
Bundle-Vendor: sip-communicator.org
Bundle-Version: 0.0.1
System-Bundle: yes
Import-Package: org.osgi.framework,
net.java.sip.communicator.service.protocol,
net.java.sip.communicator.service.protocol.event,
net.java.sip.communicator.service.keybindings,
net.java.sip.communicator.service.gui,
net.java.sip.communicator.util,
Export-package: net.java.sip.communicator.service.globalshortcut,

@ -48,6 +48,9 @@ public class GuiActivator implements BundleActivator
private static UIServiceImpl uiService = null;
/**
* OSGi bundle context.
*/
public static BundleContext bundleContext;
private static ConfigurationService configService;
@ -172,7 +175,7 @@ public void stop(BundleContext bContext) throws Exception
/**
* Returns all <tt>ProtocolProviderFactory</tt>s obtained from the bundle
* context.
*
*
* @return all <tt>ProtocolProviderFactory</tt>s obtained from the bundle
* context
*/
@ -193,9 +196,9 @@ public void stop(BundleContext bContext) throws Exception
logger.error("LoginManager : " + e);
}
if (serRefs != null)
if (serRefs != null)
{
for (ServiceReference serRef : serRefs)
for (ServiceReference serRef : serRefs)
{
ProtocolProviderFactory providerFactory
= (ProtocolProviderFactory)
@ -332,7 +335,7 @@ public static List<ProtocolProviderService> getRegisteredProviders(
* given <tt>operationSetClass</tt>.
*
* @param opSetClass the operation set class for which we're looking
* for providers
* for providers
* @return a list of all currently registered providers, which support the
* given <tt>operationSetClass</tt>
*/
@ -672,7 +675,7 @@ public static List<ContactSourceService> getContactSources()
logger.error("GuiActivator : " + e);
}
if (serRefs != null)
if (serRefs != null)
{
for (ServiceReference serRef : serRefs)
{
@ -688,7 +691,7 @@ public static List<ContactSourceService> getContactSources()
/**
* Returns all <tt>ReplacementService</tt>s obtained from the bundle
* context.
*
*
* @return all <tt>ReplacementService</tt> implementation obtained from the
* bundle context
*/
@ -726,7 +729,7 @@ public static Map<String, ReplacementService> getReplacementSources()
/**
* Returns the <tt>SmiliesReplacementService</tt> obtained from the bundle
* context.
*
*
* @return the <tt>SmiliesReplacementService</tt> implementation obtained
* from the bundle context
*/
@ -744,7 +747,7 @@ public static SmiliesReplacementService getSmiliesReplacementSource()
/**
* Returns the <tt>PhoneNumberI18nService</tt> obtained from the bundle
* context.
*
*
* @return the <tt>PhoneNumberI18nService</tt> implementation obtained
* from the bundle context
*/
@ -781,6 +784,7 @@ public static SecurityAuthority getSecurityAuthority()
* Returns the <tt>SecurityAuthority</tt> implementation registered to
* handle security authority events.
*
* @param protocolName protocol name
* @return the <tt>SecurityAuthority</tt> implementation obtained
* from the bundle context
*/
@ -896,7 +900,7 @@ public static ProtocolProviderService getPreferredAccount()
// is it the preferred protocol ?
if(wizard.getClass().getName().equals(prefWName))
{
ArrayList<AccountID> registeredAccounts
ArrayList<AccountID> registeredAccounts
= getProtocolProviderFactory(wizard.getProtocolName())
.getRegisteredAccounts();

@ -380,7 +380,7 @@ public void move(int x, int y)
*/
public void bringToFront()
{
if (mainFrame.getState() == Frame.ICONIFIED)
if (mainFrame.getState() == Frame.ICONIFIED)
mainFrame.setState(Frame.NORMAL);
// Because toFront() method gives us no guarantee that our frame would
// go on top we'll try to also first request the focus and set our

@ -92,6 +92,18 @@ public void callStateChanged(CallChangeEvent evt)
if (receivedCallDialog.isVisible())
receivedCallDialog.setVisible(false);
// Ensure that the CallDialog is created, because for now
// it is the one that listens for CallPeers.
Call call = evt.getSourceCall();
if ((evt.getNewValue()
.equals(CallState.CALL_INITIALIZATION)
|| evt.getNewValue()
.equals(CallState.CALL_IN_PROGRESS))
&& activeCalls.get(call) == null)
{
openCallContainer(call);
}
if (evt.getNewValue().equals(CallState.CALL_ENDED))
{
if (evt.getOldValue()
@ -622,6 +634,8 @@ public static void createCall( String callString,
* @param callString the string to call
* @param c the component, which indicates where should be shown the "call
* via" menu if needed
* @param l listener that is notified when the call interface has been
* started after call was created
*/
public static void createCall( String callString,
JComponent c,
@ -1494,7 +1508,7 @@ private static class EnableLocalVideoThread
/**
* Creates the enable local video call thread.
*
* @param call the call, for which to enable/disable
* @param call the call, for which to enable/disable
* @param enable
*/
public EnableLocalVideoThread(Call call, boolean enable)

@ -18,7 +18,7 @@
import com.explodingpixels.macwidgets.*;
/**
*
*
* @author Yana Stamcheva
*/
public abstract class PreCallDialog
@ -160,7 +160,7 @@ private Window createPreCallWindow( String title,
receivedCallWindow.setAlwaysOnTop(true);
// prevents dialog window to get unwanted key events and when going
// on top on linux, it steals focus and if we are accedently
// on top on linux, it steals focus and if we are accidently
// writing something and pressing enter a call get answered
receivedCallWindow.setFocusableWindowState(false);

@ -37,6 +37,11 @@ public class ContactList
implements MetaContactListListener,
MouseListener
{
/**
* Serial version UID.
*/
private static final long serialVersionUID = 0L;
private static final String ADD_OPERATION = "AddOperation";
private static final String REMOVE_OPERATION = "RemoveOperation";
@ -392,7 +397,7 @@ public void fireContactListEvent(Object source, int eventID, int clickCount)
*
* @param sourceContact the contact that this event is about
* @param eventID the id indicating the exact type of the event to fire.
* @param clickCount
* @param clickCount
*/
public void fireContactListEvent( MetaContact sourceContact,
int eventID,
@ -404,7 +409,7 @@ public void fireContactListEvent( MetaContact sourceContact,
}
/**
*
*
* @param contactListListeners
* @param event
*/
@ -1273,7 +1278,7 @@ public void run()
}
catch (MetaContactListException e)
{
}
return;

@ -18,7 +18,6 @@
import net.java.sip.communicator.impl.gui.event.*;
import net.java.sip.communicator.impl.gui.main.*;
import net.java.sip.communicator.impl.gui.main.chat.*;
import net.java.sip.communicator.impl.gui.utils.*;
import net.java.sip.communicator.service.contacteventhandler.*;
import net.java.sip.communicator.service.contactlist.*;
import net.java.sip.communicator.service.gui.*;
@ -45,6 +44,11 @@ public class ContactListPane
ContactListListener,
PluginComponentListener
{
/**
* Serial version UID.
*/
private static final long serialVersionUID = 0L;
private final MainFrame mainFrame;
private TreeContactList contactList;
@ -549,6 +553,11 @@ public CommonRightButtonMenu getCommonRightButtonMenu()
*/
private class TypingTimer extends Timer
{
/**
* Serial version UID.
*/
private static final long serialVersionUID = 0L;
private MetaContact metaContact;
public TypingTimer()

@ -0,0 +1,53 @@
/*
* 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.impl.keybindings;
import java.awt.*;
import java.util.*;
import java.util.List;
import net.java.sip.communicator.service.keybindings.*;
/**
* Global keybinding set.
*
* @author Sebastien Vincent
*/
public class GlobalKeybindingSetImpl
implements GlobalKeybindingSet
{
/**
* List of bindings (name and list of different keystroke that would
* trigger the action).
*/
private Map<String, List<AWTKeyStroke>> bindings = new
LinkedHashMap<String, List<AWTKeyStroke>>();
/**
* Provides current keybinding mappings.
* @return mapping of keystrokes to the string representation of the actions
* they perform
*/
public Map<String, List<AWTKeyStroke>> getBindings()
{
return new LinkedHashMap<String, List<AWTKeyStroke>>(this.bindings);
}
/**
* Resets the bindings and notifies the observer's listeners if they've
* changed.
* @param bindings new keybindings to be held
*/
public void setBindings(Map<String, List<AWTKeyStroke>> bindings)
{
if (!this.bindings.equals(bindings))
{
this.bindings.clear();
this.bindings.putAll(bindings);
}
}
}

@ -6,6 +6,7 @@
*/
package net.java.sip.communicator.impl.keybindings;
import net.java.sip.communicator.service.configuration.*;
import net.java.sip.communicator.service.keybindings.*;
import net.java.sip.communicator.util.*;
@ -32,6 +33,16 @@ public class KeybindingsActivator
*/
private KeybindingsServiceImpl keybindingsService = null;
/**
* Reference to the configuration service
*/
private static ConfigurationService configService;
/**
* OSGi bundle context.
*/
private static BundleContext bundleContext = null;
/**
* Called when this bundle is started.
*
@ -41,6 +52,8 @@ public void start(BundleContext context)
{
if (this.keybindingsService == null)
{
bundleContext = context;
if (logger.isDebugEnabled())
logger.debug("Service Impl: " + getClass().getName()
+ " [ STARTED ]");
@ -65,4 +78,25 @@ public void stop(BundleContext context)
this.keybindingsService = null;
}
}
/**
* Returns a reference to a ConfigurationService implementation currently
* registered in the bundle context or null if no such implementation was
* found.
*
* @return a currently valid implementation of the ConfigurationService.
*/
public static ConfigurationService getConfigService()
{
if(configService == null)
{
ServiceReference confReference
= bundleContext.getServiceReference(
ConfigurationService.class.getName());
configService
= (ConfigurationService) bundleContext.getService(
confReference);
}
return configService;
}
}

@ -6,14 +6,17 @@
*/
package net.java.sip.communicator.impl.keybindings;
import java.awt.*;
import java.io.*;
import java.text.*;
import java.util.*;
import java.util.List; //disambiguation
import javax.swing.*;
import org.osgi.framework.*;
import net.java.sip.communicator.service.configuration.*;
import net.java.sip.communicator.service.fileaccess.*;
import net.java.sip.communicator.service.keybindings.*;
import net.java.sip.communicator.util.*;
@ -54,6 +57,12 @@ class KeybindingsServiceImpl
*/
private static final String CUSTOM_KEYBINDING_DIR = "keybindings";
/**
* Path where to store the global shortcuts.
*/
private final static String CONFIGURATION_PATH =
"net.java.sip.communicator.impl.keybinding.global";
/**
* Flag indicating if service is running.
*/
@ -65,6 +74,11 @@ class KeybindingsServiceImpl
private final HashMap<KeybindingSet.Category, KeybindingSetImpl> bindings =
new HashMap<KeybindingSet.Category, KeybindingSetImpl>();
/**
* Loaded global keybinding mappings.
*/
private GlobalKeybindingSet globalBindings = null;
/**
* Starts the KeybindingService, for each keybinding category retrieving the
* default bindings then overwriting them with any custom bindings that can
@ -216,6 +230,9 @@ synchronized void start(BundleContext bc)
new KeybindingSetImpl(merged, category, customFile);
this.bindings.put(category, newSet);
newSet.addObserver(this);
globalBindings = new GlobalKeybindingSetImpl();
globalBindings.setBindings(getGlobalShortcutFromConfiguration());
}
this.isRunning = true;
@ -295,4 +312,130 @@ public void update(Observable obs, Object arg)
}
}
}
/**
* Returns list of global shortcuts from the configuration file.
*
* @return list of global shortcuts.
*/
public Map<String, List<AWTKeyStroke>> getGlobalShortcutFromConfiguration()
{
Map<String, List<AWTKeyStroke>> gBindings = new
LinkedHashMap<String, List<AWTKeyStroke>>();
ConfigurationService configService =
KeybindingsActivator.getConfigService();
List<AWTKeyStroke> kss = new ArrayList<AWTKeyStroke>();
String name = null;
String shortcut = null;
String shortcut2 = null;
String propName = null;
String propName2 = null;
name = "answer";
propName = CONFIGURATION_PATH + ".answer.1";
propName2 = CONFIGURATION_PATH + ".answer.2";
shortcut = propName != null ?
(String)configService.getProperty(propName) : null;
shortcut2 = propName2 != null ?
(String)configService.getProperty(propName2) : null;
if(shortcut != null)
{
kss.add(AWTKeyStroke.getAWTKeyStroke(shortcut));
}
if(shortcut2 != null)
{
kss.add(AWTKeyStroke.getAWTKeyStroke(shortcut2));
}
gBindings.put(name, kss);
name = "hangup";
propName = CONFIGURATION_PATH + ".hangup.1";
propName2 = CONFIGURATION_PATH + ".hangup.2";
shortcut = propName != null ?
(String)configService.getProperty(propName) : null;
shortcut2 = propName2 != null ?
(String)configService.getProperty(propName2) : null;
kss = new ArrayList<AWTKeyStroke>();
if(shortcut != null)
{
kss.add(AWTKeyStroke.getAWTKeyStroke(shortcut));
}
if(shortcut2 != null)
{
kss.add(AWTKeyStroke.getAWTKeyStroke(shortcut2));
}
gBindings.put(name, kss);
name = "contactlist";
propName = CONFIGURATION_PATH + ".contactlist.1";
propName2 = CONFIGURATION_PATH + ".contactlist.2";
shortcut = propName != null ?
(String)configService.getProperty(propName) : null;
shortcut2 = propName2 != null ?
(String)configService.getProperty(propName2) : null;
kss = new ArrayList<AWTKeyStroke>();
if(shortcut != null)
{
kss.add(AWTKeyStroke.getAWTKeyStroke(shortcut));
}
if(shortcut2 != null)
{
kss.add(AWTKeyStroke.getAWTKeyStroke(shortcut2));
}
gBindings.put(name, kss);
return gBindings;
}
/**
* Save the configuration file.
*/
public void saveGlobalShortcutFromConfiguration()
{
ConfigurationService configService =
KeybindingsActivator.getConfigService();
String shortcut = null;
String shortcut2 = null;
for(Map.Entry<String, List<AWTKeyStroke>> entry :
globalBindings.getBindings().entrySet())
{
String key = entry.getKey();
List<AWTKeyStroke> kss = entry.getValue();
String path = CONFIGURATION_PATH;
if(key.equals("answer"))
{
path += ".answer";
}
else if(key.equals("hangup"))
{
path += ".hangup";
}
else if(key.equals("contactlist"))
{
path += ".contactlist";
}
shortcut = path + ".1";
shortcut2 = path + ".2";
configService.setProperty(shortcut, kss.size() > 0 ?
kss.get(0) : null);
configService.setProperty(shortcut2, kss.size() > 1 ?
kss.get(1) : null);
}
}
/**
* Provides the bindings associated with the global category.
*
* @return global keybinding set
*/
public GlobalKeybindingSet getGlobalBindings()
{
return globalBindings;
}
}

@ -8,5 +8,6 @@ Import-Package: org.osgi.framework,
net.java.sip.communicator.util,
net.java.sip.communicator.service.fileaccess,
net.java.sip.communicator.service.gui,
net.java.sip.communicator.service.configuration,
javax.swing
Export-Package: net.java.sip.communicator.service.keybindings

@ -8,7 +8,10 @@
import java.util.*;
import net.java.sip.communicator.service.configuration.*;
import net.java.sip.communicator.service.globalshortcut.*;
import net.java.sip.communicator.service.gui.*;
import net.java.sip.communicator.service.keybindings.*;
import net.java.sip.communicator.service.resources.*;
import net.java.sip.communicator.util.*;
@ -16,7 +19,7 @@
/**
* Enabling and disabling osgi functionality for the keybinding chooser.
*
*
* @author Damian Johnson
*/
public class KeybindingChooserActivator
@ -38,6 +41,21 @@ public class KeybindingChooserActivator
*/
public static ResourceManagementService resourcesService;
/**
* Reference to the configuration service
*/
private static ConfigurationService configService;
/**
* Reference to the keybinding service
*/
private static KeybindingsService keybindingService = null;
/**
* Reference to the global shortcut service
*/
private static GlobalShortcutService globalShortcutService = null;
/**
* Starts this bundle and adds the
* <td>KeybindingsConfigPanel</tt> contained in it to the configuration
@ -93,4 +111,67 @@ public static ResourceManagementService getResources()
ResourceManagementServiceUtils.getService(bundleContext);
return resourcesService;
}
/**
* Returns a reference to a ConfigurationService implementation currently
* registered in the bundle context or null if no such implementation was
* found.
*
* @return a currently valid implementation of the ConfigurationService.
*/
public static ConfigurationService getConfigService()
{
if(configService == null)
{
ServiceReference confReference
= bundleContext.getServiceReference(
ConfigurationService.class.getName());
configService
= (ConfigurationService) bundleContext.getService(
confReference);
}
return configService;
}
/**
* Returns a reference to a KeybindingsService implementation currently
* registered in the bundle context or null if no such implementation was
* found.
*
* @return a currently valid implementation of the KeybindingsService.
*/
public static KeybindingsService getKeybindingsService()
{
if(keybindingService == null)
{
ServiceReference keybindingReference
= bundleContext.getServiceReference(
KeybindingsService.class.getName());
keybindingService
= (KeybindingsService) bundleContext.getService(
keybindingReference);
}
return keybindingService;
}
/**
* Returns a reference to a GlobalShortcutService implementation currently
* registered in the bundle context or null if no such implementation was
* found.
*
* @return a currently valid implementation of the GlobalShortcutService.
*/
public static GlobalShortcutService getGlobalShortcutService()
{
if(globalShortcutService == null)
{
ServiceReference globalShortcutReference
= bundleContext.getServiceReference(
GlobalShortcutService.class.getName());
globalShortcutService
= (GlobalShortcutService) bundleContext.getService(
globalShortcutReference);
}
return globalShortcutService;
}
}

@ -15,13 +15,14 @@
import org.osgi.framework.*;
import net.java.sip.communicator.plugin.keybindingchooser.chooser.*;
import net.java.sip.communicator.plugin.keybindingchooser.globalchooser.*;
import net.java.sip.communicator.service.keybindings.*;
import net.java.sip.communicator.util.swing.*;
/**
* The <tt>ConfigurationForm</tt> that would be added to the settings
* configuration to configure the application keybindings.
*
*
* @author Damian Johnson
* @author Lubomir Marinov
*/
@ -44,6 +45,9 @@ private static KeybindingsService getKeybindingsService()
private final HashMap<KeybindingSet, SIPChooser> choosers =
new HashMap<KeybindingSet, SIPChooser>();
/**
* Constructor.
*/
public KeybindingsConfigPanel()
{
super(new BorderLayout());
@ -74,7 +78,7 @@ public void focusLost(FocusEvent event)
continue; // defaults failed to load
SIPChooser newChooser = new SIPChooser();
newChooser.putAllBindings(bindingSet.getBindings());
newChooser.putAllBindings(bindingSet);
JPanel chooserWrapper = new TransparentPanel(new BorderLayout());
chooserWrapper.add(newChooser, BorderLayout.NORTH);
@ -88,25 +92,12 @@ public void focusLost(FocusEvent event)
this.choosers.put(bindingSet, newChooser);
}
add(chooserPanes);
JButton apply = new JButton(
KeybindingChooserActivator.getResources()
.getI18NString("service.gui.APPLY"));
// global shortcut
GlobalShortcutConfigForm globalBindingPanel =
new GlobalShortcutConfigForm();
chooserPanes.addTab("Global shortcut", globalBindingPanel);
apply.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent event)
{
for (KeybindingSet set : choosers.keySet())
set.setBindings(choosers.get(set).getBindingMap());
}
});
JPanel bottomWrapper =
new TransparentPanel(new FlowLayout(FlowLayout.RIGHT));
bottomWrapper.add(apply);
add(bottomWrapper, BorderLayout.SOUTH);
add(chooserPanes);
}
/**
@ -114,7 +105,7 @@ public void actionPerformed(ActionEvent event)
* underscores and this changes the input to lowercase except the first
* letter of each word. For instance, "RARE_CARDS" would become "Rare
* Cards".
*
*
* @param input string to be converted
* @return reader friendly variant of constant name
*/
@ -188,7 +179,7 @@ public boolean putBinding(BindingEntry newEntry, int index)
* given in its plugin-specific format. The key is translated to the
* global format of the ReouseceManagementService and the translated key
* is used to retrieve the string from the resource files.
*
*
* @param key the key of the string to be retrieved given in its
* plugin-specific format
* @return the internationalized string corresponding to a specific key

@ -1,3 +1,9 @@
/*
* 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.chooser;
import java.awt.event.*;
@ -9,7 +15,7 @@
* BindingChooser. This can be added to focused components to provide editing
* functionality for this chooser. This prevents duplicate entries and varies
* how it captures input according to the type of key event its set to capture.
*
*
* @author Damian Johnson (atagar1@gmail.com)
* @version August 19, 2007
*/
@ -36,7 +42,7 @@ public class BindingAdaptor
/**
* Provides if bindings are currently disableable via generated key adaptors
* or not.
*
*
* @return true if input can disable bindings, false otherwise.
*/
public boolean isBindingDisablingEnabled()
@ -47,7 +53,7 @@ public boolean isBindingDisablingEnabled()
/**
* Sets if bindings can be disabled via generated key adaptors with the
* disabling key code. By default this is false.
*
*
* @param enable if true then input can disable bindings, otherwise bindings
* may not be disabled
*/
@ -59,7 +65,7 @@ public void setBindingsDisableable(boolean enable)
/**
* Provides the keycode that can be input to generated key adaptors to
* disable key bindings.
*
*
* @return keycode of disabling input
*/
public int getDisablingKeyCode()
@ -72,7 +78,7 @@ public int getDisablingKeyCode()
* from returned mappings) via generated key adaptors. This only works if
* the set event type is KEY_PRESSED or KEY_RELEASED since KEY_TYPED events
* fail to provide keycodes. By default this is VK_ESCAPE.
*
*
* @param keycode keycode that sets selected entry to a disabled state
*/
public void setDisablingKeyCode(int keycode)
@ -83,7 +89,7 @@ public void setDisablingKeyCode(int keycode)
/**
* Provides the type of keystroke registered by input via generated key
* adaptors.
*
*
* @return type of input detected by generated key adaptors
*/
public int getInputEventType()
@ -95,7 +101,7 @@ public int getInputEventType()
* Sets the type of keystroke registered by input via generated key adaptors
* (by default KeyEvent.KEY_PRESSED). This must be a valid type of key event
* which includes: KEY_PRESSED, KEY_RELEASED, or KEY_TYPED.
*
*
* @param type type of keystroke registered by input
* @throws IllegalArgumentException if type doesn't match a valid key event
*/

@ -1,3 +1,9 @@
/*
* 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.chooser;
import java.awt.*;
@ -6,6 +12,7 @@
import javax.swing.*;
import net.java.sip.communicator.service.keybindings.*;
import net.java.sip.communicator.util.swing.*;
/**
@ -17,7 +24,7 @@
* particularly recommended unless automated changes to the appearance (the
* indentation style and color scheme) are disabled since they may be
* unexpectedly reverted or clash any alterations made.
*
*
* @author Damian Johnson (atagar1@gmail.com)
* @version September 1, 2007
*/
@ -35,13 +42,18 @@ public class BindingChooser
private String selectedText = "Press shortcut...";
/**
* Keybinding set.
*/
private KeybindingSet set = null;
/**
* Displays a dialog allowing the user to redefine the keystroke component
* of key bindings. The top has light blue labels describing the fields and
* the bottom provides an 'OK' and 'Cancel' option. This uses the default
* color scheme and indent style. If no entries are selected then the enter
* key is equivalent to pressing 'OK' and escape is the same as 'Cancel'.
*
*
* @param parent frame to which to apply modal property and center within
* (centers within screen if null)
* @param bindings initial mapping of keystrokes to their actions
@ -57,6 +69,20 @@ public static LinkedHashMap<KeyStroke, String> showDialog(Component parent,
.makeAdaptor());
}
/**
* Adds a collection of new key binding mappings to the end of the listing.
* If any shortcuts are already contained then the previous entries are
* replaced (not triggering the onUpdate method). Disabled shortcuts trigger
* replacement on duplicate actions instead.
*
* @param set mapping between keystrokes and actions to be added
*/
public void putAllBindings(KeybindingSet set)
{
this.set = set;
putAllBindings(set.getBindings());
}
/**
* Displays a dialog allowing the user to redefine the keystroke component
* of key bindings. The bottom provides an 'OK' and 'Cancel' option. If no
@ -67,7 +93,7 @@ public static LinkedHashMap<KeyStroke, String> showDialog(Component parent,
* setting the selected shortcut field. Also note that labels use the
* default entry size and should be omitted if using content with custom
* dimensions.
*
*
* @param parent frame to which to apply modal property and center within
* (centers within screen if null)
* @param display body of the display, containing current bindings and
@ -185,7 +211,7 @@ protected void onClick(MouseEvent event, BindingEntry entry,
/**
* Sets if the shortcut fields of entries can be selected to provide editing
* functionality or not. If false, any selected entry is deselected.
*
*
* @param editable if true shortcut fields may be selected to have their
* values changed, otherwise user input and calls to the
* setSelected method are ignored
@ -201,7 +227,7 @@ public void setEditable(boolean editable)
/**
* Provides the indent style used by the chooser.
*
*
* @return type of content in the indent field
*/
public IndentStyle getIndentStyle()
@ -212,7 +238,7 @@ public IndentStyle getIndentStyle()
/**
* Sets content display in the indent field of entries. This will prompt an
* onUpdate on all entries unless setting the style to NONE.
*
*
* @param style type of content displayed in entry's indent field
*/
public void setIndentStyle(IndentStyle style)
@ -231,7 +257,7 @@ public void setIndentStyle(IndentStyle style)
/**
* Sets the message of the selected shortcut field when awaiting user input.
* By default this is "Press shortcut...".
*
*
* @param message prompt for user input
*/
public void setSelectedText(String message)
@ -246,7 +272,7 @@ public void setSelectedText(String message)
/**
* Returns if a binding is currently awaiting input or not.
*
*
* @return true if a binding is awaiting input, false otherwise
*/
public boolean isBindingSelected()
@ -256,7 +282,7 @@ public boolean isBindingSelected()
/**
* Provides the currently selected entry if awaiting input.
*
*
* @return entry currently awaiting input, if one exists
*/
public BindingEntry getSelected()
@ -270,7 +296,7 @@ public BindingEntry getSelected()
* currently selected entry is deselected. If null, then this simply reverts
* any selections (leaving no entry selected). The onUpdate method is called
* whenever an entry is either selected or deselected.
*
*
* @param entry binding entry awaiting input for its shortcut field
* @throws IllegalArgumentException if entry is not contained in chooser
*/
@ -311,7 +337,7 @@ public void setSelected(BindingEntry entry)
/**
* Provides a key adaptor that can provide editing functionality for the
* selected entry.
*
*
* @return binding adaptor configured to this chooser
*/
public BindingAdaptor makeAdaptor()
@ -363,7 +389,7 @@ else if (field == BindingEntry.Field.SHORTCUT)
/**
* Emulates keyboard input, setting the selected entry's shortcut if an
* entry's currently awaiting input.
*
*
* @param input keystroke input for selected entry
*/
void doInput(KeyStroke input)
@ -371,6 +397,8 @@ void doInput(KeyStroke input)
if (isBindingSelected())
{
this.selectedEntry.setShortcut(input);
//apply configuration
set.setBindings(this.getBindingMap());
// TYPE indent can change according to the shortcut
// this.indentStyle.apply(this.selectedEntry,
@ -408,7 +436,7 @@ public static enum IndentStyle
/**
* Returns the enum representation of a string. This is case sensitive.
*
*
* @param str toString representation of this enum
* @return enum associated with a string
* @throws IllegalArgumentException if argument is not represented by
@ -480,7 +508,7 @@ public String toString()
* underscores and this changes the input to lowercase except the first
* letter of each word. For instance, "RARE_CARDS" would become
* "Rare Cards".
*
*
* @param input string to be converted
* @return reader friendly variant of constant name
*/

@ -1,3 +1,9 @@
/*
* 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.chooser;
import java.awt.*;
@ -9,7 +15,7 @@
/**
* Display element for a single key binding.
*
*
* @author Damian Johnson (atagar1@gmail.com)
* @version August 7, 2007
*/
@ -130,7 +136,7 @@ public boolean isDisabled()
/**
* Provides the label associated with a field.
*
*
* @param field element of display to be returned
* @return label associated with field
*/
@ -165,7 +171,7 @@ public enum Field
* the representation are unspecified and subject to change but the
* following format can be considered to be typical:<br>
* "BindingEntry (" + Shortcut + " \u2192 " + Action + ")"
*
*
* @return string representation of entry
*/
@Override
@ -188,7 +194,7 @@ public String toString()
* Checks if argument is an instance of this class with the same shortcut
* and associated action. It does not compare aspects of the display
* elements.
*
*
* @param obj element with which to be compared
* @return true if argument is an instance of this class with matching
* shortcut and action, false otherwise

@ -1,3 +1,9 @@
/*
* 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.chooser;
import java.awt.*;
@ -22,7 +28,7 @@
* shortcuts aren't supported. An exception is made in the case of disabled
* shortcuts, but to keep mappings unique duplicate actions among disabled
* entries are not permitted.
*
*
* @author Damian Johnson (atagar1@gmail.com)
* @version September 1, 2007
*/
@ -35,7 +41,7 @@ public abstract class BindingPanel
* Method called whenever an entry is either added or shifts in the display.
* For instance, if the second entry is removed then this is called on the
* third to last elements.
*
*
* @param index newly assigned index of entry
* @param entry entry that has been added or shifted
* @param isNew if true the entry is new to the display, false otherwise
@ -45,7 +51,7 @@ protected abstract void onUpdate(int index, BindingEntry entry,
/**
* Method called upon any mouse clicks within a BindingEntry in the display.
*
*
* @param event fired mouse event that triggered method call
* @param entry entry on which the click landed
* @param field field of entry on which the click landed, null if not a
@ -66,13 +72,13 @@ public BindingPanel()
* triggering the onUpdate method). Disabled shortcuts trigger replacement
* on duplicate actions instead. This uses the normal parameters used to
* generate key stokes, such as:
*
*
* <pre>
* bindingPanel.putBinding('Y', 0, &quot;Confirm Selection&quot;);
* bindingPanel.putBinding(KeyEvent.VK_DELETE, KeyEvent.CTRL_MASK
* | KeyEvent.ALT_MASK, &quot;Kill Process&quot;);
* </pre>
*
*
* @param keyCode key code of keystroke component of mapping
* @param modifier modifiers of keystroke component of mapping
* @param action string component of mapping
@ -88,7 +94,7 @@ public boolean putBinding(int keyCode, int modifier, String action)
* contains the shortcut then the previous entry is replaced instead (not
* triggering the onUpdate method). Disabled shortcuts trigger replacement
* on duplicate actions instead.
*
*
* @param shortcut keystroke component of mapping
* @param action string component of mapping
* @return true if contents did not already include shortcut
@ -103,7 +109,7 @@ public boolean putBinding(KeyStroke shortcut, String action)
* this already contains the shortcut then the previous entry is replaced
* instead (not triggering the onUpdate method). Disabled shortcuts trigger
* replacement on duplicate actions instead.
*
*
* @param shortcut keystroke component of mapping
* @param action string component of mapping
* @param index location in which to insert mapping
@ -121,7 +127,7 @@ public boolean putBinding(KeyStroke shortcut, String action, int index)
* this already contains the shortcut then the previous entry is replaced
* instead (not triggering the onUpdate method). Disabled shortcuts trigger
* replacement on duplicate actions instead.
*
*
* @param newEntry entry to add to contents
* @param index location in which to insert mapping
* @return true if contents did not already include shortcut
@ -198,7 +204,7 @@ public boolean putBinding(BindingEntry newEntry, int index)
* If any shortcuts are already contained then the previous entries are
* replaced (not triggering the onUpdate method). Disabled shortcuts trigger
* replacement on duplicate actions instead.
*
*
* @param bindings mapping between keystrokes and actions to be added
*/
public void putAllBindings(Map<KeyStroke, String> bindings)
@ -211,7 +217,7 @@ public void putAllBindings(Map<KeyStroke, String> bindings)
/**
* Removes a particular binding from the contents.
*
*
* @param entry binding to be removed
* @return true if binding was in the contents, false otherwise
*/
@ -226,7 +232,7 @@ public boolean removeBinding(BindingEntry entry)
/**
* Removes the binding at a particular index of the listing.
*
*
* @param index from which to remove entry
* @return the entry that was removed from the contents
* @throws IndexOutOfBoundsException if index is out of range (index < 0 ||
@ -269,7 +275,7 @@ public void clearBindings()
* Returns if a keystroke is in the panel's current contents. This provides
* a preemptive means of checking if adding a non-disabled shortcut would
* cause a replacement.
*
*
* @param shortcut keystroke to be checked against contents
* @return true if contents includes the shortcut, false otherwise
*/
@ -293,7 +299,7 @@ public boolean contains(KeyStroke shortcut)
/**
* Provides number of key bindings currently present.
*
*
* @return number of key bindings in the display
*/
public int getBindingCount()
@ -303,7 +309,7 @@ public int getBindingCount()
/**
* Provides the index of a particular entry.
*
*
* @param entry entry for which the index should be returned
* @return entry index, -1 if not found
*/
@ -314,7 +320,7 @@ public int getBindingIndex(BindingEntry entry)
/**
* Provides a binding at a particular index.
*
*
* @param index index from which to retrieve binding.
* @return the entry at the specified position in this list
*/
@ -325,7 +331,7 @@ public BindingEntry getBinding(int index)
/**
* Provides listing of the current keybinding entries.
*
*
* @return list of current entry contents
*/
public ArrayList<BindingEntry> getBindings()
@ -336,7 +342,7 @@ public ArrayList<BindingEntry> getBindings()
/**
* Provides the mapping between keystrokes and actions represented by the
* contents of the display. Disabled entries aren't included in the mapping.
*
*
* @return mapping between contained keystrokes and their associated actions
*/
public LinkedHashMap<KeyStroke, String> getBindingMap()
@ -356,7 +362,7 @@ public LinkedHashMap<KeyStroke, String> getBindingMap()
/**
* Provides an input map associating keystrokes to actions according to the
* contents of the display. Disabled entries aren't included in the mapping.
*
*
* @return input mapping between contained keystrokes and their associated
* actions
*/

@ -0,0 +1,311 @@
/*
* 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;
import javax.swing.*;
import javax.swing.event.*;
import net.java.sip.communicator.plugin.keybindingchooser.*;
import net.java.sip.communicator.service.globalshortcut.*;
import net.java.sip.communicator.service.keybindings.*;
import net.java.sip.communicator.util.*;
import net.java.sip.communicator.util.swing.*;
/**
* This ConfigurationForm shows the list of global shortcut
*
* @author Sebastien Vincent
*/
public class GlobalShortcutConfigForm
extends TransparentPanel
implements ListSelectionListener
{
/**
* Serial version UID.
*/
private static final long serialVersionUID = 0L;
/**
* The logger for this class.
*/
private static Logger logger = Logger.getLogger(
GlobalShortcutConfigForm.class);
/**
* Displays the registered shortcuts.
*/
private JTable shortcutsTable = new JTable();
/**
* Contains the shortcutsTable.
*/
private JScrollPane scrollPane = new JScrollPane();
/**
* Contains listPanel.
*/
private JPanel mainPanel = this;
/**
* Model for the shortcutsTable
*/
private GlobalShortcutTableModel tableModel =
new GlobalShortcutTableModel();
/**
* Current selected row.
*/
private int currentRow = -1;
/**
* Current selected row.
*/
private int currentColumn = -1;
/**
* Constructor
*/
public GlobalShortcutConfigForm()
{
super(new BorderLayout());
logger.trace("New global shortcut configuration form.");
this.initComponents();
}
/**
* Initialize the swing components.
*/
private void initComponents()
{
shortcutsTable.setRowHeight(22);
shortcutsTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
shortcutsTable.setShowHorizontalLines(false);
shortcutsTable.setShowVerticalLines(false);
shortcutsTable.setModel(tableModel);
shortcutsTable.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
shortcutsTable.addMouseListener(new MouseAdapter()
{
@Override
public void mouseClicked(MouseEvent e)
{
if(e.getClickCount() >= 1)
{
int row = GlobalShortcutConfigForm.this.shortcutsTable.
getSelectedRow();
int column = GlobalShortcutConfigForm.this.shortcutsTable.
getSelectedColumn();
if(row >= 0 && column >= 1)
{
currentRow = row;
currentColumn = column;
}
}
}
});
shortcutsTable.addKeyListener(new KeyAdapter()
{
private KeyEvent buffer = null;
@Override
public void keyPressed(KeyEvent event)
{
// Reports KEY_PRESSED events on release to support modifiers
this.buffer = event;
}
@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>();
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;
}
en.setShortcuts(kss);
GlobalShortcutConfigForm.this.refresh();
GlobalShortcutConfigForm.this.saveConfig();
}
}
}
});
scrollPane.getViewport().add(this.shortcutsTable);
mainPanel.add(this.scrollPane, BorderLayout.CENTER);
mainPanel.setPreferredSize(new Dimension(500, 400));
shortcutsTable.getSelectionModel().addListSelectionListener(this);
loadConfig();
}
/**
* Loads configuration.
*/
private void loadConfig()
{
KeybindingsService keybindingService =
KeybindingChooserActivator.getKeybindingsService();
GlobalKeybindingSet set = keybindingService.getGlobalBindings();
for(Map.Entry<String, List<AWTKeyStroke>> entry :
set.getBindings().entrySet())
{
String key = entry.getKey();
List<AWTKeyStroke> kss = entry.getValue();
GlobalShortcutEntry gke = null;
String desc = null;
if(key.equals("answer"))
{
desc = Resources.getString(
"plugin.keybindings.globalchooser.ANSWER_CALL");
}
else if(key.equals("hangup"))
{
desc = Resources.getString(
"plugin.keybindings.globalchooser.HANGUP_CALL");
}
else if(key.equals("contactlist"))
{
desc = Resources.getString(
"plugin.keybindings.globalchooser.SHOW_CONTACTLIST");
}
else
continue;
gke = new GlobalShortcutEntry(desc, kss);
tableModel.addEntry(gke);
}
refresh();
}
/**
* Save configuration.
*/
public void saveConfig()
{
KeybindingsService keybindingService =
KeybindingChooserActivator.getKeybindingsService();
GlobalShortcutService globalShortcutService =
KeybindingChooserActivator.getGlobalShortcutService();
Map<String, List<AWTKeyStroke>> gBindings =
keybindingService.getGlobalBindings().getBindings();
List<GlobalShortcutEntry> entries = tableModel.getEntries();
List<AWTKeyStroke> kss = null;
for(GlobalShortcutEntry entry : entries)
{
String desc = null;
if(entry.getAction().equals(Resources.getString(
"plugin.keybindings.globalchooser.ANSWER_CALL")))
{
desc = "answer";
}
else if(entry.getAction().equals(Resources.getString(
"plugin.keybindings.globalchooser.HANGUP_CALL")))
{
desc = "hangup";
}
else if(entry.getAction().equals(Resources.getString(
"plugin.keybindings.globalchooser.SHOW_CONTACTLIST")))
{
desc = "contactlist";
}
else
continue;
kss = gBindings.get(desc);
kss.clear();
kss.add(entry.getShortcut());
kss.add(entry.getShortcut2());
gBindings.put(desc, kss);
}
keybindingService.saveGlobalShortcutFromConfiguration();
globalShortcutService.reloadGlobalShortcuts();
}
/**
* Required by ListSelectionListener.
*
* @param e event triggered
*/
public void valueChanged(ListSelectionEvent e)
{
if(shortcutsTable.getSelectedRow() == -1)
{
currentRow = -1;
currentColumn = -1;
}
}
/**
* refreshes the table display
*/
private void refresh()
{
tableModel.fireTableStructureChanged();
}
/**
* Indicates if this is an advanced configuration form.
* @return <tt>true</tt> if this is an advanced configuration form,
* otherwise it returns <tt>false</tt>
*/
public boolean isAdvanced()
{
return true;
}
/**
* Add bindings.
*
* @param bindings list of bindings
*
public void addBindings(Map<String, List<AWTKeyStroke>> bindings)
{
for(Map.Entry<String, List<AWTKeyStroke>> entry : bindings.entrySet())
{
GlobalShortcutEntry e = new GlobalShortcutEntry(
entry.getKey(), entry.getValue());
tableModel.addEntry(e);
}
refresh();
}
*/
}

@ -0,0 +1,248 @@
/*
* 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.util.List; //disambiguation
import java.awt.*;
import java.awt.event.*;
/**
* Entry for a global shortcut.
*
* @author Sebastien Vincent
*/
public class GlobalShortcutEntry
{
/**
* Serial version UID.
*/
private static final long serialVersionUID = 0L;
/**
* Disabled keystroke.
*/
private static final AWTKeyStroke DISABLED = null;
/**
* Action name.
*/
private String action = null;
/**
* Primary shortcut.
*/
private AWTKeyStroke shortcut = null;
/**
* Second shortcut.
*/
private AWTKeyStroke shortcut2 = null;
/**
* Constructor.
*
* @param action action
* @param shortcuts list of shortcut for this action
*/
public GlobalShortcutEntry(String action, List<AWTKeyStroke> shortcuts)
{
setAction(action);
setShortcuts(shortcuts);
}
/**
* Returns primary shortcut if it exists.
*
* @return primary shortcut if it exists.
*/
public AWTKeyStroke getShortcut()
{
return this.shortcut;
}
/**
* Returns second shortcut if it exists.
*
* @return second shortcut if it exists.
*/
public AWTKeyStroke getShortcut2()
{
return this.shortcut2;
}
/**
* Set the shortcut keystroke and field.
*
* @param shortcut <tt>AWTKeyStroke</tt>
* @return string representation of the keystroke
*/
public static String getShortcutText(AWTKeyStroke shortcut)
{
if (shortcut == DISABLED)
{
return "Disabled";
}
else
{
StringBuffer buffer = new StringBuffer();
if (shortcut.getKeyEventType() == KeyEvent.KEY_TYPED)
{
buffer.append(shortcut.getKeyChar());
}
else
{
int keycode = shortcut.getKeyCode();
int modifiers = shortcut.getModifiers();
// Indicates modifiers of the keystroke
boolean shiftMask = (modifiers & InputEvent.SHIFT_MASK) != 0;
boolean ctrlMask = (modifiers & InputEvent.CTRL_MASK) != 0;
boolean metaMask = (modifiers & InputEvent.META_MASK) != 0;
boolean altMask = (modifiers & InputEvent.ALT_MASK) != 0;
if (shiftMask && keycode != KeyEvent.VK_SHIFT)
buffer.append("Shift + ");
if (ctrlMask && keycode != KeyEvent.VK_CONTROL)
buffer.append("Ctrl + ");
if (metaMask && keycode != KeyEvent.VK_META)
buffer.append("Meta + ");
if (altMask && keycode != KeyEvent.VK_ALT)
buffer.append("Alt + ");
buffer.append(KeyEvent.getKeyText(keycode));
}
return buffer.toString();
}
}
/**
* Set the shortcuts for this action.
*
* @param shortcuts list of shortcuts
*/
public void setShortcuts(List<AWTKeyStroke> shortcuts)
{
if(shortcuts.size() > 0)
{
this.shortcut = shortcuts.get(0);
}
if(shortcuts.size() > 1)
{
this.shortcut2 = shortcuts.get(1);
}
}
/**
* Returns action string.
*
* @return action
*/
public String getAction()
{
return action;
}
/**
* Set action string
*
* @param action action
*/
public void setAction(String action)
{
this.action = action;
}
/**
* If this global keybindings is disabled.
*
* @return true if this global keybinding is disabled
*/
public boolean isDisabled()
{
return this.shortcut == DISABLED && this.shortcut2 == DISABLED;
}
/**
* Provides the string representation of this mapping. The exact details of
* the representation are unspecified and subject to change but the
* following format can be considered to be typical:<br>
* "BindingEntry (" + Shortcut + " \u2192 " + Action + ")"
*
* @return string representation of entry
*/
@Override
public String toString()
{
StringBuilder builder = new StringBuilder();
builder.append("GlobalBindingEntry (");
if (isDisabled())
builder.append("Disabled");
else
builder.append(getShortcut());
builder.append(" \u2192 "); // arrow pointing right
builder.append(getAction());
builder.append(")");
return builder.toString();
}
/**
* Checks if argument is an instance of this class with the same shortcut
* and associated action. It does not compare aspects of the display
* elements.
*
* @param obj element with which to be compared
* @return true if argument is an instance of this class with matching
* shortcut and action, false otherwise
*/
@Override
public boolean equals(Object obj)
{
if (obj == this)
return true;
else if (!(obj instanceof GlobalShortcutEntry))
return false;
GlobalShortcutEntry entry = (GlobalShortcutEntry) obj;
boolean equals = true;
String action = this.getAction();
if (action == null)
equals &= entry.getAction() == null;
else
equals &= action.equals(entry.getAction());
AWTKeyStroke shortcut = this.getShortcut();
if (shortcut == null)
equals &= entry.getShortcut() == null;
else
equals &= shortcut.equals(entry.getShortcut());
shortcut = this.getShortcut2();
if (shortcut == null)
equals &= entry.getShortcut() == null;
else
equals &= shortcut.equals(entry.getShortcut2());
return equals;
}
/**
* Returns hashcode for this instance.
*
* @return hashcode for this instance
*/
@Override
public int hashCode()
{
int hash = 17;
hash = 37 * hash + (getAction() == null ? 0 : getAction().hashCode());
hash =
37 * hash + (getShortcut() == null ? 0 : getShortcut().hashCode());
return hash;
}
}

@ -0,0 +1,199 @@
/*
* 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.util.*;
import javax.swing.table.*;
/**
* Table model for global shortcuts.
*
* @author Sebastien Vincent
*/
public class GlobalShortcutTableModel
extends AbstractTableModel
{
/**
* Serial version UID.
*/
private static final long serialVersionUID = 0L;
/**
* List of shortcuts.
*/
private List<GlobalShortcutEntry> shortcuts =
new ArrayList<GlobalShortcutEntry>();
/**
* Returns the title for this column
*
* @param column the column
*
* @return the title for this column
*
* @see javax.swing.table.AbstractTableModel#getColumnName
*/
public String getColumnName(int column)
{
switch(column)
{
case 0:
return Resources.getString(
"plugin.keybindings.globalchooser.SHORTCUT_NAME");
case 1:
return Resources.getString(
"plugin.keybindings.globalchooser.SHORTCUT_PRIMARY");
case 2:
return Resources.getString(
"plugin.keybindings.globalchooser.SHORTCUT_SECOND");
default:
throw new IllegalArgumentException("column not found");
}
}
/**
* Returns the number of rows in the table
*
* @return the number of rows in the table
* @see javax.swing.table.AbstractTableModel#getRowCount
*/
public int getRowCount()
{
return shortcuts.size();
}
/**
* Returns the number of column in the table
*
* @return the number of columns in the table
*
* @see javax.swing.table.AbstractTableModel#getColumnCount
*/
public int getColumnCount()
{
// 3 columns: "name", "primary shortcut", "second shortcut"
return 3;
}
/**
* Returns the text for the given cell of the table
*
* @param row cell row
* @param column cell column
* @return object at the row/column
* @see javax.swing.table.AbstractTableModel#getValueAt
*/
public Object getValueAt(int row, int column)
{
switch(column)
{
case 0:
return getEntryAt(row).getAction();
case 1:
return GlobalShortcutEntry.getShortcutText(
getEntryAt(row).getShortcut());
case 2:
return GlobalShortcutEntry.getShortcutText(
getEntryAt(row).getShortcut2());
default:
throw new IllegalArgumentException("column not found");
}
}
/**
* Returns the LdapDirectory at the row 'row'
*
* @param row the row on which to find the LdapDirectory
*
* @return the LdapDirectory found
*/
public GlobalShortcutEntry getEntryAt(int row)
{
int i = 0;
for(GlobalShortcutEntry entry : shortcuts)
{
if(i == row)
return entry;
i++;
}
throw new IllegalArgumentException("row not found");
}
/**
* Returns whether a cell is editable.
* @param row row of the cell
* @param col column of the cell
*
* @return whether the cell is editable
*/
public boolean isCellEditable(int row, int col)
{
return false;
}
/**
* Overrides a method that always returned Object.class
* Now it will return Boolean.class for the first method,
* letting the DefaultTableCellRenderer create checkboxes.
*
* @param columnIndex index of the column
* @return Column class
*/
public Class<?> getColumnClass(int columnIndex)
{
Object o = getValueAt(0, columnIndex);
if(o == null)
return String.class;
return o.getClass();
}
/**
* Sets a value in an editable cell.
*
* @param aValue value to set
* @param rowIndex row index
* @param columnIndex column index
*/
public void setValueAt(Object aValue, int rowIndex, int columnIndex)
{
if(columnIndex != 0)
throw new IllegalArgumentException("non editable column!");
}
/**
* Adds an entry.
*
* @param entry entry to add
*/
public void addEntry(GlobalShortcutEntry entry)
{
shortcuts.add(entry);
}
/**
* Adds an entry.
*
* @param entry entry to add
*/
public void removeEntry(GlobalShortcutEntry entry)
{
shortcuts.remove(entry);
}
/**
* Returns all shortcuts.
*
* @return all shortcuts.
*/
public List<GlobalShortcutEntry> getEntries()
{
return shortcuts;
}
}

@ -0,0 +1,43 @@
/*
* 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 net.java.sip.communicator.plugin.keybindingchooser.*;
/**
* The <tt>Resources</tt> class manages the access to the internationalization
* properties files and the image resources used in this plugin.
*
* @author Yana Stamcheva
*/
public class Resources
{
/**
* Returns an internationalized string corresponding to the given key.
*
* @param key The key of the string.
* @return An internationalized string corresponding to the given key.
*/
public static String getString(String key)
{
return KeybindingChooserActivator.getResources()
.getI18NString(key);
}
/**
* Returns an internationalized string corresponding to the given key.
*
* @param key The key of the string.
* @param params additionnal parameters
* @return An internationalized string corresponding to the given key.
*/
public static String getString(String key, String[] params)
{
return KeybindingChooserActivator.getResources()
.getI18NString(key, params);
}
}

@ -8,7 +8,10 @@ Import-Package: org.osgi.framework,
net.java.sip.communicator.service.gui,
net.java.sip.communicator.service.keybindings,
net.java.sip.communicator.service.resources,
net.java.sip.communicator.service.configuration,
net.java.sip.communicator.service.globalshortcut,
net.java.sip.communicator.util,
net.java.sip.communicator.util.swing,
javax.swing,
javax.swing.event
javax.swing.event,
javax.swing.table,

@ -0,0 +1,42 @@
/*
* 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.service.globalshortcut;
import java.awt.*;
/**
* Event related to global shortcut.
*
* @author Sebastien Vincent
*/
public class GlobalShortcutEvent
{
/**
* Key stroke.
*/
private final AWTKeyStroke keyStroke;
/**
* Initializes a new <tt>GlobalShortcutEvent</tt>.
*
* @param keyStroke keystroke
*/
public GlobalShortcutEvent(AWTKeyStroke keyStroke)
{
this.keyStroke = keyStroke;
}
/**
* Returns keyStroke.
*
* @return keystroke
*/
public AWTKeyStroke getKeyStroke()
{
return keyStroke;
}
}

@ -0,0 +1,22 @@
/*
* 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.service.globalshortcut;
/**
* Global shortcut listener.
*
* @author Sebastien Vincent
*/
public interface GlobalShortcutListener
{
/**
* Callback when an shortcut is typed
*
* @param evt <tt>GlobalShortcutEvent</tt>
*/
public void shortcutReceived(GlobalShortcutEvent evt);
}

@ -0,0 +1,41 @@
/*
* 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.service.globalshortcut;
import java.awt.*;
/**
* This global shortcut service permits to register listeners for global
* shortcut (i.e. keystroke even if application is not foreground).
*
* @author Sebastien Vincent
*/
public interface GlobalShortcutService
{
/**
* Registers an action to execute when the keystroke is typed.
*
* @param l listener to notify when keystroke is typed
* @param keyStroke keystroke that will trigger the action
*/
public void registerShortcut(GlobalShortcutListener l,
AWTKeyStroke keyStroke);
/**
* Unregisters an action to execute when the keystroke is typed.
*
* @param l listener to remove
* @param keyStroke keystroke that will trigger the action
*/
public void unregisterShortcut(GlobalShortcutListener l,
AWTKeyStroke keyStroke);
/**
* Reload global shortcuts.
*/
public void reloadGlobalShortcuts();
}

@ -0,0 +1,33 @@
/*
* 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.service.keybindings;
import java.util.*;
import java.util.List; //disambiguation
import java.awt.*;
/**
* Global keybinding set interface.
*
* @author Sebastien Vincent
*/
public interface GlobalKeybindingSet
{
/**
* Provides current keybinding mappings.
* @return mapping of keystrokes to the string representation of the actions
* they perform
*/
public Map<String, List<AWTKeyStroke>> getBindings();
/**
* Resets the bindings and notifies the observer's listeners if they've
* changed.
* @param bindings new keybindings to be held
*/
public void setBindings(Map<String, List<AWTKeyStroke>> bindings);
}

@ -23,20 +23,20 @@ public abstract class KeybindingSet
* they perform
*/
public abstract HashMap <KeyStroke, String> getBindings();
/**
* Resets the bindings and notifies the observer's listeners if they've
* changed.
* @param newBindings new keybindings to be held
*/
public abstract void setBindings(Map <KeyStroke, String> newBindings);
/**
* Provides the portion of the UI to which the bindings belong.
* @return binding category
*/
public abstract Category getCategory();
/**
* Keybinding sets available in the Sip Communicator.
*/
@ -44,16 +44,16 @@ public enum Category
{
CHAT("keybindings-chat", Persistence.SERIAL_HASH),
MAIN("keybindings-main", Persistence.SERIAL_HASH);
private final String resource;
private final Persistence persistenceFormat;
Category(String resource, Persistence format)
{
this.resource = resource;
this.persistenceFormat = format;
}
/**
* Provides the name keybindings are saved and loaded with.
* @return filename used for keybindings
@ -62,7 +62,7 @@ public String getResource()
{
return this.resource;
}
/**
* Provides the format used to save and load keybinding resources.
* @return style of persistence used by keybindings

@ -6,6 +6,10 @@
*/
package net.java.sip.communicator.service.keybindings;
import java.awt.*;
import java.util.*;
import java.util.List;
/**
* The <tt>KeybindingService</tt> handles the distribution of configurable and
* persistent keybinding sets.
@ -21,4 +25,23 @@ public interface KeybindingsService
* actions
*/
KeybindingSet getBindings(KeybindingSet.Category category);
/**
* Provides the bindings associated with the global category.
*
* @return global keybinding set
*/
GlobalKeybindingSet getGlobalBindings();
/**
* Returns list of global shortcuts from the configuration file.
*
* @return list of global shortcuts.
*/
public Map<String, List<AWTKeyStroke>> getGlobalShortcutFromConfiguration();
/**
* Save the configuration file.
*/
public void saveGlobalShortcutFromConfiguration();
}
Loading…
Cancel
Save