1 157 yongsun /* 2 157 yongsun * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. 3 157 yongsun * 4 177 yongsun * Copyright (c) 2008 Sun Microsystems, Inc. All Rights Reserved. 5 157 yongsun * 6 157 yongsun * The contents of this file are subject to the terms of either the GNU Lesser 7 157 yongsun * General Public License Version 2.1 only ("LGPL") or the Common Development and 8 157 yongsun * Distribution License ("CDDL")(collectively, the "License"). You may not use this 9 157 yongsun * file except in compliance with the License. You can obtain a copy of the CDDL at 10 157 yongsun * http://www.opensource.org/licenses/cddl1.php and a copy of the LGPLv2.1 at 11 157 yongsun * http://www.opensource.org/licenses/lgpl-license.php. See the License for the 12 157 yongsun * specific language governing permissions and limitations under the License. When 13 157 yongsun * distributing the software, include this License Header Notice in each file and 14 157 yongsun * include the full text of the License in the License file as well as the 15 157 yongsun * following notice: 16 157 yongsun * 17 157 yongsun * NOTICE PURSUANT TO SECTION 9 OF THE COMMON DEVELOPMENT AND DISTRIBUTION LICENSE 18 157 yongsun * (CDDL) 19 157 yongsun * For Covered Software in this distribution, this License shall be governed by the 20 157 yongsun * laws of the State of California (excluding conflict-of-law provisions). 21 157 yongsun * Any litigation relating to this License shall be subject to the jurisdiction of 22 157 yongsun * the Federal Courts of the Northern District of California and the state courts 23 157 yongsun * of the State of California, with venue lying in Santa Clara County, California. 24 157 yongsun * 25 157 yongsun * Contributor(s): 26 157 yongsun * 27 157 yongsun * If you wish your version of this file to be governed by only the CDDL or only 28 157 yongsun * the LGPL Version 2.1, indicate your decision by adding "[Contributor]" elects to 29 157 yongsun * include this software in this distribution under the [CDDL or LGPL Version 2.1] 30 157 yongsun * license." If you don't indicate a single choice of license, a recipient has the 31 157 yongsun * option to distribute your version of this file under either the CDDL or the LGPL 32 157 yongsun * Version 2.1, or to extend the choice of license to its licensees as provided 33 157 yongsun * above. However, if you add LGPL Version 2.1 code and therefore, elected the LGPL 34 157 yongsun * Version 2 license, then the option applies only if the new code is made subject 35 157 yongsun * to such option by the copyright holder. 36 157 yongsun */ 37 157 yongsun 38 157 yongsun #import "SunPinyinInputController.h" 39 157 yongsun #import "SunPinyinApplicationDelegate.h" 40 157 yongsun #import "imi_imkitwin.h" 41 157 yongsun 42 174 yongsun // forward declaration of 'Private' category 43 174 yongsun @interface SunPinyinController(Private) 44 174 yongsun -(void)tryToSwitchStyle; 45 174 yongsun -(void)createSession; 46 174 yongsun -(void)destroySession; 47 174 yongsun @end 48 174 yongsun 49 174 yongsun // implementation of the public interface 50 157 yongsun @implementation SunPinyinController 51 157 yongsun 52 157 yongsun /* 53 157 yongsun Implement one of the three ways to receive input from the client. 54 157 yongsun Here are the three approaches: 55 157 yongsun 56 157 yongsun 1. Support keybinding. 57 157 yongsun In this approach the system takes each keydown and trys to map the keydown 58 157 yongsun to an action method that the input method has implemented. If an action 59 157 yongsun is found the system calls didCommandBySelector:client:. If no action 60 157 yongsun method is found inputText:client: is called. An input method choosing 61 157 yongsun this approach should implement 62 157 yongsun 63 157 yongsun -(BOOL)inputText:(NSString*)string client:(id)sender; 64 157 yongsun -(BOOL)didCommandBySelector:(SEL)aSelector client:(id)sender; 65 157 yongsun 66 157 yongsun 2. Receive all key events without the keybinding, but do "unpack" the relevant text data. 67 157 yongsun Key events are broken down into the Unicodes, the key code that generated 68 157 yongsun them, and modifier flags. This data is then sent to the input method's 69 157 yongsun inputText:key:modifiers:client: method. For this approach implement: 70 157 yongsun 71 157 yongsun -(BOOL)inputText:(NSString*)string key:(NSInteger)keyCode modifiers:(NSUInteger)flags client:(id)sender; 72 157 yongsun 73 157 yongsun 3. Receive events directly from the Text Services Manager as NSEvent objects. 74 157 yongsun For this approach implement: 75 157 yongsun 76 157 yongsun -(BOOL)handleEvent:(NSEvent*)event client:(id)sender; 77 157 yongsun */ 78 157 yongsun 79 157 yongsun /*! 80 157 yongsun @method 81 157 yongsun @abstract Receive incoming event 82 157 yongsun @discussion This method receives key events from the client application. 83 157 yongsun */ 84 157 yongsun 85 157 yongsun -(BOOL)handleEvent:(NSEvent*)event client:(id)sender 86 157 yongsun { 87 157 yongsun // Return YES to indicate the the key input was received and dealt with. 88 157 yongsun // Key processing will not continue in that case. In other words the 89 157 yongsun // system will not deliver a key down event to the application. 90 157 yongsun // Returning NO means the original key down will be passed on to the client. 91 270 yongsun if (!_pv) return NO; 92 270 yongsun 93 157 yongsun _currentClient = sender; 94 190 yongsun bool handled = NO; 95 190 yongsun NSUInteger modifiers = [event modifierFlags]; 96 274 yongsun SwitchingPolicies policy = [[NSApp delegate] switchingPolicy]; 97 274 yongsun 98 274 yongsun if (SWITCH_BY_CAPS == policy) 99 274 yongsun _englishMode = (modifiers & NSAlphaShiftKeyMask); 100 191 yongsun 101 190 yongsun switch ([event type]) { 102 190 yongsun case NSFlagsChanged: 103 275 yongsun if (SWITCH_BY_SHIFT == policy && modifiers == 0 && 104 275 yongsun _lastEventTypes[1] == NSFlagsChanged && _lastModifiers[1] == NSShiftKeyMask) 105 275 yongsun { 106 275 yongsun if (!(_lastModifiers[0] & NSShiftKeyMask)) 107 275 yongsun _englishMode = !_englishMode; 108 275 yongsun 109 274 yongsun if (_englishMode) 110 274 yongsun [[NSApp delegate] messageNotify:NSLocalizedString(@"Switched to English mode", nil)]; 111 274 yongsun } 112 274 yongsun 113 274 yongsun if (_englishMode) { 114 191 yongsun // We need two spaces to commit in modern style 115 191 yongsun if (_currentStyle == CIMIViewFactory::SVT_MODERN) 116 191 yongsun [self commitComposition:nil]; 117 191 yongsun else 118 191 yongsun _pv->onKeyEvent (' ', ' ', 0); 119 191 yongsun } 120 190 yongsun break; 121 190 yongsun case NSKeyDown: 122 274 yongsun NSInteger keyCode = [event keyCode]; 123 190 yongsun NSString* string = [event characters]; 124 190 yongsun unsigned char keyChar = [string UTF8String][0]; 125 187 yongsun 126 190 yongsun if (modifiers & NSCommandKeyMask) 127 190 yongsun break; 128 187 yongsun 129 274 yongsun if (_englishMode) { 130 274 yongsun if (SWITCH_BY_CAPS == policy && isprint(keyChar)) { 131 190 yongsun string = (modifiers & NSShiftKeyMask)? string: [string lowercaseString]; 132 190 yongsun [self commitString:string]; 133 190 yongsun handled = YES; 134 190 yongsun } 135 190 yongsun break; 136 190 yongsun } 137 190 yongsun 138 190 yongsun [self tryToSwitchStyle]; 139 190 yongsun handled = _pv->onKeyEvent (keyCode, keyChar, modifiers); 140 190 yongsun break; 141 190 yongsun defaults: 142 190 yongsun break; 143 190 yongsun } 144 157 yongsun 145 275 yongsun _lastModifiers [0] = _lastModifiers[1]; 146 275 yongsun _lastEventTypes[0] = _lastEventTypes[1]; 147 275 yongsun _lastModifiers [1] = modifiers; 148 275 yongsun _lastEventTypes[1] = [event type]; 149 190 yongsun return handled; 150 157 yongsun } 151 157 yongsun 152 157 yongsun -(NSUInteger)recognizedEvents:(id)sender 153 157 yongsun { 154 157 yongsun return NSKeyDownMask | NSFlagsChangedMask; 155 190 yongsun } 156 190 yongsun 157 190 yongsun -(void)activateServer:(id)sender 158 190 yongsun { 159 306 yongsun if ([[NSApp delegate] usingUSKbLayout]) 160 306 yongsun [sender overrideKeyboardWithKeyboardNamed:@"com.apple.keylayout.US"]; 161 294 yongsun } 162 294 yongsun 163 294 yongsun -(id)initWithServer:(IMKServer*)server delegate:(id)delegate client:(id)inputClient 164 294 yongsun { 165 306 yongsun if (self = [super initWithServer:server delegate:delegate client:inputClient]) 166 294 yongsun [self createSession]; 167 294 yongsun 168 294 yongsun return self; 169 157 yongsun } 170 157 yongsun 171 174 yongsun -(void)deactivateServer:(id)sender 172 174 yongsun { 173 174 yongsun [[[NSApp delegate] candiWin] hideCandidates]; 174 174 yongsun } 175 174 yongsun 176 157 yongsun /*! 177 157 yongsun @method 178 157 yongsun @abstract Called when a user action was taken that ends an input session. 179 157 yongsun Typically triggered by the user selecting a new input method 180 157 yongsun or keyboard layout. 181 157 yongsun @discussion When this method is called your controller should send the 182 157 yongsun current input buffer to the client via a call to 183 157 yongsun insertText:replacementRange:. Additionally, this is the time 184 157 yongsun to clean up if that is necessary. 185 157 yongsun */ 186 157 yongsun 187 157 yongsun -(void)commitComposition:(id)sender 188 157 yongsun { 189 190 yongsun NSString *string = _currentStyle == CIMIViewFactory::SVT_MODERN? 190 190 yongsun _preeditString: [_preeditString stringByReplacingOccurrencesOfString:@" " withString:@""]; 191 295 yongsun if (string && [string length]) 192 295 yongsun [self commitString:string]; 193 270 yongsun if (_pv) _pv->clearIC(); 194 163 yongsun } 195 163 yongsun 196 163 yongsun -(NSMenu*)menu 197 163 yongsun { 198 163 yongsun return [[NSApp delegate] menu]; 199 163 yongsun } 200 163 yongsun 201 176 yongsun -(void)showPrefPanel:(id)sender 202 176 yongsun { 203 176 yongsun [[NSApp delegate] showPrefPanel:sender]; 204 176 yongsun } 205 176 yongsun 206 190 yongsun -(void)toggleChinesePuncts:(id)sender 207 190 yongsun { 208 190 yongsun [[NSApp delegate] toggleChinesePuncts:sender]; 209 294 yongsun if (_pv) _pv->setStatusAttrValue(CIMIWinHandler::STATUS_ID_FULLPUNC, 210 294 yongsun [[NSApp delegate] inputChinesePuncts]); 211 190 yongsun } 212 190 yongsun 213 190 yongsun -(void)toggleFullSymbols:(id)sender 214 190 yongsun { 215 190 yongsun [[NSApp delegate] toggleFullSymbols:sender]; 216 294 yongsun if (_pv) _pv->setStatusAttrValue(CIMIWinHandler::STATUS_ID_FULLSIMBOL, 217 294 yongsun [[NSApp delegate] inputFullSymbols]); 218 190 yongsun } 219 190 yongsun 220 174 yongsun -(void)dealloc 221 174 yongsun { 222 270 yongsun [self destroySession]; 223 174 yongsun [super dealloc]; 224 174 yongsun } 225 174 yongsun 226 174 yongsun -(void)commitString:(NSString*)string 227 174 yongsun { 228 295 yongsun // fixed that IME does not work with M$ powerpoint 2008 229 295 yongsun _caret = [string length]; 230 295 yongsun [self showPreeditString:[string retain]]; 231 295 yongsun 232 190 yongsun [_currentClient insertText:string 233 190 yongsun replacementRange:NSMakeRange(NSNotFound, NSNotFound)]; 234 187 yongsun 235 190 yongsun [_preeditString release]; 236 190 yongsun _preeditString = nil; 237 187 yongsun 238 174 yongsun [[[NSApp delegate] candiWin] hideCandidates]; 239 174 yongsun } 240 174 yongsun 241 190 yongsun // firefox would call 'commitComposition:' when preedit is emptied 242 174 yongsun -(void)showPreeditString:(NSString*)string 243 174 yongsun { 244 190 yongsun // cache the preedit string 245 190 yongsun [_preeditString release]; 246 187 yongsun _preeditString = [string retain]; 247 187 yongsun 248 187 yongsun NSDictionary* attrs; 249 187 yongsun NSAttributedString* attrString; 250 187 yongsun 251 187 yongsun attrs = [self markForStyle:kTSMHiliteSelectedRawText atRange:NSMakeRange(0, [string length])]; 252 187 yongsun attrString = [[NSAttributedString alloc] initWithString:string attributes:attrs]; 253 187 yongsun 254 174 yongsun // Range (0, 0) will clear the marked text 255 187 yongsun [_currentClient setMarkedText:attrString 256 187 yongsun selectionRange:NSMakeRange(_caret, 0) 257 174 yongsun replacementRange:NSMakeRange(NSNotFound, NSNotFound)]; 258 187 yongsun 259 187 yongsun [attrString release]; 260 187 yongsun } 261 187 yongsun 262 187 yongsun -(void)setCaret:(int)caret andCandiStart:(int)start 263 187 yongsun { 264 187 yongsun _caret = caret; 265 187 yongsun _candiStart = start; 266 174 yongsun } 267 174 yongsun 268 174 yongsun -(void)showCandidates:(NSArray*)candidates 269 174 yongsun { 270 174 yongsun NSRect cursorRect; 271 187 yongsun int curIdx = _currentStyle == CIMIViewFactory::SVT_MODERN? 272 187 yongsun _caret:_candiStart; 273 187 yongsun [_currentClient attributesForCharacterIndex:curIdx lineHeightRectangle:&cursorRect]; 274 174 yongsun [[[NSApp delegate] candiWin] showCandidates:candidates around:cursorRect]; 275 174 yongsun } 276 174 yongsun 277 190 yongsun -(void)updateStatus:(int)key withValue:(int)value 278 190 yongsun { 279 190 yongsun switch (key) { 280 190 yongsun case CIMIWinHandler::STATUS_ID_FULLPUNC: 281 190 yongsun if (value != [[NSApp delegate] inputChinesePuncts]) 282 190 yongsun [self toggleChinesePuncts:nil]; 283 190 yongsun break; 284 190 yongsun case CIMIWinHandler::STATUS_ID_FULLSIMBOL: 285 190 yongsun if (value != [[NSApp delegate] inputFullSymbols]) 286 190 yongsun [self toggleFullSymbols:nil]; 287 190 yongsun break; 288 190 yongsun default: 289 190 yongsun break; 290 190 yongsun } 291 190 yongsun } 292 190 yongsun 293 174 yongsun @end // SunPinyinController 294 174 yongsun 295 174 yongsun 296 174 yongsun // implementation of private interface 297 174 yongsun @implementation SunPinyinController(Private) 298 174 yongsun 299 163 yongsun -(void)tryToSwitchStyle 300 163 yongsun { 301 163 yongsun CSunpinyinOptions *opts = [[NSApp delegate] preferences]; 302 174 yongsun 303 164 yongsun if (*opts == _pv->getPreference()) 304 164 yongsun return; 305 164 yongsun 306 261 yongsun if (_currentStyle == opts->m_ViewType && 307 261 yongsun _currentCharset == opts->m_GBK) { 308 176 yongsun _pv->s_CandiWindowSize = opts->m_CandiWindowSize; 309 174 yongsun _pv->setPreference(opts); 310 174 yongsun return; 311 163 yongsun } 312 174 yongsun 313 174 yongsun /* input style changed, have to recreate session */ 314 174 yongsun [self destroySession]; 315 174 yongsun [self createSession]; 316 163 yongsun } 317 163 yongsun 318 163 yongsun -(void)createSession 319 163 yongsun { 320 272 yongsun if (![[NSApp delegate] sysData]) 321 272 yongsun return; 322 272 yongsun 323 163 yongsun CSunpinyinOptions *opts = [[NSApp delegate] preferences]; 324 163 yongsun _currentStyle = opts->m_ViewType; 325 261 yongsun _currentCharset = opts->m_GBK; 326 190 yongsun 327 190 yongsun // create view and set its properties and preferences 328 163 yongsun _pv = CIMIViewFactory::createView(_currentStyle); 329 190 yongsun _pv->setStatusAttrValue(CIMIWinHandler::STATUS_ID_FULLPUNC, 330 190 yongsun [[NSApp delegate] inputChinesePuncts]); 331 190 yongsun _pv->setStatusAttrValue(CIMIWinHandler::STATUS_ID_FULLSIMBOL, 332 190 yongsun [[NSApp delegate] inputFullSymbols]); 333 190 yongsun _pv->s_CandiWindowSize = opts->m_CandiWindowSize; 334 190 yongsun _pv->setPreference(opts); 335 163 yongsun 336 190 yongsun // create ic and attach to the view 337 163 yongsun CIMIContext *pic = new CIMIContext(); 338 163 yongsun pic->setNonCompleteSyllable(true); 339 163 yongsun pic->setCoreData ([[NSApp delegate] sysData]); 340 163 yongsun pic->setHistoryMemory([[NSApp delegate] history]); 341 163 yongsun pic->clear(); 342 163 yongsun _pv->attachIC(pic); 343 190 yongsun 344 190 yongsun // create callback handler and attach to the view 345 163 yongsun CIMKitWindowHandler* pwh = new CIMKitWindowHandler(self); 346 163 yongsun _pv->attachWinHandler(pwh); 347 163 yongsun } 348 163 yongsun 349 163 yongsun -(void)destroySession 350 163 yongsun { 351 270 yongsun if (!_pv) return; 352 270 yongsun 353 190 yongsun [[NSApp delegate] saveHistory]; 354 190 yongsun delete _pv->getIC(); 355 190 yongsun delete _pv->getWinHandler(); 356 190 yongsun delete _pv; 357 190 yongsun _pv = nil; 358 157 yongsun } 359 157 yongsun 360 174 yongsun @end // SunPinyinController(Private) 361