~ubuntu-branches/ubuntu/quantal/sunpinyin/quantal

« back to all changes in this revision

Viewing changes to wrapper/macos/SunPinyinInputController.mm

  • Committer: Package Import Robot
  • Author(s): YunQiang Su
  • Date: 2012-04-11 03:06:40 UTC
  • mfrom: (1.1.4) (1.2.8 sid)
  • Revision ID: package-import@ubuntu.com-20120411030640-8mxepz5e6wffy87c
Tags: 2.0.3+git20120404-1
* Medium urgency for fixing RC bug.
* New upstream commit: fix FTBFS with gcc-4.7 (Closes: #667385).
* Add Multi-Arch: same to libsunpinyin3, -dev and -dbg.
* Add YunQiang Su to uploaders.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/*
2
 
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3
 
 * 
4
 
 * Copyright (c) 2008 Sun Microsystems, Inc. All Rights Reserved.
5
 
 * 
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
15
 
 * following notice:
16
 
 * 
17
 
 * NOTICE PURSUANT TO SECTION 9 OF THE COMMON DEVELOPMENT AND DISTRIBUTION LICENSE
18
 
 * (CDDL)
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.
24
 
 * 
25
 
 * Contributor(s):
26
 
 * 
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. 
36
 
 */
37
 
 
38
 
#import "SunPinyinInputController.h"
39
 
#import "SunPinyinApplicationDelegate.h"
40
 
#import "macos_keycode.h"
41
 
#import "imi_session_wrapper.h"
42
 
 
43
 
// forward declaration of 'Private' category
44
 
@interface SunPinyinController(Private) 
45
 
-(void)createSession;
46
 
-(void)destroySession;
47
 
@end 
48
 
 
49
 
// implementation of the public interface
50
 
@implementation SunPinyinController
51
 
 
52
 
/*
53
 
Implement one of the three ways to receive input from the client. 
54
 
Here are the three approaches:
55
 
                 
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
62
 
 
63
 
    -(BOOL)inputText:(NSString*)string client:(id)sender;
64
 
    -(BOOL)didCommandBySelector:(SEL)aSelector client:(id)sender;
65
 
                        
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:
70
 
 
71
 
     -(BOOL)inputText:(NSString*)string key:(NSInteger)keyCode modifiers:(NSUInteger)flags client:(id)sender;
72
 
                        
73
 
  3. Receive events directly from the Text Services Manager as NSEvent objects.
74
 
    For this approach implement:
75
 
 
76
 
    -(BOOL)handleEvent:(NSEvent*)event client:(id)sender;
77
 
*/
78
 
 
79
 
/*!
80
 
    @method     
81
 
    @abstract   Receive incoming event
82
 
    @discussion This method receives key events from the client application. 
83
 
*/
84
 
 
85
 
-(BOOL)handleEvent:(NSEvent*)event client:(id)sender
86
 
{
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;
92
 
 
93
 
    _currentClient = sender;
94
 
    bool handled = NO;
95
 
    NSUInteger modifiers = [event modifierFlags];
96
 
    SwitchingPolicies switchPolicy = [[NSApp delegate] switchingPolicy];
97
 
    CommitPolicies commitPolicy = [[NSApp delegate] commitPolicy];
98
 
 
99
 
    if (SWITCH_BY_CAPS == switchPolicy) {
100
 
        _englishMode = (modifiers & NSAlphaShiftKeyMask);
101
 
        _session->switchInputMode(_englishMode, commitPolicy);
102
 
    }
103
 
 
104
 
    switch ([event type]) {
105
 
        case NSFlagsChanged:
106
 
            // FIXME: a dirty workaround for chrome sending duplicated NSFlagsChanged event
107
 
            if (_lastEventTypes[1] == NSFlagsChanged && _lastModifiers[1] == modifiers)
108
 
                return YES;
109
 
 
110
 
            if (SWITCH_BY_SHIFT == switchPolicy && modifiers == 0 && 
111
 
                _lastEventTypes[1] == NSFlagsChanged && _lastModifiers[1] == NSShiftKeyMask &&
112
 
                !(_lastModifiers[0] & NSShiftKeyMask))
113
 
            {
114
 
                _englishMode = !_englishMode;
115
 
                _session->switchInputMode(_englishMode, commitPolicy);
116
 
 
117
 
                if (_englishMode)
118
 
                    [[NSApp delegate] messageNotify:NSLocalizedString(@"Switched to English mode", nil)];
119
 
            }
120
 
            break;
121
 
        case NSKeyDown:
122
 
            NSInteger keyCode = [event keyCode];
123
 
            NSString* string = [event characters];
124
 
            unsigned char keyChar = [string UTF8String][0];
125
 
 
126
 
            if (_englishMode) {
127
 
                if (SWITCH_BY_CAPS == switchPolicy && isprint(keyChar)) {
128
 
                    string = (modifiers & NSShiftKeyMask)? string: [string lowercaseString];
129
 
                    [self commitString:string];
130
 
                    handled = YES;
131
 
                }
132
 
                break;
133
 
            }
134
 
            
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);
138
 
            break;
139
 
        defaults:
140
 
            break;
141
 
    }
142
 
 
143
 
    _lastModifiers [0] = _lastModifiers[1];
144
 
    _lastEventTypes[0] = _lastEventTypes[1];
145
 
    _lastModifiers [1] = modifiers;
146
 
    _lastEventTypes[1] = [event type];
147
 
    return handled;
148
 
}
149
 
 
150
 
-(NSUInteger)recognizedEvents:(id)sender
151
 
{
152
 
    return NSKeyDownMask | NSFlagsChangedMask;
153
 
}
154
 
 
155
 
-(void)activateServer:(id)sender
156
 
{
157
 
    if ([[NSApp delegate] usingUSKbLayout])
158
 
        [sender overrideKeyboardWithKeyboardNamed:@"com.apple.keylayout.US"];
159
 
}
160
 
 
161
 
-(id)initWithServer:(IMKServer*)server delegate:(id)delegate client:(id)inputClient
162
 
{
163
 
    if (self = [super initWithServer:server delegate:delegate client:inputClient])
164
 
        [self createSession];
165
 
 
166
 
    return self;
167
 
}
168
 
 
169
 
-(void)deactivateServer:(id)sender
170
 
{
171
 
    [[[NSApp delegate] candiWin] hideCandidates];
172
 
 
173
 
    NSString *string = [_preeditString stringByReplacingOccurrencesOfString:@" " withString:@""];
174
 
    if (string && [string length])
175
 
        [self commitString:string];
176
 
    _session->clear();
177
 
}
178
 
 
179
 
/*!
180
 
    @method     
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 
183
 
                or keyboard layout.
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.
188
 
*/
189
 
 
190
 
-(void)commitComposition:(id)sender 
191
 
{
192
 
    // FIXME: chrome's address bar issues this callback when showing suggestions. 
193
 
    if ([[sender bundleIdentifier] isEqualToString:@"com.google.Chrome"])
194
 
        return;
195
 
 
196
 
    NSString *string = [_preeditString stringByReplacingOccurrencesOfString:@" " withString:@""];
197
 
    if (string && [string length])
198
 
        [self commitString:string];
199
 
    _session->clear();
200
 
}
201
 
 
202
 
-(NSMenu*)menu
203
 
{
204
 
    return [[NSApp delegate] menu];
205
 
}
206
 
 
207
 
-(void)showPrefPanel:(id)sender
208
 
{
209
 
    [[NSApp delegate] showPrefPanel:sender];
210
 
}
211
 
 
212
 
-(void)toggleChinesePuncts:(id)sender
213
 
{
214
 
    [[NSApp delegate] toggleChinesePuncts:sender];
215
 
    _session->setStatusAttrValue(CIMIWinHandler::STATUS_ID_FULLPUNC,
216
 
                                 [[NSApp delegate] inputChinesePuncts]);      
217
 
}
218
 
 
219
 
-(void)toggleFullSymbols:(id)sender
220
 
{
221
 
    [[NSApp delegate] toggleFullSymbols:sender];
222
 
    _session->setStatusAttrValue(CIMIWinHandler::STATUS_ID_FULLSYMBOL,
223
 
                                 [[NSApp delegate] inputFullSymbols]);    
224
 
}
225
 
 
226
 
-(void)dealloc 
227
 
{
228
 
    [self destroySession];
229
 
    [super dealloc];
230
 
}
231
 
 
232
 
-(void)commitString:(NSString*)string
233
 
{
234
 
    // fixed that IME does not work with M$ powerpoint 2008
235
 
    _caret = [string length];
236
 
    [self showPreeditString:[string retain]];
237
 
 
238
 
    [_currentClient insertText:string 
239
 
                    replacementRange:NSMakeRange(NSNotFound, NSNotFound)];
240
 
 
241
 
    [_preeditString release];
242
 
    _preeditString = nil;
243
 
 
244
 
    [[[NSApp delegate] candiWin] hideCandidates];
245
 
}
246
 
 
247
 
// firefox would call 'commitComposition:' when preedit is emptied
248
 
-(void)showPreeditString:(NSString*)string
249
 
{
250
 
    // cache the preedit string
251
 
    [_preeditString release];
252
 
    _preeditString = [string retain];
253
 
 
254
 
    NSDictionary*       attrs;
255
 
    NSAttributedString* attrString;
256
 
 
257
 
    attrs = [self markForStyle:kTSMHiliteSelectedRawText atRange:NSMakeRange(0, [string length])];
258
 
    attrString = [[NSAttributedString alloc] initWithString:string attributes:attrs];
259
 
 
260
 
    // Range (0, 0) will clear the marked text
261
 
    [_currentClient setMarkedText:attrString
262
 
                    selectionRange:NSMakeRange(_caret, 0) 
263
 
                    replacementRange:NSMakeRange(NSNotFound, NSNotFound)];
264
 
 
265
 
    [attrString release];
266
 
}
267
 
 
268
 
-(void)setCaret:(int)caret andCandiStart:(int)start
269
 
{
270
 
    _caret = caret;
271
 
    _candiStart = start;
272
 
}
273
 
 
274
 
-(void)showCandidates:(NSArray*)candidates
275
 
{
276
 
    NSRect cursorRect;
277
 
    int curIdx = _candiStart;
278
 
    [_currentClient attributesForCharacterIndex:curIdx lineHeightRectangle:&cursorRect];
279
 
    [[[NSApp delegate] candiWin] showCandidates:candidates around:cursorRect];
280
 
}
281
 
 
282
 
-(void)updateStatus:(int)key withValue:(int)value
283
 
{
284
 
    switch (key) {
285
 
    case CIMIWinHandler::STATUS_ID_FULLPUNC:
286
 
        if (value != [[NSApp delegate] inputChinesePuncts])
287
 
            [self toggleChinesePuncts:nil];
288
 
        break;
289
 
    case CIMIWinHandler::STATUS_ID_FULLSYMBOL:
290
 
        if (value != [[NSApp delegate] inputFullSymbols])
291
 
            [self toggleFullSymbols:nil];
292
 
        break;
293
 
    default:
294
 
        break;
295
 
    }
296
 
}
297
 
 
298
 
-(void)windowHandlerTimerCallback:(NSTimer*)timer
299
 
{    
300
 
    if (timer) {
301
 
        _session->windowHandlerTimerCallback();
302
 
    }
303
 
}
304
 
 
305
 
@end // SunPinyinController 
306
 
 
307
 
 
308
 
// implementation of private interface
309
 
@implementation SunPinyinController(Private)
310
 
 
311
 
-(void)createSession
312
 
{
313
 
    _session = new CSunpinyinSessionWrapper (self);
314
 
}
315
 
 
316
 
-(void)destroySession
317
 
{
318
 
    delete _session;
319
 
    _session = nil;
320
 
}
321
 
@end // SunPinyinController(Private)