Home | History | Annotate | Download | only in macos
      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