diff --git a/Headers/gnustep/gui/NSInputManager.h b/Headers/gnustep/gui/NSInputManager.h index 36d5b3194..85a6c94a0 100644 --- a/Headers/gnustep/gui/NSInputManager.h +++ b/Headers/gnustep/gui/NSInputManager.h @@ -1,4 +1,4 @@ -/* -*-objc-*- +/* -*-objc-*- NSInputManager.h Copyright (C) 2001 Free Software Foundation, Inc. @@ -6,6 +6,9 @@ Author: Fred Kiefer Date: August 2001 + Author: Nicola Pero + Date: December 2001 + This file is part of the GNUstep GUI Library. This library is free software; you can redistribute it and/or @@ -28,6 +31,7 @@ #define _GNUstep_H_NSInputManager #include +#include #include #include #include @@ -37,9 +41,7 @@ @class NSImage; @protocol NSTextInput -// Marking text -- (void) setMarkedText: (id)aString - selectedRange: (NSRange)selRange; +- (void) setMarkedText: (id)aString selectedRange: (NSRange)selRange; - (BOOL) hasMarkedText; - (NSRange) markedRange; - (NSRange) selectedRange; @@ -55,25 +57,54 @@ - (void) insertText: (id)aString; @end +struct _GSInputManagerBinding +{ + /* The character this binding is about. */ + unichar character; + + /* The character is bound to different selectors according to the + various modifiers which can be used with the character. + + There are eight selectors for the eight possibilities - each of + NSShiftKeyMask, NSControlKeyMask, NSAlternateKeyMask might be on + or off and each combination gives a different selector. + + The index of the selector to use is given by + + (modifiers & (NSShiftKeyMask | NSControlKeyMask | NSAlternateKeyMask)) / 2 + + a NULL selector means use the default action */ + SEL selector[8]; +}; + + + @interface NSInputManager: NSObject +{ + id _currentClient; + + /* An array of bindings. */ + struct _GSInputManagerBinding *_bindings; + + /* The size of the array. */ + int _bindingsCount; +} + (NSInputManager *) currentInputManager; -+ (void) cycleToNextInputLanguage: (id)sender; -+ (void) cycleToNextInputServerInLanguage: (id)sender; + +- (id) initWithName: (NSString *)inputServerName + host: (NSString *)hostName; - (BOOL) handleMouseEvent: (NSEvent *)theMouseEvent; -- (NSImage *) image; -- (NSInputManager *) initWithName: (NSString *)inputServerName - host: (NSString *)hostName; +- (void) handleKeyboardEvents: (NSArray *)eventArray + client: (id)client; - (NSString *) language; - (NSString *) localizedInputManagerName; - (void) markedTextAbandoned: (id)client; - (void) markedTextSelectionChanged: (NSRange)newSel client: (id)client; -- (NSInputServer *) server; - (BOOL) wantsToDelayTextChangeNotifications; - (BOOL) wantsToHandleMouseEvents; - (BOOL) wantsToInterpretAllKeystrokes; @end -#endif //_GNUstep_H_NSInputManager - +#endif /* _GNUstep_H_NSInputManager */ diff --git a/Source/NSInputManager.m b/Source/NSInputManager.m index 99fadfaf5..e8f0ccbea 100644 --- a/Source/NSInputManager.m +++ b/Source/NSInputManager.m @@ -2,8 +2,8 @@ Copyright (C) 2001 Free Software Foundation, Inc. - Author: Fred Kiefer - Date: August 2001 + Author: Nicola Pero + Date: December 2001 This file is part of the GNUstep GUI Library. @@ -24,46 +24,498 @@ */ #include -#include #include #include +#include + +/* A table mapping character names to characters, used to interpret + the character names found in KeyBindings dictionary files. */ +#define CHARACTER_TABLE_SIZE 77 + +static struct +{ + NSString *name; + unichar character; +} +character_table[CHARACTER_TABLE_SIZE] = +{ + /* Function keys. */ + { @"UpArrow", NSUpArrowFunctionKey }, + { @"DownArrow", NSDownArrowFunctionKey }, + { @"LeftArrow", NSLeftArrowFunctionKey }, + { @"RightArrow", NSRightArrowFunctionKey }, + { @"F1", NSF1FunctionKey }, + { @"F2", NSF2FunctionKey }, + { @"F3", NSF3FunctionKey }, + { @"F4", NSF4FunctionKey }, + { @"F5", NSF5FunctionKey }, + { @"F6", NSF6FunctionKey }, + { @"F7", NSF7FunctionKey }, + { @"F8", NSF8FunctionKey }, + { @"F9", NSF9FunctionKey }, + { @"F10", NSF10FunctionKey }, + { @"F11", NSF11FunctionKey }, + { @"F12", NSF12FunctionKey }, + { @"F13", NSF13FunctionKey }, + { @"F14", NSF14FunctionKey }, + { @"F15", NSF15FunctionKey }, + { @"F16", NSF16FunctionKey }, + { @"F17", NSF17FunctionKey }, + { @"F18", NSF18FunctionKey }, + { @"F19", NSF19FunctionKey }, + { @"F20", NSF20FunctionKey }, + { @"F21", NSF21FunctionKey }, + { @"F22", NSF22FunctionKey }, + { @"F23", NSF23FunctionKey }, + { @"F24", NSF24FunctionKey }, + { @"F25", NSF25FunctionKey }, + { @"F26", NSF26FunctionKey }, + { @"F27", NSF27FunctionKey }, + { @"F28", NSF28FunctionKey }, + { @"F29", NSF29FunctionKey }, + { @"F30", NSF30FunctionKey }, + { @"F31", NSF31FunctionKey }, + { @"F32", NSF32FunctionKey }, + { @"F33", NSF33FunctionKey }, + { @"F34", NSF34FunctionKey }, + { @"F35", NSF35FunctionKey }, + { @"Insert", NSInsertFunctionKey }, + { @"Delete", NSDeleteFunctionKey }, + { @"Home", NSHomeFunctionKey }, + { @"Begin", NSBeginFunctionKey }, + { @"End", NSEndFunctionKey }, + { @"PageUp", NSPageUpFunctionKey }, + { @"PageDown", NSPageDownFunctionKey }, + { @"PrintScreen", NSPrintScreenFunctionKey }, + { @"ScrollLock", NSScrollLockFunctionKey }, + { @"Pause", NSPauseFunctionKey }, + { @"SysReq", NSSysReqFunctionKey }, + { @"Break", NSBreakFunctionKey }, + { @"Reset", NSResetFunctionKey }, + { @"Stop", NSStopFunctionKey }, + { @"Menu", NSMenuFunctionKey }, + { @"User", NSUserFunctionKey }, + { @"System", NSSystemFunctionKey }, + { @"Print", NSPrintFunctionKey }, + { @"ClearLine", NSClearLineFunctionKey }, + { @"ClearDisplay", NSClearDisplayFunctionKey }, + { @"InsertLine", NSInsertLineFunctionKey }, + { @"DeleteLine", NSDeleteLineFunctionKey }, + { @"InsertChar", NSInsertCharFunctionKey }, + { @"DeleteChar", NSDeleteCharFunctionKey }, + { @"Prev", NSPrevFunctionKey }, + { @"Next", NSNextFunctionKey }, + { @"Select", NSSelectFunctionKey }, + { @"Execute", NSExecuteFunctionKey }, + { @"Undo", NSUndoFunctionKey }, + { @"Redo", NSRedoFunctionKey }, + { @"Find", NSFindFunctionKey }, + { @"Help", NSHelpFunctionKey }, + { @"ModeSwitch", NSModeSwitchFunctionKey }, + + /* Special characters by name. Useful if you want, for example, + to associate some special action to C-Tab or similar evils. */ + { @"Backspace", NSBackspaceCharacter }, + { @"Tab", NSTabCharacter }, + { @"Enter", NSEnterCharacter }, + { @"FormFeed", NSFormFeedCharacter }, + { @"CarriageReturn", NSCarriageReturnCharacter } +}; + +static NSInputManager *currentInputManager = nil; + @implementation NSInputManager -// Class methods + (NSInputManager *) currentInputManager { - return nil; + if (currentInputManager == nil) + { + currentInputManager = [[self alloc] initWithName: nil host: nil]; + } + + return currentInputManager; } -+ (void) cycleToNextInputLanguage: (id)sender -{} +- (void) bindKey: (NSString *)key toAction: (NSString *)action +{ + /* First we try to parse the key into a character/flags couple */ + unichar character = 0; + unsigned flags = 0; + BOOL isFunctionKey = NO; + /* Then we parse the action into a selector */ + SEL selector; -+ (void) cycleToNextInputServerInLanguage: (id)sender -{} + NSString *c; + /* Parse the key: first break it into segments separated by - */ + NSArray *components = [key componentsSeparatedByString: @"-"]; + + /* Then, parse the modifiers. The modifiers are the components + - all of them except the last one! */ + int i, count = [components count]; + int index; + + for (i = 0; i < count - 1; i++) + { + NSString *modifier = [components objectAtIndex: i]; + + if ([modifier isEqualToString: @"Control"] + || [modifier isEqualToString: @"Ctrl"] + || [modifier isEqualToString: @"C"]) + { + flags |= NSControlKeyMask; + } + else if ([modifier isEqualToString: @"Alternate"] + || [modifier isEqualToString: @"Alt"] + || [modifier isEqualToString: @"A"] + || [modifier isEqualToString: @"Meta"] + || [modifier isEqualToString: @"M"]) + { + flags |= NSAlternateKeyMask; + } + else if ([modifier isEqualToString: @"Shift"] + || [modifier isEqualToString: @"S"]) + { + flags |= NSShiftKeyMask; + } + else + { + NSLog (@"NSInputManager - unknown modifier '%@' ignored", modifier); + } + } + + /* The actual index in the little SEL table is the following one. */ + index = flags / 2; + + /* Now, parse the actual key. */ + c = [components objectAtIndex: (count - 1)]; + + if ([c isEqualToString: @""]) + { + /* This happens if '-' was the character. */ + character = '-'; + } + else if ([c length] == 1) + { + /* A single character, such as 'a'. */ + character = [c characterAtIndex: 0]; + } + else + { + /* A descriptive string, such as Tab or Home. */ + for (i = 0; i < CHARACTER_TABLE_SIZE; i++) + { + if ([c isEqualToString: (character_table[i]).name]) + { + character = (character_table[i]).character; + isFunctionKey = YES; + break; + } + } + if (i == CHARACTER_TABLE_SIZE) + { + NSLog (@"NSInputManager - unknown character '%@' ignored", c); + return; + } + } + + selector = NSSelectorFromString (action); + if (selector == NULL) + { + NSLog (@"NSInputManager: unknown selector '%@' ignored", action); + return; + } + + /* Check if there are already some bindings for this character. */ + for (i = 0; i < _bindingsCount; i++) + { + if (_bindings[i].character == character) + { + (_bindings[i]).selector[index] = selector; + if (!isFunctionKey) + { + /* Not a function key - set the selector for the character + with shift as well - we don't actually know if the character + is typed with shift or not ! */ + flags |= NSShiftKeyMask; + index = flags / 2; + + (_bindings[i]).selector[index] = selector; + } + return; + } + } + + /* Ok - allocate memory for the new binding. */ + if (_bindingsCount == 0) + { + _bindingsCount = 1; + _bindings = objc_malloc (sizeof (struct _GSInputManagerBinding)); + } + else + { + _bindingsCount++; + _bindings = objc_realloc (_bindings, + sizeof (struct _GSInputManagerBinding) + * _bindingsCount); + } + _bindings[_bindingsCount - 1].character = character; + + /* Set to NULL all selectors. */ + for (i = 0; i < 8; i++) + { + (_bindings[_bindingsCount - 1]).selector[i] = NULL; + } + + /* Now save the selector. */ + (_bindings[_bindingsCount - 1]).selector[index] = selector; + if (!isFunctionKey) + { + /* Not a function key - set the selector for the character + with shift as well - we don't actually know if the character + is typed with shift or not ! */ + flags |= NSShiftKeyMask; + index = flags / 2; + + (_bindings[_bindingsCount - 1]).selector[index] = selector; + } +} + +- (void) dealloc +{ + objc_free (_bindings); + return; +} + +- (void) loadBindingsFromFile: (NSString *)fullPath +{ + NS_DURING + { + NSDictionary *bindings; + NSEnumerator *e; + NSString *key; + + bindings = [NSDictionary dictionaryWithContentsOfFile: fullPath]; + if (bindings == nil) + { + [NSException raise]; + } + + e = [bindings keyEnumerator]; + while ((key = [e nextObject]) != nil) + { + [self bindKey: key + toAction: [bindings objectForKey: key]]; + } + } + NS_HANDLER + { + NSLog (@"Unable to load KeyBindings from file %@", fullPath); + } + NS_ENDHANDLER +} + +- (void) loadBindingsWithName: (NSString *)fileName +{ + NSArray *paths; + NSEnumerator *enumerator; + NSString *bundlePath; + + paths = NSSearchPathForDirectoriesInDomains (GSLibrariesDirectory, + NSAllDomainsMask, YES); + /* paths are in the order - user, network, local, root. Instead we + want to load keybindings in the order root, local, network, user + - so that user can override root - for this reason we use a + reverseObjectEnumerator. */ + enumerator = [paths reverseObjectEnumerator]; + while ((bundlePath = [enumerator nextObject]) != nil) + { + NSBundle *bundle = [NSBundle bundleWithPath: bundlePath]; + NSString *fullPath = [bundle pathForResource: fileName + ofType: @"dict" + inDirectory: @"KeyBindings"]; + if (fullPath != nil) + { + [self loadBindingsFromFile: fullPath]; + } + } +} - (NSInputManager *) initWithName: (NSString *)inputServerName - host: (NSString *)hostName + host: (NSString *)hostName { - return nil; + NSString *defaultKeyBindings; + NSArray *customKeyBindings; + NSUserDefaults *defaults; + CREATE_AUTORELEASE_POOL (pool); + + defaults = [NSUserDefaults standardUserDefaults]; + + self = [super init]; + + /* Normally, when we start up, we load all the keybindings we find + in the following files, in this order: + + $GNUSTEP_SYSTEM_ROOT/Libraries/Resources/KeyBindings/DefaultKeyBindings.dict + $GNUSTEP_LOCAL_ROOT/Libraries/Resources/KeyBindings/DefaultKeyBindings.dict + $GNUSTEP_NETWORK_ROOT/Libraries/Resources/KeyBindings/DefaultKeyBindings.dict + $GNUSTEP_USER_ROOT/Libraries/Resources/KeyBindings/DefaultKeyBindings.dict + + This gives you a first way of adding your customized keybindings + - adding a DefaultKeyBindings.dict to your GNUSTEP_USER_ROOT, and + putting additional keybindings in there. This allows you to add + new keybindings to the standard ones, or override standard ones + with your own. These keybindings are normally used by all your + applications (this is why they are in 'DefaultKeyBindings'). + + In addition, you can specify a list of additional key bindings + files to be loaded by setting the GSCustomKeyBindings default to + an array of file names. We will attempt to load all those + keybindings in a way similar to what we do with the + DefaultKeyBindings. We load them after the default ones, in the + order you specify. This allows you to have application-specific + keybindings, where you put different keybindings in different + files, and run different applications with different + GSCustomKeyBindings, telling them to use different keybindings + files. + + Last, in special cases you might want to have the + DefaultKeybindings totally ignored. In this case, you set the + GSDefaultKeyBindings variable to a different filename (different + from 'DefaultKeybindings'). We attempt to load all keybindings + stored in the files with that name where we normally would load + DefaultKeybindings. */ + + /* First, load the DefaultKeyBindings. */ + defaultKeyBindings = [defaults stringForKey: @"GSDefaultKeyBindings"]; + + if (defaultKeyBindings == nil) + { + defaultKeyBindings = @"DefaultKeyBindings"; + } + + [self loadBindingsWithName: defaultKeyBindings]; + + /* Then, if any, the CustomKeyBindings, in the specified order. */ + customKeyBindings = [defaults arrayForKey: @"GSCustomKeyBindings"]; + + if (customKeyBindings != nil) + { + int i, count = [customKeyBindings count]; + Class string = [NSString class]; + + for (i = 0; i < count; i++) + { + NSString *filename = [customKeyBindings objectAtIndex: i]; + + if ([filename isKindOfClass: string]) + { + [self loadBindingsWithName: filename]; + } + } + } + + RELEASE (pool); + + return self; } +- (void) handleKeyboardEvents: (NSArray *)eventArray + client: (id)client +{ + NSEvent *theEvent; + NSEnumerator *eventEnum = [eventArray objectEnumerator]; + + _currentClient = client; + + while ((theEvent = [eventEnum nextObject]) != nil) + { + NSString *characters = [theEvent characters]; + NSString *unmodifiedCharacters = [theEvent charactersIgnoringModifiers]; + unichar character = 0; + unsigned flags = [theEvent modifierFlags] & (NSShiftKeyMask + | NSAlternateKeyMask + | NSControlKeyMask); + BOOL done = NO; + int i; + + if ([unmodifiedCharacters length] > 0) + { + character = [unmodifiedCharacters characterAtIndex: 0]; + } + + + /* Look up the character in the keybindings dictionary. */ + for (i = 0; i < _bindingsCount; i++) + { + if (_bindings[i].character == character) + { + SEL selector = (_bindings[i]).selector[flags / 2]; + + if (selector == NULL) + { + /* Get out of loop with done = NO - we found the + keybinding, but it was bound to the default + action, so we stop searching and fall back on the + default action. */ + break; + } + else + { + [self doCommandBySelector: selector]; + done = YES; + break; + } + } + } + + /* If not yet found, perform the default action. */ + if (!done) + { + switch (character) + { + case NSBackspaceCharacter: + [self doCommandBySelector: @selector (deleteBackward:)]; + break; + + case NSTabCharacter: + if (flags & NSShiftKeyMask) + { + [self doCommandBySelector: @selector (insertBacktab:)]; + } + else + { + [self doCommandBySelector: @selector (insertTab:)]; + } + break; + + case NSEnterCharacter: + case NSFormFeedCharacter: + case NSCarriageReturnCharacter: + [self doCommandBySelector: @selector (insertNewline:)]; + break; + + default: + [self insertText: characters]; + break; + } + } + } +} + + - (BOOL) handleMouseEvent: (NSEvent *)theMouseEvent { return NO; } -- (NSImage *) image -{ - return nil; -} - - (NSString *) language { - return nil; + return @"English"; } + - (NSString *) localizedInputManagerName { return nil; @@ -73,14 +525,9 @@ {} - (void) markedTextSelectionChanged: (NSRange)newSel - client: (id)client + client: (id)client {} -- (NSInputServer *) server -{ - return nil; -} - - (BOOL) wantsToDelayTextChangeNotifications { return NO; @@ -96,7 +543,6 @@ return NO; } -// NSTextInput protocol - (void) setMarkedText: (id)aString selectedRange: (NSRange)selRange {} @@ -108,12 +554,12 @@ - (NSRange) markedRange { - return NSMakeRange(NSNotFound, 0); + return NSMakeRange (NSNotFound, 0); } - (NSRange) selectedRange { - return NSMakeRange(NSNotFound, 0); + return NSMakeRange (NSNotFound, 0); } - (void) unmarkText @@ -140,7 +586,9 @@ } - (void) doCommandBySelector: (SEL)aSelector -{} +{ + [_currentClient doCommandBySelector: aSelector]; +} - (NSRect) firstRectForCharacterRange: (NSRange)theRange { @@ -148,6 +596,8 @@ } - (void) insertText: (id)aString -{} +{ + [_currentClient insertText: aString]; +} @end