2
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
4
* Copyright (c) 2008 Sun Microsystems, Inc. All Rights Reserved.
6
* The contents of this file are subject to the terms of either the GNU Lesser
7
* General Public License Version 2.1 only ("LGPL") or the Common Development and
8
* Distribution License ("CDDL")(collectively, the "License"). You may not use this
9
* file except in compliance with the License. You can obtain a copy of the CDDL at
10
* http://www.opensource.org/licenses/cddl1.php and a copy of the LGPLv2.1 at
11
* http://www.opensource.org/licenses/lgpl-license.php. See the License for the
12
* specific language governing permissions and limitations under the License. When
13
* distributing the software, include this License Header Notice in each file and
14
* include the full text of the License in the License file as well as the
17
* NOTICE PURSUANT TO SECTION 9 OF THE COMMON DEVELOPMENT AND DISTRIBUTION LICENSE
19
* For Covered Software in this distribution, this License shall be governed by the
20
* laws of the State of California (excluding conflict-of-law provisions).
21
* Any litigation relating to this License shall be subject to the jurisdiction of
22
* the Federal Courts of the Northern District of California and the state courts
23
* of the State of California, with venue lying in Santa Clara County, California.
27
* If you wish your version of this file to be governed by only the CDDL or only
28
* the LGPL Version 2.1, indicate your decision by adding "[Contributor]" elects to
29
* include this software in this distribution under the [CDDL or LGPL Version 2.1]
30
* license." If you don't indicate a single choice of license, a recipient has the
31
* option to distribute your version of this file under either the CDDL or the LGPL
32
* Version 2.1, or to extend the choice of license to its licensees as provided
33
* above. However, if you add LGPL Version 2.1 code and therefore, elected the LGPL
34
* Version 2 license, then the option applies only if the new code is made subject
35
* to such option by the copyright holder.
38
#import "SunPinyinInputController.h"
39
#import "SunPinyinApplicationDelegate.h"
40
#import "macos_keycode.h"
41
#import "imi_session_wrapper.h"
43
// forward declaration of 'Private' category
44
@interface SunPinyinController(Private)
46
-(void)destroySession;
49
// implementation of the public interface
50
@implementation SunPinyinController
53
Implement one of the three ways to receive input from the client.
54
Here are the three approaches:
56
1. Support keybinding.
57
In this approach the system takes each keydown and trys to map the keydown
58
to an action method that the input method has implemented. If an action
59
is found the system calls didCommandBySelector:client:. If no action
60
method is found inputText:client: is called. An input method choosing
61
this approach should implement
63
-(BOOL)inputText:(NSString*)string client:(id)sender;
64
-(BOOL)didCommandBySelector:(SEL)aSelector client:(id)sender;
66
2. Receive all key events without the keybinding, but do "unpack" the relevant text data.
67
Key events are broken down into the Unicodes, the key code that generated
68
them, and modifier flags. This data is then sent to the input method's
69
inputText:key:modifiers:client: method. For this approach implement:
71
-(BOOL)inputText:(NSString*)string key:(NSInteger)keyCode modifiers:(NSUInteger)flags client:(id)sender;
73
3. Receive events directly from the Text Services Manager as NSEvent objects.
74
For this approach implement:
76
-(BOOL)handleEvent:(NSEvent*)event client:(id)sender;
81
@abstract Receive incoming event
82
@discussion This method receives key events from the client application.
85
-(BOOL)handleEvent:(NSEvent*)event client:(id)sender
87
// Return YES to indicate the the key input was received and dealt with.
88
// Key processing will not continue in that case. In other words the
89
// system will not deliver a key down event to the application.
90
// Returning NO means the original key down will be passed on to the client.
91
if (!_session->isValid()) return NO;
93
_currentClient = sender;
95
NSUInteger modifiers = [event modifierFlags];
96
SwitchingPolicies switchPolicy = [[NSApp delegate] switchingPolicy];
97
CommitPolicies commitPolicy = [[NSApp delegate] commitPolicy];
99
if (SWITCH_BY_CAPS == switchPolicy) {
100
_englishMode = (modifiers & NSAlphaShiftKeyMask);
101
_session->switchInputMode(_englishMode, commitPolicy);
104
switch ([event type]) {
106
// FIXME: a dirty workaround for chrome sending duplicated NSFlagsChanged event
107
if (_lastEventTypes[1] == NSFlagsChanged && _lastModifiers[1] == modifiers)
110
if (SWITCH_BY_SHIFT == switchPolicy && modifiers == 0 &&
111
_lastEventTypes[1] == NSFlagsChanged && _lastModifiers[1] == NSShiftKeyMask &&
112
!(_lastModifiers[0] & NSShiftKeyMask))
114
_englishMode = !_englishMode;
115
_session->switchInputMode(_englishMode, commitPolicy);
118
[[NSApp delegate] messageNotify:NSLocalizedString(@"Switched to English mode", nil)];
122
NSInteger keyCode = [event keyCode];
123
NSString* string = [event characters];
124
unsigned char keyChar = [string UTF8String][0];
127
if (SWITCH_BY_CAPS == switchPolicy && isprint(keyChar)) {
128
string = (modifiers & NSShiftKeyMask)? string: [string lowercaseString];
129
[self commitString:string];
135
// translate osx keyevents to ime keyevents
136
CKeyEvent key_event = osx_keyevent_to_ime_keyevent (keyCode, keyChar, modifiers);
137
handled = _session->onKeyEvent (key_event);
143
_lastModifiers [0] = _lastModifiers[1];
144
_lastEventTypes[0] = _lastEventTypes[1];
145
_lastModifiers [1] = modifiers;
146
_lastEventTypes[1] = [event type];
150
-(NSUInteger)recognizedEvents:(id)sender
152
return NSKeyDownMask | NSFlagsChangedMask;
155
-(void)activateServer:(id)sender
157
if ([[NSApp delegate] usingUSKbLayout])
158
[sender overrideKeyboardWithKeyboardNamed:@"com.apple.keylayout.US"];
161
-(id)initWithServer:(IMKServer*)server delegate:(id)delegate client:(id)inputClient
163
if (self = [super initWithServer:server delegate:delegate client:inputClient])
164
[self createSession];
169
-(void)deactivateServer:(id)sender
171
[[[NSApp delegate] candiWin] hideCandidates];
173
NSString *string = [_preeditString stringByReplacingOccurrencesOfString:@" " withString:@""];
174
if (string && [string length])
175
[self commitString:string];
181
@abstract Called when a user action was taken that ends an input session.
182
Typically triggered by the user selecting a new input method
184
@discussion When this method is called your controller should send the
185
current input buffer to the client via a call to
186
insertText:replacementRange:. Additionally, this is the time
187
to clean up if that is necessary.
190
-(void)commitComposition:(id)sender
192
// FIXME: chrome's address bar issues this callback when showing suggestions.
193
if ([[sender bundleIdentifier] isEqualToString:@"com.google.Chrome"])
196
NSString *string = [_preeditString stringByReplacingOccurrencesOfString:@" " withString:@""];
197
if (string && [string length])
198
[self commitString:string];
204
return [[NSApp delegate] menu];
207
-(void)showPrefPanel:(id)sender
209
[[NSApp delegate] showPrefPanel:sender];
212
-(void)toggleChinesePuncts:(id)sender
214
[[NSApp delegate] toggleChinesePuncts:sender];
215
_session->setStatusAttrValue(CIMIWinHandler::STATUS_ID_FULLPUNC,
216
[[NSApp delegate] inputChinesePuncts]);
219
-(void)toggleFullSymbols:(id)sender
221
[[NSApp delegate] toggleFullSymbols:sender];
222
_session->setStatusAttrValue(CIMIWinHandler::STATUS_ID_FULLSYMBOL,
223
[[NSApp delegate] inputFullSymbols]);
228
[self destroySession];
232
-(void)commitString:(NSString*)string
234
// fixed that IME does not work with M$ powerpoint 2008
235
_caret = [string length];
236
[self showPreeditString:[string retain]];
238
[_currentClient insertText:string
239
replacementRange:NSMakeRange(NSNotFound, NSNotFound)];
241
[_preeditString release];
242
_preeditString = nil;
244
[[[NSApp delegate] candiWin] hideCandidates];
247
// firefox would call 'commitComposition:' when preedit is emptied
248
-(void)showPreeditString:(NSString*)string
250
// cache the preedit string
251
[_preeditString release];
252
_preeditString = [string retain];
255
NSAttributedString* attrString;
257
attrs = [self markForStyle:kTSMHiliteSelectedRawText atRange:NSMakeRange(0, [string length])];
258
attrString = [[NSAttributedString alloc] initWithString:string attributes:attrs];
260
// Range (0, 0) will clear the marked text
261
[_currentClient setMarkedText:attrString
262
selectionRange:NSMakeRange(_caret, 0)
263
replacementRange:NSMakeRange(NSNotFound, NSNotFound)];
265
[attrString release];
268
-(void)setCaret:(int)caret andCandiStart:(int)start
274
-(void)showCandidates:(NSArray*)candidates
277
int curIdx = _candiStart;
278
[_currentClient attributesForCharacterIndex:curIdx lineHeightRectangle:&cursorRect];
279
[[[NSApp delegate] candiWin] showCandidates:candidates around:cursorRect];
282
-(void)updateStatus:(int)key withValue:(int)value
285
case CIMIWinHandler::STATUS_ID_FULLPUNC:
286
if (value != [[NSApp delegate] inputChinesePuncts])
287
[self toggleChinesePuncts:nil];
289
case CIMIWinHandler::STATUS_ID_FULLSYMBOL:
290
if (value != [[NSApp delegate] inputFullSymbols])
291
[self toggleFullSymbols:nil];
298
-(void)windowHandlerTimerCallback:(NSTimer*)timer
301
_session->windowHandlerTimerCallback();
305
@end // SunPinyinController
308
// implementation of private interface
309
@implementation SunPinyinController(Private)
313
_session = new CSunpinyinSessionWrapper (self);
316
-(void)destroySession
321
@end // SunPinyinController(Private)