mirror of
https://github.com/keymanapp/keyman.git
synced 2026-09-03 06:07:41 +00:00
[mac] Support Adobe products in legacy mode (and VS Code)
This commit is contained in:
parent
8fd9bc7e74
commit
256ba2ef1e
8 changed files with 203 additions and 132 deletions
|
|
@ -390,9 +390,9 @@
|
|||
98A778C21A8C53BF00CF809D /* KMInputMethodAppDelegate.h */,
|
||||
98A778C31A8C53BF00CF809D /* KMInputMethodAppDelegate.m */,
|
||||
E21799031FC5B74D00F2D66A /* KMInputMethodEventHandler.h */,
|
||||
E21799041FC5B7BC00F2D66A /* KMInputMethodEventHandler.m */,
|
||||
E24C79EC1FFFEA6B00D8E46F /* KMInputMethodEventHandlerProtected.h */,
|
||||
E22020F920050D6300B74FAC /* KMInputMethodBrowserClientEventHandlerProtected.h */,
|
||||
E21799041FC5B7BC00F2D66A /* KMInputMethodEventHandler.m */,
|
||||
E24C79EB1FFFCE3000D8E46F /* KMInputMethodBrowserClientEventHandler.h */,
|
||||
E22020F3200505EF00B74FAC /* KMInputMethodSafariClientEventHandler.h */,
|
||||
E24C79E91FFFCC7500D8E46F /* KMInputMethodBrowserClientEventHandler.m */,
|
||||
|
|
|
|||
|
|
@ -9,10 +9,10 @@
|
|||
#import <Foundation/Foundation.h>
|
||||
#import <InputMethodKit/InputMethodKit.h>
|
||||
#import <Carbon/Carbon.h>
|
||||
#import "KMInputMethodAppDelegate.h"
|
||||
|
||||
@interface KMInputController : IMKInputController
|
||||
|
||||
- (void)menuAction:(id)sender;
|
||||
- (BOOL)handleDeleteBackLowLevel:(NSEvent *)event;
|
||||
|
||||
@end
|
||||
|
|
|
|||
|
|
@ -57,6 +57,16 @@ NSMutableArray *servers;
|
|||
return [_eventHandler handleEvent:event client:sender];
|
||||
}
|
||||
|
||||
// Passthrough from the app delegate low level event hook
|
||||
// to the input method event handler for Delete Back.
|
||||
- (BOOL)handleDeleteBackLowLevel:(NSEvent *)event {
|
||||
if(_eventHandler != nil) {
|
||||
return [_eventHandler handleDeleteBackLowLevel:event];
|
||||
}
|
||||
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (void)activateServer:(id)sender {
|
||||
@synchronized(servers) {
|
||||
[sender overrideKeyboardWithKeyboardNamed:@"com.apple.keylayout.US"];
|
||||
|
|
@ -84,7 +94,9 @@ NSMutableArray *servers;
|
|||
_eventHandler = [[KMInputMethodBrowserClientEventHandler alloc] init];
|
||||
}
|
||||
else {
|
||||
_eventHandler = [[KMInputMethodEventHandler alloc] initWithClient:clientAppId];
|
||||
// We cache the client for use with events sourced from the low level tap
|
||||
// where we don't necessarily have any access to the current client.
|
||||
_eventHandler = [[KMInputMethodEventHandler alloc] initWithClient:clientAppId client:sender];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -123,6 +135,7 @@ NSMutableArray *servers;
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
- (NSDictionary *)modes:(id)sender {
|
||||
if ([self.AppDelegate debugMode])
|
||||
|
|
|
|||
|
|
@ -72,7 +72,7 @@ extern NSString *const kWebSite;
|
|||
@property (nonatomic, strong) NSImage *keyboardIcon;
|
||||
@property (nonatomic, strong) NSAlert *downloadInfoView;
|
||||
@property (nonatomic, strong) NSProgressIndicator *progressIndicator;
|
||||
@property (nonatomic, weak) IMKInputController *inputController;
|
||||
@property (nonatomic, weak) KMInputController *inputController;
|
||||
@property (nonatomic, strong) NSWindowController *configWindow;
|
||||
@property (nonatomic, strong) NSWindowController *downloadKBWindow;
|
||||
@property (nonatomic, strong) KMAboutWindowController *aboutWindow;
|
||||
|
|
|
|||
|
|
@ -81,20 +81,28 @@ id _lastServerWithOSKShowing = nil;
|
|||
andSelector:@selector(handleURLEvent:withReplyEvent:)
|
||||
forEventClass:kInternetEventClass
|
||||
andEventID:kAEGetURL];
|
||||
|
||||
self.lowLevelEventTap = CGEventTapCreate(kCGAnnotatedSessionEventTap, kCGHeadInsertEventTap, kCGEventTapOptionListenOnly, CGEventMaskBit(kCGEventFlagsChanged) | CGEventMaskBit(kCGEventLeftMouseDown) | CGEventMaskBit(kCGEventLeftMouseUp), (CGEventTapCallBack)eventTapFunction, nil);
|
||||
|
||||
|
||||
self.lowLevelEventTap = CGEventTapCreate(kCGAnnotatedSessionEventTap,
|
||||
kCGHeadInsertEventTap,
|
||||
kCGEventTapOptionListenOnly,
|
||||
CGEventMaskBit(kCGEventFlagsChanged) |
|
||||
CGEventMaskBit(kCGEventLeftMouseDown) |
|
||||
CGEventMaskBit(kCGEventLeftMouseUp) |
|
||||
CGEventMaskBit(kCGEventKeyDown),
|
||||
(CGEventTapCallBack)eventTapFunction,
|
||||
nil);
|
||||
|
||||
if (!self.lowLevelEventTap) {
|
||||
NSLog(@"Can't tap into low level events!");
|
||||
}
|
||||
else {
|
||||
CFRelease(self.lowLevelEventTap);
|
||||
}
|
||||
|
||||
|
||||
self.runLoopEventSrc = CFMachPortCreateRunLoopSource(kCFAllocatorDefault, self.lowLevelEventTap, 0);
|
||||
|
||||
|
||||
CFRunLoopRef runLoop = CFRunLoopGetCurrent();
|
||||
|
||||
|
||||
if (self.runLoopEventSrc && runLoop) {
|
||||
CFRunLoopAddSource(runLoop, self.runLoopEventSrc, kCFRunLoopDefaultMode);
|
||||
}
|
||||
|
|
@ -119,7 +127,7 @@ id _lastServerWithOSKShowing = nil;
|
|||
#endif
|
||||
|
||||
- (void)handleURLEvent:(NSAppleEventDescriptor*)event withReplyEvent:(NSAppleEventDescriptor*)replyEvent {
|
||||
|
||||
|
||||
[self processURL:[[event paramDescriptorForKeyword:keyDirectObject] stringValue]];
|
||||
}
|
||||
|
||||
|
|
@ -129,14 +137,14 @@ id _lastServerWithOSKShowing = nil;
|
|||
NSURL *url = [NSURL URLWithString:urlStr];
|
||||
if (self.debugMode)
|
||||
NSLog(@"url = %@", url);
|
||||
|
||||
|
||||
if ([url.lastPathComponent isEqualToString:@"download"]) {
|
||||
if (_connection != nil) {
|
||||
if (self.debugMode)
|
||||
NSLog(@"Already downloading a keyboard.");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
NSURL *downloadUrl;
|
||||
NSArray *params = [[url query] componentsSeparatedByString:@"&"];
|
||||
for (NSString *value in params) {
|
||||
|
|
@ -154,11 +162,11 @@ id _lastServerWithOSKShowing = nil;
|
|||
downloadUrl = [NSURL URLWithString:urlString];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (downloadUrl && _downloadFilename) {
|
||||
if (_infoWindow.window != nil)
|
||||
[_infoWindow close];
|
||||
|
||||
|
||||
[self.downloadInfoView setInformativeText:self.downloadFilename];
|
||||
if (self.configWindow.window != nil) {
|
||||
[self.configWindow.window makeKeyAndOrderFront:nil];
|
||||
|
|
@ -251,13 +259,13 @@ CGEventRef eventTapFunction(CGEventTapProxy proxy, CGEventType type, CGEventRef
|
|||
}
|
||||
return event;
|
||||
}
|
||||
|
||||
|
||||
NSEvent* sysEvent = [NSEvent eventWithCGEvent:event];
|
||||
// Too many of these to be useful for most debugging sessions, but we'll keep this around to be
|
||||
// un-commented when needed.
|
||||
//if (appDelegate.debugMode)
|
||||
// NSLog(@"System Event: %@", sysEvent);
|
||||
|
||||
|
||||
switch (type) {
|
||||
case kCGEventFlagsChanged:
|
||||
appDelegate.currentModifierFlags = sysEvent.modifierFlags;
|
||||
|
|
@ -265,13 +273,24 @@ CGEventRef eventTapFunction(CGEventTapProxy proxy, CGEventType type, CGEventRef
|
|||
appDelegate.contextChangingEventDetected = YES;
|
||||
}
|
||||
break;
|
||||
|
||||
|
||||
case kCGEventLeftMouseUp:
|
||||
case kCGEventLeftMouseDown:
|
||||
case kCGEventOtherMouseUp:
|
||||
case kCGEventOtherMouseDown:
|
||||
appDelegate.contextChangingEventDetected = YES;
|
||||
break;
|
||||
|
||||
case kCGEventKeyDown:
|
||||
// Pass back delete events through to the input method event handler
|
||||
// because some 'legacy' apps don't allow us to see back delete events
|
||||
// that we have synthesized (and we need to see them, for serialization
|
||||
// of events)
|
||||
if(sysEvent.keyCode == kVK_Delete && appDelegate.inputController != nil) {
|
||||
[appDelegate.inputController handleDeleteBackLowLevel:sysEvent];
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
|
@ -288,7 +307,7 @@ CGEventRef eventTapFunction(CGEventTapProxy proxy, CGEventType type, CGEventRef
|
|||
_kme = [[KMEngine alloc] initWithKMX:nil contextBuffer:self.contextBuffer];
|
||||
[_kme setDebugMode:self.debugMode];
|
||||
}
|
||||
|
||||
|
||||
return _kme;
|
||||
}
|
||||
|
||||
|
|
@ -400,7 +419,7 @@ CGEventRef eventTapFunction(CGEventTapProxy proxy, CGEventType type, CGEventRef
|
|||
[fm createDirectoryAtPath:_keyboardsPath withIntermediateDirectories:YES attributes:nil error:nil];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return _keyboardsPath;
|
||||
}
|
||||
|
||||
|
|
@ -427,11 +446,11 @@ CGEventRef eventTapFunction(CGEventTapProxy proxy, CGEventType type, CGEventRef
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (others != nil)
|
||||
[_kmxFileList addObject:others];
|
||||
}
|
||||
|
||||
|
||||
return _kmxFileList;
|
||||
}
|
||||
|
||||
|
|
@ -447,7 +466,7 @@ CGEventRef eventTapFunction(CGEventTapProxy proxy, CGEventType type, CGEventRef
|
|||
x++;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
|
|
@ -462,15 +481,15 @@ CGEventRef eventTapFunction(CGEventTapProxy proxy, CGEventType type, CGEventRef
|
|||
x++;
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
if (index >= x && index <= (x+pArray.count)) {
|
||||
packagePath = [[pArray objectAtIndex:0] stringByDeletingLastPathComponent];
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
x += (pArray.count+1);
|
||||
}
|
||||
|
||||
|
||||
return packagePath;
|
||||
}
|
||||
|
||||
|
|
@ -487,7 +506,7 @@ CGEventRef eventTapFunction(CGEventTapProxy proxy, CGEventType type, CGEventRef
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return index;
|
||||
}
|
||||
|
||||
|
|
@ -497,12 +516,12 @@ CGEventRef eventTapFunction(CGEventTapProxy proxy, CGEventType type, CGEventRef
|
|||
NSString *mPath = [NSString stringWithString:[path stringByDeletingLastPathComponent]];
|
||||
if ([mPath isEqualToString:sourcePath])
|
||||
return @"Others";
|
||||
|
||||
|
||||
while (![mPath isEqualToString:sourcePath]) {
|
||||
packageFolder = [mPath lastPathComponent];
|
||||
mPath = [mPath stringByDeletingLastPathComponent];
|
||||
}
|
||||
|
||||
|
||||
return packageFolder;
|
||||
}
|
||||
|
||||
|
|
@ -517,17 +536,17 @@ CGEventRef eventTapFunction(CGEventTapProxy proxy, CGEventType type, CGEventRef
|
|||
hasInfo = YES;
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
if (hasInfo && [line startsWith:@"Name="]) {
|
||||
NSString *value = [[[line substringFromIndex:5] componentsSeparatedByString:@","] objectAtIndex:0];
|
||||
packageName = [NSString stringWithString:[value stringByReplacingOccurrencesOfString:@"\"" withString:@""]];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (packageName == nil)
|
||||
packageName = packageFolder;
|
||||
|
||||
|
||||
return packageName;
|
||||
}
|
||||
|
||||
|
|
@ -552,7 +571,7 @@ CGEventRef eventTapFunction(CGEventTapProxy proxy, CGEventType type, CGEventRef
|
|||
NSMutableArray *files = [NSMutableArray arrayWithCapacity:0];
|
||||
NSMutableArray *fonts = [NSMutableArray arrayWithCapacity:0];
|
||||
NSMutableArray *kbs = [NSMutableArray arrayWithCapacity:0];
|
||||
|
||||
|
||||
@try {
|
||||
NSString *fileContents = [[NSString stringWithContentsOfFile:infoFile encoding:NSUTF8StringEncoding error:NULL] stringByReplacingOccurrencesOfString:@"\r" withString:@""];
|
||||
NSArray *lines = [fileContents componentsSeparatedByString:@"\n"];
|
||||
|
|
@ -560,7 +579,7 @@ CGEventRef eventTapFunction(CGEventTapProxy proxy, CGEventType type, CGEventRef
|
|||
for (NSString *line in lines) {
|
||||
if (!line.length)
|
||||
continue;
|
||||
|
||||
|
||||
if ([line startsWith:kPackage]) {
|
||||
contentType = ctPackage;
|
||||
continue;
|
||||
|
|
@ -585,14 +604,14 @@ CGEventRef eventTapFunction(CGEventTapProxy proxy, CGEventType type, CGEventRef
|
|||
contentType = ctFiles;
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
switch (contentType) {
|
||||
case ctPackage: {
|
||||
if ([line startsWith:kReadMeFile])
|
||||
[infoDict setObject:[line substringFromIndex:kReadMeFile.length+1] forKey:kReadMeFile];
|
||||
else if ([line startsWith:kGraphicFile])
|
||||
[infoDict setObject:[line substringFromIndex:kGraphicFile.length+1] forKey:kGraphicFile];
|
||||
|
||||
|
||||
break;
|
||||
}
|
||||
case ctButtons:
|
||||
|
|
@ -607,7 +626,7 @@ CGEventRef eventTapFunction(CGEventTapProxy proxy, CGEventType type, CGEventRef
|
|||
NSString *v2 = [[vs objectAtIndex:1] stringByReplacingOccurrencesOfString:@"\"" withString:@""];
|
||||
[infoDict setObject:@[v1, v2] forKey:kWelcome];
|
||||
}
|
||||
|
||||
|
||||
break;
|
||||
}
|
||||
case ctInfo: {
|
||||
|
|
@ -646,14 +665,14 @@ CGEventRef eventTapFunction(CGEventTapProxy proxy, CGEventType type, CGEventRef
|
|||
NSString *v2 = [[vs objectAtIndex:1] stringByReplacingOccurrencesOfString:@"\"" withString:@""];
|
||||
[infoDict setObject:@[v1, v2] forKey:kWebSite];
|
||||
}
|
||||
|
||||
|
||||
break;
|
||||
}
|
||||
case ctFiles: {
|
||||
NSUInteger x = [line rangeOfString:@"="].location;
|
||||
if (x == NSNotFound)
|
||||
continue;
|
||||
|
||||
|
||||
NSString *s = [line substringFromIndex:x+2];
|
||||
if ([s startsWith:kFile]) {
|
||||
NSArray *vs = [s componentsSeparatedByString:@"\","];
|
||||
|
|
@ -673,7 +692,7 @@ CGEventRef eventTapFunction(CGEventTapProxy proxy, CGEventType type, CGEventRef
|
|||
NSString *v2 = [[vs objectAtIndex:1] stringByReplacingOccurrencesOfString:@"\"" withString:@""];
|
||||
[kbs addObject:@[v1, v2]];
|
||||
}
|
||||
|
||||
|
||||
break;
|
||||
}
|
||||
default:
|
||||
|
|
@ -685,14 +704,14 @@ CGEventRef eventTapFunction(CGEventTapProxy proxy, CGEventType type, CGEventRef
|
|||
NSLog(@"Error = %@", e.description);
|
||||
return nil;
|
||||
}
|
||||
|
||||
|
||||
if (files.count)
|
||||
[infoDict setValue:files forKey:kFile];
|
||||
if (fonts.count)
|
||||
[infoDict setValue:fonts forKey:kFont];
|
||||
if (kbs.count)
|
||||
[infoDict setValue:kbs forKey:kKeyboard];
|
||||
|
||||
|
||||
return infoDict.count?[NSDictionary dictionaryWithDictionary:infoDict]:nil;
|
||||
}
|
||||
|
||||
|
|
@ -701,7 +720,7 @@ CGEventRef eventTapFunction(CGEventTapProxy proxy, CGEventType type, CGEventRef
|
|||
NSUserDefaults *userData = [NSUserDefaults standardUserDefaults];
|
||||
_selectedKeyboard = [userData objectForKey:kKMSelectedKeyboardKey];
|
||||
}
|
||||
|
||||
|
||||
return _selectedKeyboard;
|
||||
}
|
||||
|
||||
|
|
@ -719,7 +738,7 @@ CGEventRef eventTapFunction(CGEventTapProxy proxy, CGEventType type, CGEventRef
|
|||
if (!_activeKeyboards)
|
||||
_activeKeyboards = [[NSMutableArray alloc] initWithCapacity:0];
|
||||
}
|
||||
|
||||
|
||||
return _activeKeyboards;
|
||||
}
|
||||
|
||||
|
|
@ -744,7 +763,7 @@ CGEventRef eventTapFunction(CGEventTapProxy proxy, CGEventType type, CGEventRef
|
|||
if (![[NSFileManager defaultManager] fileExistsAtPath:path])
|
||||
[pathsToRemove addObject:path];
|
||||
}
|
||||
|
||||
|
||||
if (pathsToRemove.count > 0) {
|
||||
[self.activeKeyboards removeObjectsInArray:pathsToRemove];
|
||||
NSUserDefaults *userData = [NSUserDefaults standardUserDefaults];
|
||||
|
|
@ -757,7 +776,7 @@ CGEventRef eventTapFunction(CGEventTapProxy proxy, CGEventType type, CGEventRef
|
|||
if (_contextBuffer == nil) {
|
||||
_contextBuffer = [[NSMutableString alloc] initWithString:@""];
|
||||
}
|
||||
|
||||
|
||||
return _contextBuffer;
|
||||
}
|
||||
|
||||
|
|
@ -770,15 +789,15 @@ CGEventRef eventTapFunction(CGEventTapProxy proxy, CGEventType type, CGEventRef
|
|||
|
||||
- (void)awakeFromNib {
|
||||
[self setKeyboardsSubMenu];
|
||||
|
||||
|
||||
NSMenuItem *config = [self.menu itemWithTag:2];
|
||||
if (config)
|
||||
[config setAction:@selector(menuAction:)];
|
||||
|
||||
|
||||
NSMenuItem *osk = [self.menu itemWithTag:3];
|
||||
if (osk)
|
||||
[osk setAction:@selector(menuAction:)];
|
||||
|
||||
|
||||
NSMenuItem *about = [self.menu itemWithTag:4];
|
||||
if (about)
|
||||
[about setAction:@selector(menuAction:)];
|
||||
|
|
@ -822,7 +841,7 @@ CGEventRef eventTapFunction(CGEventTapProxy proxy, CGEventType type, CGEventRef
|
|||
|
||||
[keyboards.submenu addItem:item];
|
||||
}
|
||||
|
||||
|
||||
if (keyboards.submenu.numberOfItems == 0) {
|
||||
NSMenuItem *item = [[NSMenuItem alloc] initWithTitle:@"(None)" action:NULL keyEquivalent:@""];
|
||||
[keyboards.submenu addItem:item];
|
||||
|
|
@ -868,7 +887,7 @@ CGEventRef eventTapFunction(CGEventTapProxy proxy, CGEventType type, CGEventRef
|
|||
if ([extension isEqualToString:@"kmx"])
|
||||
[kmxFiles addObject:[path stringByAppendingPathComponent:filePath]];
|
||||
}
|
||||
|
||||
|
||||
return kmxFiles;
|
||||
}
|
||||
|
||||
|
|
@ -881,7 +900,7 @@ CGEventRef eventTapFunction(CGEventTapProxy proxy, CGEventType type, CGEventRef
|
|||
if ([extension isEqualToString:@"kvk"])
|
||||
[kvkFiles addObject:[self.keyboardsPath stringByAppendingPathComponent:filePath]];
|
||||
}
|
||||
|
||||
|
||||
return kvkFiles;
|
||||
}
|
||||
|
||||
|
|
@ -894,14 +913,14 @@ CGEventRef eventTapFunction(CGEventTapProxy proxy, CGEventType type, CGEventRef
|
|||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return kvkFilePath;
|
||||
}
|
||||
|
||||
- (NSWindowController *)oskWindow {
|
||||
if (!_oskWindow)
|
||||
_oskWindow = [[OSKWindowController alloc] initWithWindowNibName:@"OSKWindowController"];
|
||||
|
||||
|
||||
return _oskWindow;
|
||||
}
|
||||
|
||||
|
|
@ -943,7 +962,7 @@ CGEventRef eventTapFunction(CGEventTapProxy proxy, CGEventType type, CGEventRef
|
|||
if (_aboutWindow.window == nil) {
|
||||
_aboutWindow = [[KMAboutWindowController alloc] initWithWindowNibName:@"KMAboutWindowController"];
|
||||
}
|
||||
|
||||
|
||||
return _aboutWindow;
|
||||
}
|
||||
|
||||
|
|
@ -955,7 +974,7 @@ CGEventRef eventTapFunction(CGEventTapProxy proxy, CGEventType type, CGEventRef
|
|||
if (_infoWindow.window == nil) {
|
||||
_infoWindow = [[KMInfoWindowController alloc] initWithWindowNibName:@"KMInfoWindowController"];
|
||||
}
|
||||
|
||||
|
||||
return _infoWindow;
|
||||
}
|
||||
|
||||
|
|
@ -967,7 +986,7 @@ CGEventRef eventTapFunction(CGEventTapProxy proxy, CGEventType type, CGEventRef
|
|||
if (_kbHelpWindow.window == nil) {
|
||||
_kbHelpWindow = [[KMKeyboardHelpWindowController alloc] initWithWindowNibName:@"KMKeyboardHelpWindowController"];
|
||||
}
|
||||
|
||||
|
||||
return _kbHelpWindow;
|
||||
}
|
||||
|
||||
|
|
@ -979,7 +998,7 @@ CGEventRef eventTapFunction(CGEventTapProxy proxy, CGEventType type, CGEventRef
|
|||
if (_downloadKBWindow.window == nil) {
|
||||
_downloadKBWindow = [[KMDownloadKBWindowController alloc] initWithWindowNibName:@"KMDownloadKBWindowController"];
|
||||
}
|
||||
|
||||
|
||||
return _downloadKBWindow;
|
||||
}
|
||||
|
||||
|
|
@ -1003,11 +1022,11 @@ CGEventRef eventTapFunction(CGEventTapProxy proxy, CGEventType type, CGEventRef
|
|||
[self.infoWindow.window makeKeyAndOrderFront:nil];
|
||||
[self.infoWindow.window setLevel:NSFloatingWindowLevel];
|
||||
}
|
||||
|
||||
|
||||
NSString *packagePath = [self.keyboardsPath stringByAppendingPathComponent:[self.downloadFilename stringByDeletingPathExtension]];
|
||||
[self.infoWindow setPackagePath:packagePath];
|
||||
}
|
||||
|
||||
|
||||
_downloadInfoView = nil;
|
||||
_connection = nil;
|
||||
_downloadFilename = nil;
|
||||
|
|
@ -1024,7 +1043,7 @@ CGEventRef eventTapFunction(CGEventTapProxy proxy, CGEventType type, CGEventRef
|
|||
[_downloadInfoView setAlertStyle:NSInformationalAlertStyle];
|
||||
[_downloadInfoView setAccessoryView:self.progressIndicator];
|
||||
}
|
||||
|
||||
|
||||
return _downloadInfoView;
|
||||
}
|
||||
|
||||
|
|
@ -1036,7 +1055,7 @@ CGEventRef eventTapFunction(CGEventTapProxy proxy, CGEventType type, CGEventRef
|
|||
[_progressIndicator setMaxValue:100];
|
||||
[_progressIndicator setDoubleValue:0];
|
||||
}
|
||||
|
||||
|
||||
return _progressIndicator;
|
||||
}
|
||||
|
||||
|
|
@ -1095,7 +1114,7 @@ CGEventRef eventTapFunction(CGEventTapProxy proxy, CGEventType type, CGEventRef
|
|||
- (void)handleKeyEvent:(NSEvent *)event {
|
||||
if (_oskWindow == nil)
|
||||
return;
|
||||
|
||||
|
||||
[_oskWindow.oskView handleKeyEvent:event];
|
||||
}
|
||||
|
||||
|
|
@ -1105,12 +1124,12 @@ extern const CGKeyCode kProcessPendingBuffer;
|
|||
// allows us to override the norma behavior for unit testing, where there is no
|
||||
// active event loop to post to.
|
||||
- (void)postKeyboardEventWithSource: (CGEventSourceRef)source code:(CGKeyCode) virtualKey postCallback:(PostEventCallback)postEvent{
|
||||
|
||||
|
||||
CGEventRef ev = CGEventCreateKeyboardEvent (source, virtualKey, true); //down
|
||||
if (postEvent)
|
||||
postEvent(ev);
|
||||
CFRelease(ev);
|
||||
if (virtualKey != kProcessPendingBuffer) { // special 0xFF code is not a real key-press, so no "up" is needed
|
||||
if (virtualKey != kProcessPendingBuffer) { // special 0xFF code is not a real key-press, so no "up" is needed
|
||||
ev = CGEventCreateKeyboardEvent (source, virtualKey, false); //up
|
||||
if (postEvent)
|
||||
postEvent(ev);
|
||||
|
|
@ -1133,7 +1152,7 @@ extern const CGKeyCode kProcessPendingBuffer;
|
|||
didUnzip = [za UnzipFileTo:destFolder overWrite:YES];
|
||||
[za UnzipCloseFile];
|
||||
}
|
||||
|
||||
|
||||
if (didUnzip) {
|
||||
if (self.debugMode)
|
||||
NSLog(@"Unzipped file: %@", filePath);
|
||||
|
|
@ -1151,7 +1170,7 @@ extern const CGKeyCode kProcessPendingBuffer;
|
|||
NSLog(@"Failed to unzip file: %@", filePath);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return didUnzip;
|
||||
}
|
||||
|
||||
|
|
@ -1159,14 +1178,14 @@ extern const CGKeyCode kProcessPendingBuffer;
|
|||
if (_fontsPath == nil) {
|
||||
BOOL isDir;
|
||||
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES);
|
||||
|
||||
|
||||
if (paths.count == 1) {
|
||||
NSString *path = [[paths objectAtIndex:0] stringByAppendingPathComponent:@"Fonts"];
|
||||
if ([[NSFileManager defaultManager] fileExistsAtPath:path isDirectory:&isDir] && isDir)
|
||||
_fontsPath = [NSString stringWithString:path];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return _fontsPath;
|
||||
}
|
||||
|
||||
|
|
@ -1174,17 +1193,17 @@ extern const CGKeyCode kProcessPendingBuffer;
|
|||
NSString *fontsPath = self.fontsPath;
|
||||
if (fontsPath == nil)
|
||||
return;
|
||||
|
||||
|
||||
NSArray *fonts = [self FontFilesAtPath:path];
|
||||
for (NSString *srcPath in fonts) {
|
||||
NSString *destPath = [fontsPath stringByAppendingPathComponent:[srcPath lastPathComponent]];
|
||||
NSError *error;
|
||||
if ([[NSFileManager defaultManager] fileExistsAtPath:destPath])
|
||||
[[NSFileManager defaultManager] removeItemAtPath:destPath error:&error];
|
||||
|
||||
|
||||
if (error == nil)
|
||||
[[NSFileManager defaultManager] copyItemAtPath:srcPath toPath:destPath error:&error];
|
||||
|
||||
|
||||
if (error != nil)
|
||||
NSLog(@"Error = %@", error);
|
||||
}
|
||||
|
|
@ -1199,7 +1218,7 @@ extern const CGKeyCode kProcessPendingBuffer;
|
|||
if ([extension isEqualToString:@"ttf"] || [extension isEqualToString:@"otf"])
|
||||
[fontFiles addObject:[path stringByAppendingPathComponent:filePath]];
|
||||
}
|
||||
|
||||
|
||||
return fontFiles;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -14,8 +14,9 @@
|
|||
|
||||
@interface KMInputMethodEventHandler : NSObject
|
||||
|
||||
- (instancetype)initWithClient:(NSString *)clientAppId;
|
||||
- (instancetype)initWithClient:(NSString *)clientAppId client:(id)sender;
|
||||
- (BOOL)handleEvent:(NSEvent *)event client:(id)sender;
|
||||
- (BOOL)handleDeleteBackLowLevel:(NSEvent *)event;
|
||||
- (void)deactivate;
|
||||
|
||||
@end
|
||||
|
|
|
|||
|
|
@ -43,9 +43,10 @@ NSRange _previousSelRange;
|
|||
}
|
||||
|
||||
// This is the public initializer.
|
||||
- (instancetype)initWithClient:(NSString *)clientAppId {
|
||||
- (instancetype)initWithClient:(NSString *)clientAppId client:(id) sender {
|
||||
senderForDeleteBack = sender;
|
||||
// TODO: Pages and Keynote (and possibly lots of other undiscovered apps that are otherwise compliant
|
||||
// with Apple's IM faramework) have a problem in that if the user selects a different font (or other
|
||||
// with Apple's IM framework) have a problem in that if the user selects a different font (or other
|
||||
// formatting) and then types a sequence that causes characters to be added to the document and then
|
||||
// subsequently replaced, the replacement causes the formatting decision to be forgotten. This can be
|
||||
// "fixed" by treating them as legacy apps, but it causes other problems.
|
||||
|
|
@ -58,10 +59,15 @@ NSRange _previousSelRange;
|
|||
[clientAppId isEqual: @"org.sil.app.builder.dictionary.DictionaryAppBuilder"] ||
|
||||
[clientAppId isEqual: @"com.microsoft.Word"] ||
|
||||
[clientAppId isEqual: @"org.openoffice.script"] ||
|
||||
[clientAppId isEqual: @"com.adobe.illustrator"] ||
|
||||
[clientAppId isEqual: @"com.adobe.InDesign"] ||
|
||||
[clientAppId isEqual: @"com.adobe.Photoshop"] ||
|
||||
[clientAppId isEqual: @"com.adobe.AfterEffects"] ||
|
||||
[clientAppId isEqual: @"com.microsoft.VSCode"] ||
|
||||
[clientAppId isEqual: @"com.google.Chrome"] ||
|
||||
[clientAppId isEqual: @"com.Keyman.test.legacyInput"]
|
||||
/*||[clientAppId isEqual: @"ro.sync.exml.Oxygen"] - Oxygen has worse problems */);
|
||||
|
||||
|
||||
// We used to default to NO, so these were the obvious exceptions. But then we realized that
|
||||
// in any app, command keys can change the selection, so now we default to YES, and only have
|
||||
// a few situations where we pretend it can't. This flag should probably be renamed to something
|
||||
|
|
@ -71,7 +77,7 @@ NSRange _previousSelRange;
|
|||
// [clientAppId isEqual: @"com.apple.dt.Xcode"]) {
|
||||
// _clientSelectionCanChangeUnexpectedly = YES;
|
||||
// }
|
||||
|
||||
|
||||
// In Xcode, if Keyman is the active IM and is in "debugMode" and "English plus Spanish" is the current keyboard and you type "Crashlytics force now", it will force a simulated crash to test reporting to fabric.io.
|
||||
if ([self.AppDelegate debugMode] && [clientAppId isEqual: @"com.apple.dt.Xcode"]) {
|
||||
NSLog(@"Crashlytics - Preparing to detect Easter egg.");
|
||||
|
|
@ -79,7 +85,7 @@ NSRange _previousSelRange;
|
|||
}
|
||||
else
|
||||
_easterEggForCrashlytics = nil;
|
||||
|
||||
|
||||
// For the Atom editor, this isn't really true (the context CAN change unexpectedly), but we can't get
|
||||
// the context, so we pretend/hope it won't.
|
||||
BOOL selectionCanChangeUnexpectedly = (![clientAppId isEqual: @"com.github.atom"]);
|
||||
|
|
@ -148,14 +154,14 @@ NSRange _previousSelRange;
|
|||
return;
|
||||
}
|
||||
NSString* text = [self pendingBuffer];
|
||||
|
||||
|
||||
if ([self.AppDelegate debugMode])
|
||||
NSLog(@"Inserting text from pending buffer: \"%@\"", text);
|
||||
|
||||
|
||||
[client insertText:text replacementRange:NSMakeRange(NSNotFound, NSNotFound)];
|
||||
_previousSelRange.location += text.length;
|
||||
_previousSelRange.length = 0;
|
||||
|
||||
|
||||
[self setPendingBuffer:@""];
|
||||
}
|
||||
|
||||
|
|
@ -220,9 +226,9 @@ NSRange _previousSelRange;
|
|||
NSLog(@"*** updateContextBuffer ***");
|
||||
NSLog(@"sender: %@", sender);
|
||||
}
|
||||
|
||||
|
||||
NSRange selRange = [self getSelectionRangefromClient: sender];
|
||||
|
||||
|
||||
// Since client can't tell us the actual current position, we assume the previously known location in the context.
|
||||
// (It won't matter anyway if the client also fails to report its context.)
|
||||
NSUInteger len = (_clientCanProvideSelectionInfo != Yes) ? _previousSelRange.length : selRange.location;
|
||||
|
|
@ -243,7 +249,7 @@ NSRange _previousSelRange;
|
|||
-(NSString *)getLimitedContextFrom:(id)sender at:(NSUInteger) len {
|
||||
if (![sender respondsToSelector:@selector(attributedSubstringFromRange:)])
|
||||
return nil;
|
||||
|
||||
|
||||
NSUInteger start = 0;
|
||||
if (len > kMaxContext) {
|
||||
if ([self.AppDelegate debugMode])
|
||||
|
|
@ -251,7 +257,7 @@ NSRange _previousSelRange;
|
|||
start = len - kMaxContext;
|
||||
len = kMaxContext;
|
||||
}
|
||||
|
||||
|
||||
NSString *preBuffer = [[sender attributedSubstringFromRange:NSMakeRange(start, len)] string];
|
||||
if ([self.AppDelegate debugMode]) {
|
||||
NSLog(@"preBuffer = \"%@\"", preBuffer ? preBuffer : @"nil");
|
||||
|
|
@ -260,7 +266,7 @@ NSRange _previousSelRange;
|
|||
else
|
||||
NSLog(@"preBuffer has a length of 0");
|
||||
}
|
||||
|
||||
|
||||
return preBuffer;
|
||||
}
|
||||
|
||||
|
|
@ -301,7 +307,7 @@ NSRange _previousSelRange;
|
|||
_contextOutOfDate = YES;
|
||||
self.AppDelegate.contextChangingEventDetected = NO;
|
||||
}
|
||||
|
||||
|
||||
if (_contextOutOfDate)
|
||||
[self updateContextBuffer:client];
|
||||
}
|
||||
|
|
@ -347,7 +353,7 @@ NSRange _previousSelRange;
|
|||
}
|
||||
else
|
||||
return NO;
|
||||
|
||||
|
||||
if ([self.AppDelegate debugMode]) {
|
||||
NSLog(@"actions = %@", actions);
|
||||
}
|
||||
|
|
@ -357,7 +363,7 @@ NSRange _previousSelRange;
|
|||
NSLog(@"Handling %@ action...", actionType);
|
||||
NSLog(@"contextBuffer = \"%@\"", self.contextBuffer.length?[self.contextBuffer codeString]:@"{empty}");
|
||||
}
|
||||
|
||||
|
||||
if ([actionType isEqualToString:Q_STR]) {
|
||||
NSString *output = [action objectForKey:actionType];
|
||||
if ([self.AppDelegate debugMode])
|
||||
|
|
@ -390,7 +396,7 @@ NSRange _previousSelRange;
|
|||
[self replaceExistingSelectionIn:sender with:output];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Even if the characters to insert are pending, we want to append them to the context buffer now.
|
||||
// Waiting until they are inserted would probably be safe, but on the off-chance that the engine
|
||||
// generates additional actions beyond this current one, we want to be sure that the context reflects
|
||||
|
|
@ -406,7 +412,7 @@ NSRange _previousSelRange;
|
|||
// (which could include deadkeys the client doesn't know about).
|
||||
[self.contextBuffer deleteLastNChars:n];
|
||||
n -= dc;
|
||||
|
||||
|
||||
// n is now the number of characters to delete from the client.
|
||||
if (n > 0) {
|
||||
deleteBackPosted = [self deleteBack:n in:sender for: event];
|
||||
|
|
@ -441,7 +447,7 @@ NSRange _previousSelRange;
|
|||
NSLog(@"Processing an unhandled delete-back...");
|
||||
NSLog(@"_numberOfPostedDeletesToExpect = %lu", _numberOfPostedDeletesToExpect);
|
||||
}
|
||||
|
||||
|
||||
// If we have pending characters to insert following the delete-back, then
|
||||
// the context buffer has already been properly set to reflect the deletions.
|
||||
if ((_legacyMode && (_pendingBuffer == nil || _pendingBuffer.length == 0)) ||
|
||||
|
|
@ -460,7 +466,7 @@ NSRange _previousSelRange;
|
|||
if (--_numberOfPostedDeletesToExpect == 0) {
|
||||
if ([self.AppDelegate debugMode])
|
||||
NSLog(@"Processing final posted delete-back...");
|
||||
|
||||
|
||||
self.willDeleteNullChar = NO;
|
||||
if (_legacyMode) {
|
||||
if (_pendingBuffer != nil && _pendingBuffer.length > 0) {
|
||||
|
|
@ -491,6 +497,29 @@ NSRange _previousSelRange;
|
|||
}
|
||||
}
|
||||
|
||||
// handleDeleteBackLowLevel: handles the situation for Delete Back for
|
||||
// some 'legacy' mode apps such as Adobe apps, because when we inject
|
||||
// the Delete Back, it is passed through to the app but we never see it
|
||||
// in our normal handleEvent function. However, other apps, such as Word,
|
||||
// show the event both in the low level tap and in the normal IM
|
||||
// handleEvent. Thus, we set a flag after processing a Delete Back here so
|
||||
// that we don't accidentally process it twice. Note that a Delete Back that
|
||||
// we handle here should never be passed on to Keyman Engine for transform,
|
||||
// as it will be part of the output from the transform.
|
||||
- (BOOL)handleDeleteBackLowLevel:(NSEvent *)event {
|
||||
ignoreNextDeleteBackHighLevel = NO;
|
||||
if(event.keyCode == kVK_Delete && _legacyMode && [self pendingBuffer].length > 0) {
|
||||
BOOL updateEngineContext = YES;
|
||||
if ([self.AppDelegate debugMode]) {
|
||||
NSLog(@"legacy: delete-back received, processing");
|
||||
}
|
||||
[self processUnhandledDeleteBack:self.senderForDeleteBack updateEngineContext: &updateEngineContext];
|
||||
ignoreNextDeleteBackHighLevel = YES;
|
||||
}
|
||||
|
||||
return ignoreNextDeleteBackHighLevel;
|
||||
}
|
||||
|
||||
- (BOOL)handleEvent:(NSEvent *)event client:(id)sender {
|
||||
// OSK key feedback from hardware keyboard is disabled
|
||||
/*if (event.type == NSKeyDown)
|
||||
|
|
@ -510,7 +539,7 @@ NSRange _previousSelRange;
|
|||
{
|
||||
if ([self.AppDelegate debugMode]) {
|
||||
NSLog(@"Processing the special %hu code", kProcessPendingBuffer);
|
||||
|
||||
|
||||
NSUInteger length = [self pendingBuffer].length;
|
||||
if (length > 0) {
|
||||
for (NSUInteger ich = 0; ich < length; ich++)
|
||||
|
|
@ -521,19 +550,26 @@ NSRange _previousSelRange;
|
|||
[self insertPendingBufferTextIn:sender];
|
||||
return YES;
|
||||
}
|
||||
|
||||
|
||||
if(event.keyCode == kVK_Delete && _legacyMode && ignoreNextDeleteBackHighLevel) {
|
||||
// This event was sent by Keyman and we should just
|
||||
// pass it through to the app. handleDeleteBackLowLevel
|
||||
// already did anything we need with it.
|
||||
return NO; // We'll let the client app accept the Delete Back
|
||||
}
|
||||
|
||||
[self checkContextIn:sender];
|
||||
|
||||
[self updateContextBufferIfNeeded:sender];
|
||||
|
||||
|
||||
if ([self.AppDelegate debugMode]) {
|
||||
NSLog(@"sender type = %@", NSStringFromClass([sender class]));
|
||||
if (_clientCanProvideSelectionInfo == Yes)
|
||||
NSLog(@"sender selection range location = %lu", [self getSelectionRangefromClient:sender].location);
|
||||
}
|
||||
|
||||
|
||||
BOOL handled = [self handleKeymanEngineActions:event in: sender];
|
||||
|
||||
|
||||
if (!handled) {
|
||||
NSUInteger nc = [self.contextBuffer deleteLastNullChars];
|
||||
if (nc > 0) {
|
||||
|
|
@ -544,10 +580,10 @@ NSRange _previousSelRange;
|
|||
self.willDeleteNullChar = YES;
|
||||
[self postDeleteBacks:nc for:event];
|
||||
_keyCodeOfOriginalEvent = event.keyCode;
|
||||
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
|
||||
// For other events that the Keyman engine does not have rules, just apply context changes
|
||||
// and let client handle the event
|
||||
NSString* charactersToAppend = nil;
|
||||
|
|
@ -555,9 +591,9 @@ NSRange _previousSelRange;
|
|||
unsigned short keyCode = event.keyCode;
|
||||
switch (keyCode) {
|
||||
case kVK_Delete:
|
||||
[self processUnhandledDeleteBack:sender updateEngineContext:&updateEngineContext];
|
||||
[self processUnhandledDeleteBack: sender updateEngineContext: &updateEngineContext];
|
||||
break;
|
||||
|
||||
|
||||
case kVK_LeftArrow:
|
||||
// I had started some code to try to guess where in the context we ended up after a left arrow,
|
||||
// but too many potential pitfalls. Marc says it's better to just let it be dumb.
|
||||
|
|
@ -571,12 +607,12 @@ NSRange _previousSelRange;
|
|||
_contextOutOfDate = YES;
|
||||
updateEngineContext = NO;
|
||||
break;
|
||||
|
||||
|
||||
case kVK_Return:
|
||||
case kVK_ANSI_KeypadEnter:
|
||||
charactersToAppend = @"\n";
|
||||
break;
|
||||
|
||||
|
||||
default:
|
||||
{
|
||||
// NOTE: Although ch is usually the same as keyCode, when the option key is depressed (and
|
||||
|
|
@ -602,12 +638,12 @@ NSRange _previousSelRange;
|
|||
_previousSelRange.length = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (updateEngineContext) {
|
||||
[self.kme setContextBuffer:self.contextBuffer];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if ([self.AppDelegate debugMode]) {
|
||||
if (_contextOutOfDate)
|
||||
NSLog(@"Context now out of date.");
|
||||
|
|
@ -620,7 +656,7 @@ NSRange _previousSelRange;
|
|||
}
|
||||
NSLog(@"***");
|
||||
}
|
||||
|
||||
|
||||
// Note: Although this would seem to be the obvious place to set _previousSelRange (in legacy mode), we can't
|
||||
// because the selection range doesn't get updated until after we return from this method.
|
||||
return handled;
|
||||
|
|
@ -635,7 +671,7 @@ NSRange _previousSelRange;
|
|||
[self deleteBack:n at: pos in: client];
|
||||
if (_legacyMode)
|
||||
return [self deleteBackLegacy:n at: pos with: selectedRange for: event];
|
||||
|
||||
|
||||
return NO;
|
||||
}
|
||||
|
||||
|
|
@ -644,13 +680,13 @@ NSRange _previousSelRange;
|
|||
NSLog(@"Using Apple IM-compliant mode.");
|
||||
NSLog(@"pos = %lu", pos);
|
||||
}
|
||||
|
||||
|
||||
if (pos >= n && pos != NSNotFound) {
|
||||
NSInteger preCharPos = pos - (n+1);
|
||||
if ((preCharPos) >= 0) {
|
||||
NSUInteger nbrOfPreCharacters;
|
||||
NSString *preChar = nil;
|
||||
|
||||
|
||||
// This regex will look back through the context until it finds a *known* base
|
||||
// character because some (non-legacy) apps (e.g., Mail) do not properly handle sending
|
||||
// combining marks on their own via insertText. One potentially negative implication
|
||||
|
|
@ -666,7 +702,7 @@ NSRange _previousSelRange;
|
|||
// which always used just a single character regardless of its class.
|
||||
NSError *error = NULL;
|
||||
NSRegularExpression *regexNonCombiningMark = [NSRegularExpression regularExpressionWithPattern:@"\\P{M}" options:NSRegularExpressionCaseInsensitive error:&error];
|
||||
|
||||
|
||||
for (nbrOfPreCharacters = 1; YES; nbrOfPreCharacters++, preCharPos--) {
|
||||
if ([client respondsToSelector:@selector(attributedSubstringFromRange:)])
|
||||
preChar = [[client attributedSubstringFromRange:NSMakeRange(preCharPos, nbrOfPreCharacters)] string];
|
||||
|
|
@ -682,7 +718,7 @@ NSRange _previousSelRange;
|
|||
}
|
||||
if ([self.AppDelegate debugMode])
|
||||
NSLog(@"Testing preChar: %@", preChar);
|
||||
|
||||
|
||||
if ([regexNonCombiningMark numberOfMatchesInString:preChar options:NSMatchingAnchored range:NSMakeRange(0, 1)] > 0)
|
||||
break;
|
||||
if (preCharPos == 0) {
|
||||
|
|
@ -721,12 +757,12 @@ NSRange _previousSelRange;
|
|||
if (self.contextBuffer != nil && (pos == 0 || pos == NSNotFound)) {
|
||||
pos = self.contextBuffer.length + n;
|
||||
}
|
||||
|
||||
|
||||
if ([self.AppDelegate debugMode]) {
|
||||
NSLog(@"Using Legacy mode.");
|
||||
NSLog(@"pos = %lu", pos);
|
||||
}
|
||||
|
||||
|
||||
if (pos >= n) {
|
||||
// n is now the number of delete-backs we need to post (plus one more if there is selected text)
|
||||
if ([self.AppDelegate debugMode]) {
|
||||
|
|
@ -734,21 +770,20 @@ NSRange _previousSelRange;
|
|||
if (_clientCanProvideSelectionInfo == No || _clientCanProvideSelectionInfo == Unreliable)
|
||||
NSLog(@"Cannot trust client to report accurate selection length - assuming no selection.");
|
||||
}
|
||||
|
||||
|
||||
if (_pendingBuffer != nil && [[self pendingBuffer] length] > 0) {
|
||||
// We shouldn't be sending out characters before the corresponding Delete Back events are received
|
||||
// if this does happen, that's unexpected...
|
||||
NSLog(@"Legacy mode: ERROR: did not find expected Delete Back event");
|
||||
}
|
||||
|
||||
// Note: If pos is "not found", most likely the client can't accurately report the location. This might be
|
||||
// dangerous, but for now let's go ahead and attempt to delete the characters we think should be there.
|
||||
if (_pendingBuffer != nil && [[self pendingBuffer] length] > 0) {
|
||||
NSException* exception = [NSException
|
||||
exceptionWithName:@"InvalidOperationException"
|
||||
reason:@"Cannot process subsequent Q_BACK after Q_STR"
|
||||
userInfo:nil];
|
||||
@throw exception;
|
||||
}
|
||||
|
||||
|
||||
if (_clientCanProvideSelectionInfo == Yes && selectedRange.length > 0)
|
||||
n++; // First delete-back will delete the existing selection.
|
||||
[self postDeleteBacks:n for:event];
|
||||
|
||||
|
||||
CFRelease(_sourceFromOriginalEvent);
|
||||
_sourceFromOriginalEvent = nil;
|
||||
return YES;
|
||||
|
|
@ -759,14 +794,14 @@ NSRange _previousSelRange;
|
|||
- (void)sendEvent:(NSEvent *)event {
|
||||
ProcessSerialNumber psn;
|
||||
GetFrontProcess(&psn);
|
||||
|
||||
|
||||
CGEventSourceRef source = CGEventCreateSourceFromEvent([event CGEvent]);
|
||||
CGEventRef keyDownEvent = CGEventCreateKeyboardEvent(source, event.keyCode, true);
|
||||
CGEventRef keyUpEvent = CGEventCreateKeyboardEvent(source, event.keyCode, false);
|
||||
|
||||
|
||||
CGEventPostToPSN(&psn, keyDownEvent);
|
||||
CGEventPostToPSN(&psn, keyUpEvent);
|
||||
|
||||
|
||||
CFRelease(source);
|
||||
CFRelease(keyDownEvent);
|
||||
CFRelease(keyUpEvent);
|
||||
|
|
@ -774,9 +809,9 @@ NSRange _previousSelRange;
|
|||
|
||||
- (void)postDeleteBacks:(NSUInteger)count for:(NSEvent *) event {
|
||||
_numberOfPostedDeletesToExpect = count;
|
||||
|
||||
|
||||
_sourceFromOriginalEvent = CGEventCreateSourceFromEvent([event CGEvent]);
|
||||
|
||||
|
||||
for (int db = 0; db < count; db++)
|
||||
{
|
||||
if ([self.AppDelegate debugMode]) {
|
||||
|
|
@ -795,7 +830,7 @@ NSRange _previousSelRange;
|
|||
- (void)postKeyPressToFrontProcess:(CGKeyCode)code from:(CGEventSourceRef) source {
|
||||
ProcessSerialNumber psn;
|
||||
GetFrontProcess(&psn);
|
||||
|
||||
|
||||
if ([self.AppDelegate debugMode]) {
|
||||
if (code == kProcessPendingBuffer) {
|
||||
NSLog(@"Posting code to tell Keyman to process characters in pending buffer.");
|
||||
|
|
@ -804,7 +839,7 @@ NSRange _previousSelRange;
|
|||
NSLog(@"Posting a keypress (down/up) to the 'front process' for the %hu key.", code);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
[self.AppDelegate postKeyboardEventWithSource:source code:code postCallback:^(CGEventRef eventToPost) {
|
||||
CGEventPostToPSN((ProcessSerialNumberPtr)&psn, eventToPost);
|
||||
}];
|
||||
|
|
|
|||
|
|
@ -34,6 +34,9 @@ typedef NS_ENUM(NSInteger, ClientCapability) {
|
|||
@property (assign) BOOL clientSelectionCanChangeUnexpectedly; // REVIEW: Maybe we can get notification from these clients by handling mouseDownOnCharacterIndex.
|
||||
@property (assign) ClientCapability clientCanProvideSelectionInfo;
|
||||
|
||||
@property id senderForDeleteBack;
|
||||
@property BOOL ignoreNextDeleteBackHighLevel;
|
||||
|
||||
- (instancetype)initWithLegacyMode:(BOOL)legacy clientSelectionCanChangeUnexpectedly:(BOOL) flagClientSelectionCanChangeUnexpectedly;
|
||||
- (void)handleCommand:(NSEvent *)event;
|
||||
// This just sets the legacyMode property to true and spits out a debug message to that effect.
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue