From 107a1cf7cc2377ae6876b5b76b873d6dc6643cd1 Mon Sep 17 00:00:00 2001 From: Bjorn Winckler Date: Sat, 25 Jul 2009 00:25:32 +0200 Subject: [PATCH] Improve IM support for 10.5 When using "set noimd" remember which input source was used in normal mode separately from the input source used in insert mode. This way it is possible to e.g. use a US layout in normal mode and a non-US layout in insert mode. The input source used in normal mode must be ASCII capable or it won't be remembered -- any input source used in insert mode is remembered. On 10.4 the IM code is unchanged because 10.4 does not support the Text Input Source Services. --- src/MacVim/MMAtsuiTextView.h | 1 + src/MacVim/MMAtsuiTextView.m | 5 ++ src/MacVim/MMTextView.h | 1 + src/MacVim/MMTextView.m | 5 ++ src/MacVim/MMTextViewHelper.h | 10 ++++ src/MacVim/MMTextViewHelper.m | 109 ++++++++++++++++++++++++++++++++-- src/MacVim/MMVimController.m | 6 +- 7 files changed, 129 insertions(+), 8 deletions(-) diff --git a/src/MacVim/MMAtsuiTextView.h b/src/MacVim/MMAtsuiTextView.h index 9a358ffb64..eead8cf5c3 100644 --- a/src/MacVim/MMAtsuiTextView.h +++ b/src/MacVim/MMAtsuiTextView.h @@ -69,6 +69,7 @@ enum { MMMaxCellsPerChar = 2 }; - (void)setMouseShape:(int)shape; - (void)setAntialias:(BOOL)state; - (void)setImControl:(BOOL)enable; +- (void)activateIm:(BOOL)enable; - (BOOL)convertPoint:(NSPoint)point toRow:(int *)row column:(int *)column; - (NSPoint)pointForRow:(int)row column:(int)col; - (NSRect)rectForRow:(int)row column:(int)col numRows:(int)nr diff --git a/src/MacVim/MMAtsuiTextView.m b/src/MacVim/MMAtsuiTextView.m index 4dcae5a01c..14b70e4336 100644 --- a/src/MacVim/MMAtsuiTextView.m +++ b/src/MacVim/MMAtsuiTextView.m @@ -329,6 +329,11 @@ defaultLineHeightForFont(NSFont *font) [helper setImControl:enable]; } +- (void)activateIm:(BOOL)enable +{ + [helper activateIm:enable]; +} + - (void)keyDown:(NSEvent *)event { [helper keyDown:event]; diff --git a/src/MacVim/MMTextView.h b/src/MacVim/MMTextView.h index fd8c450b8a..910fbeb3ba 100644 --- a/src/MacVim/MMTextView.h +++ b/src/MacVim/MMTextView.h @@ -33,6 +33,7 @@ - (void)setMouseShape:(int)shape; - (void)setAntialias:(BOOL)antialias; - (void)setImControl:(BOOL)enable; +- (void)activateIm:(BOOL)enable; // // MMTextStorage methods diff --git a/src/MacVim/MMTextView.m b/src/MacVim/MMTextView.m index 02d5c5d077..797ba44351 100644 --- a/src/MacVim/MMTextView.m +++ b/src/MacVim/MMTextView.m @@ -312,6 +312,11 @@ [helper setImControl:enable]; } +- (void)activateIm:(BOOL)enable +{ + [helper activateIm:enable]; +} + - (NSFont *)font { return [(MMTextStorage*)[self textStorage] font]; diff --git a/src/MacVim/MMTextViewHelper.h b/src/MacVim/MMTextViewHelper.h index 809042b3c7..df7336ce0c 100644 --- a/src/MacVim/MMTextViewHelper.h +++ b/src/MacVim/MMTextViewHelper.h @@ -10,6 +10,11 @@ #import +#if (MAC_OS_X_VERSION_MAX_ALLOWED > MAC_OS_X_VERSION_10_4) +// Need Carbon for TIS...() functions +#import +#endif + enum { // These values are chosen so that the min text view size is not too small @@ -43,6 +48,10 @@ enum { int preEditColumn; BOOL imControl; BOOL imState; +#if (MAC_OS_X_VERSION_MAX_ALLOWED > MAC_OS_X_VERSION_10_4) + TISInputSourceRef lastImSource; + TISInputSourceRef lastAsciiImSource; +#endif } - (void)setTextView:(id)view; @@ -85,5 +94,6 @@ enum { - (void)setMarkedRange:(NSRange)range; - (NSRect)firstRectForCharacterRange:(NSRange)range; - (void)setImControl:(BOOL)enable; +- (void)activateIm:(BOOL)enable; @end diff --git a/src/MacVim/MMTextViewHelper.m b/src/MacVim/MMTextViewHelper.m index fc28ba74ba..00ae27b2c6 100644 --- a/src/MacVim/MMTextViewHelper.m +++ b/src/MacVim/MMTextViewHelper.m @@ -57,6 +57,17 @@ static float MMDragAreaSize = 73.0f; [markedText release]; markedText = nil; [markedTextAttributes release]; markedTextAttributes = nil; +#if (MAC_OS_X_VERSION_MAX_ALLOWED > MAC_OS_X_VERSION_10_4) + if (lastAsciiImSource) { + CFRelease(lastAsciiImSource); + lastAsciiImSource = NULL; + } + if (lastImSource) { + CFRelease(lastImSource); + lastImSource = NULL; + } +#endif + [super dealloc]; } @@ -93,9 +104,6 @@ static float MMDragAreaSize = 73.0f; // returns from this method without releasing and resetting this reference! currentEvent = [event retain]; - if (imControl) - [self checkImState]; - if ([self hasMarkedText]) { // HACK! Need to redisplay manually otherwise the marked text may not // be correctly displayed (e.g. it is still visible after pressing Esc @@ -145,6 +153,11 @@ static float MMDragAreaSize = 73.0f; if (string) [self doKeyDown:string]; + // NOTE: Check IM state _after_ key has been interpreted or we'll pick up + // the old IM state when it has been switched via a keyboard shortcut. + if (imControl) + [self checkImState]; + [currentEvent release]; currentEvent = nil; } @@ -698,8 +711,88 @@ static float MMDragAreaSize = 73.0f; { // This flag corresponds to the (negation of the) 'imd' option. When // enabled changes to the input method are detected and forwarded to the - // backend. + // backend. On 10.5 and later we do not forward changes to the input + // method, instead we let Vim be in complete control. + +#if (MAC_OS_X_VERSION_MAX_ALLOWED > MAC_OS_X_VERSION_10_4) + // The TIS symbols are weakly linked. + if (NULL != TISCopyCurrentKeyboardInputSource) { + // We get here when compiled on 10.5 and running on 10.5 (or later). + + // Save current input source for use when IM is on and get an ASCII + // source for use when IM is off. + if (lastAsciiImSource) CFRelease(lastAsciiImSource); + lastAsciiImSource = TISCopyCurrentASCIICapableKeyboardInputSource(); + if (lastImSource) CFRelease(lastImSource); + lastImSource = TISCopyCurrentKeyboardInputSource(); + } +#endif + + // The imControl flag is only used on 10.4 -- on 10.5 we wait for Vim to + // call activateIm: and never explicitly check if the input source changes. imControl = enable; + ASLogInfo(@"IM control %sabled", enable ? "en" : "dis"); +} + +- (void)activateIm:(BOOL)enable +{ + ASLogDebug(@"Activate IM=%d", enable); + +#if (MAC_OS_X_VERSION_MAX_ALLOWED > MAC_OS_X_VERSION_10_4) + // The TIS symbols are weakly linked. + if (NULL != TISCopyCurrentKeyboardInputSource) { + // We get here when compiled for 10.5 and running on 10.5 (or later) + + TISInputSourceRef ref = NULL; + if (enable) { + // Enable IM: switch back to input source used when IM was last on. + if (lastImSource) + ref = lastImSource; + + // Remember current input source if it is ASCII capable so we can + // switch back to it when IM is once more disabled. + TISInputSourceRef curRef = TISCopyCurrentKeyboardInputSource(); + if (curRef) { + CFBooleanRef boolRef = (CFBooleanRef)TISGetInputSourceProperty( + curRef, kTISPropertyInputSourceIsASCIICapable); + BOOL curIsAscii = boolRef ? CFBooleanGetValue(boolRef) : NO; + + if (curIsAscii) { + if (lastAsciiImSource) CFRelease(lastAsciiImSource); + lastAsciiImSource = curRef; + } else { + CFRelease(curRef); + } + } + } else { + // Disable IM: switch back to ASCII input source that was used when + // IM was last off. + if (lastAsciiImSource) + ref = lastAsciiImSource; + + // Remember current input source so we can switch back to it when + // IM is once more enabled. + if (lastImSource) CFRelease(lastImSource); + lastImSource = TISCopyCurrentKeyboardInputSource(); + } + + if (ref) { + ASLogDebug(@"Change input source: %@", + TISGetInputSourceProperty(ref, kTISPropertyInputSourceID)); + TISSelectInputSource(ref); + } + + return; + } + + // We get here when compiled on 10.5 but running on 10.4 -- fall through + // and use old IM code... +#endif +#if (MAC_OS_X_VERSION_MIN_REQUIRED <= MAC_OS_X_VERSION_10_4) + // NOTE: The IM code is delegated to the frontend since calling it in + // the backend caused weird bugs (second dock icon appearing etc.). + KeyScript(enable ? smKeySysScript : smKeyRoman); +#endif } @end // MMTextViewHelper @@ -801,6 +894,13 @@ static float MMDragAreaSize = 73.0f; - (void)checkImState { +#if (MAC_OS_X_VERSION_MIN_REQUIRED <= MAC_OS_X_VERSION_10_4) +#if (MAC_OS_X_VERSION_MAX_ALLOWED > MAC_OS_X_VERSION_10_4) + if (NULL != TISCopyCurrentKeyboardInputSource) + return; // Compiled for 10.4 -- 10.5, running on 10.5 +#endif + // Compiled for 10.4 or higher, running on 10.4 + // IM is active whenever the current script is the system script and the // system script isn't roman. (Hence IM can only be active when using // non-roman scripts.) @@ -815,6 +915,7 @@ static float MMDragAreaSize = 73.0f; int msgid = state ? ActivatedImMsgID : DeactivatedImMsgID; [[self vimController] sendMessage:msgid data:nil]; } +#endif } - (void)hideMouseCursor diff --git a/src/MacVim/MMVimController.m b/src/MacVim/MMVimController.m index 265d7aad90..36db8b1a38 100644 --- a/src/MacVim/MMVimController.m +++ b/src/MacVim/MMVimController.m @@ -807,11 +807,9 @@ static BOOL isUnsafeMessage(int msgid); flags:[[dict objectForKey:@"flags"] intValue]]; } } else if (ActivateKeyScriptMsgID == msgid) { - // NOTE: The IM code is delegated to the frontend since calling it in - // the backend caused weird bugs (second dock icon appearing etc.). - KeyScript(smKeySysScript); + [[[windowController vimView] textView] activateIm:YES]; } else if (DeactivateKeyScriptMsgID == msgid) { - KeyScript(smKeyRoman); + [[[windowController vimView] textView] activateIm:NO]; } else if (EnableImControlMsgID == msgid) { [[[windowController vimView] textView] setImControl:YES]; } else if (DisableImControlMsgID == msgid) {