1/*
2 * Copyright (C) 2010 Apple Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 *    notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 *    notice, this list of conditions and the following disclaimer in the
11 *    documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23 * THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#import "config.h"
27#import "WKTextInputWindowController.h"
28
29#if USE(APPKIT)
30
31#import <WebKitSystemInterface.h>
32
33@interface WKTextInputView : NSTextView {
34}
35@end
36
37@implementation WKTextInputView
38
39- (NSArray *)validAttributesForMarkedText
40{
41    // Let TSM know that a bottom input window would be created for marked text.
42    NSArray *regularAttributes = [super validAttributesForMarkedText];
43    NSMutableArray *floatingWindowAttributes = [NSMutableArray arrayWithArray:regularAttributes];
44    [floatingWindowAttributes addObject:@"__NSUsesFloatingInputWindow"];
45    return floatingWindowAttributes;
46}
47
48@end
49
50@interface WKTextInputPanel : NSPanel {
51    NSTextView *_inputTextView;
52}
53
54- (NSTextInputContext *)_inputContext;
55- (BOOL)_interpretKeyEvent:(NSEvent *)event usingLegacyCocoaTextInput:(BOOL)usingLegacyCocoaTextInput string:(NSString **)string;
56
57- (BOOL)_hasMarkedText;
58- (void)_unmarkText;
59
60@end
61
62#define inputWindowHeight 20
63
64@implementation WKTextInputPanel
65
66- (void)dealloc
67{
68    [[NSNotificationCenter defaultCenter] removeObserver:self];
69
70    [_inputTextView release];
71
72    [super dealloc];
73}
74
75- (id)init
76{
77    self = [super initWithContentRect:NSZeroRect styleMask:WKGetInputPanelWindowStyle() backing:NSBackingStoreBuffered defer:YES];
78    if (!self)
79        return nil;
80
81    // Set the frame size.
82    NSRect visibleFrame = [[NSScreen mainScreen] visibleFrame];
83    NSRect frame = NSMakeRect(visibleFrame.origin.x, visibleFrame.origin.y, visibleFrame.size.width, inputWindowHeight);
84
85    [self setFrame:frame display:NO];
86
87    _inputTextView = [[WKTextInputView alloc] initWithFrame:[(NSView *)self.contentView frame]];
88    _inputTextView.autoresizingMask = NSViewWidthSizable | NSViewHeightSizable | NSViewMaxXMargin | NSViewMinXMargin | NSViewMaxYMargin | NSViewMinYMargin;
89
90    NSScrollView* scrollView = [[NSScrollView alloc] initWithFrame:[(NSView *)self.contentView frame]];
91    scrollView.documentView = _inputTextView;
92    self.contentView = scrollView;
93    [scrollView release];
94
95    [self setFloatingPanel:YES];
96
97    return self;
98}
99
100- (void)_unmarkText
101{
102    [_inputTextView setString:@""];
103    [self orderOut:nil];
104}
105
106- (BOOL)_interpretKeyEvent:(NSEvent *)event usingLegacyCocoaTextInput:(BOOL)usingLegacyCocoaTextInput string:(NSString **)string
107{
108#pragma clang diagnostic push
109#pragma clang diagnostic ignored "-Wdeprecated-declarations"
110    BOOL hadMarkedText = [_inputTextView hasMarkedText];
111#pragma clang diagnostic pop
112
113    *string = nil;
114
115    // Let TSM know that a bottom input window would be created for marked text.
116    // FIXME: Can be removed once we can rely on __NSUsesFloatingInputWindow (or a better API) being available everywhere.
117    EventRef carbonEvent = static_cast<EventRef>(const_cast<void*>([event eventRef]));
118    if (carbonEvent) {
119        Boolean ignorePAH = true;
120        SetEventParameter(carbonEvent, 'iPAH', typeBoolean, sizeof(ignorePAH), &ignorePAH);
121    }
122
123    if (![[_inputTextView inputContext] handleEvent:event])
124        return NO;
125
126#pragma clang diagnostic push
127#pragma clang diagnostic ignored "-Wdeprecated-declarations"
128    if ([_inputTextView hasMarkedText]) {
129#pragma clang diagnostic pop
130        // Don't show the input method window for dead keys
131        if ([[event characters] length] > 0)
132            [self orderFront:nil];
133
134        return YES;
135    }
136
137    bool shouldReturnTextString = hadMarkedText;
138
139    // In the updated Cocoa text input model spec, we always want to return the text even if the text view didn't have marked text.
140    if (!usingLegacyCocoaTextInput)
141        shouldReturnTextString = true;
142
143    if (shouldReturnTextString) {
144        [[_inputTextView inputContext] discardMarkedText]; // Don't show Cangjie predictive candidates, cf. <rdar://14458297>.
145        [self orderOut:nil];
146
147        NSString *text = [[_inputTextView textStorage] string];
148        if ([text length] > 0)
149            *string = [[text copy] autorelease];
150    }
151
152    [_inputTextView setString:@""];
153    return hadMarkedText;
154}
155
156- (NSTextInputContext *)_inputContext
157{
158    return [_inputTextView inputContext];
159}
160
161- (BOOL)_hasMarkedText
162{
163#pragma clang diagnostic push
164#pragma clang diagnostic ignored "-Wdeprecated-declarations"
165    return [_inputTextView hasMarkedText];
166#pragma clang diagnostic pop
167}
168
169@end
170
171@implementation WKTextInputWindowController
172
173+ (WKTextInputWindowController *)sharedTextInputWindowController
174{
175    static WKTextInputWindowController *textInputWindowController;
176    if (!textInputWindowController)
177        textInputWindowController = [[WKTextInputWindowController alloc] init];
178
179    return textInputWindowController;
180}
181
182- (id)init
183{
184    self = [super init];
185    if (!self)
186        return nil;
187
188    _panel = [[WKTextInputPanel alloc] init];
189
190    return self;
191}
192
193- (NSTextInputContext *)inputContext
194{
195    return [_panel _inputContext];
196}
197
198- (BOOL)hasMarkedText
199{
200    return [_panel _hasMarkedText];
201}
202
203- (BOOL)interpretKeyEvent:(NSEvent *)event usingLegacyCocoaTextInput:(BOOL)usingLegacyCocoaTextInput string:(NSString **)string
204{
205    return [_panel _interpretKeyEvent:event usingLegacyCocoaTextInput:usingLegacyCocoaTextInput string:string];
206}
207
208- (void)unmarkText
209{
210    [_panel _unmarkText];
211}
212
213@end
214
215#endif // USE(APPKIT)
216