Upstream version 10.39.225.0
[platform/framework/web/crosswalk.git] / src / ui / views / cocoa / bridged_content_view.mm
1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #import "ui/views/cocoa/bridged_content_view.h"
6
7 #include "base/logging.h"
8 #import "base/mac/scoped_nsobject.h"
9 #include "base/strings/sys_string_conversions.h"
10 #include "ui/base/ime/text_input_client.h"
11 #include "ui/gfx/canvas_paint_mac.h"
12 #include "ui/gfx/geometry/rect.h"
13 #include "ui/strings/grit/ui_strings.h"
14 #include "ui/views/view.h"
15 #include "ui/views/widget/widget.h"
16
17 @interface BridgedContentView ()
18
19 // Translates the location of |theEvent| to toolkit-views coordinates and passes
20 // the event to NativeWidgetMac for handling.
21 - (void)handleMouseEvent:(NSEvent*)theEvent;
22
23 // Execute a command on the currently focused TextInputClient.
24 // |commandId| should be a resource ID from ui_strings.grd.
25 - (void)doCommandByID:(int)commandId;
26
27 @end
28
29 @implementation BridgedContentView
30
31 @synthesize hostedView = hostedView_;
32 @synthesize textInputClient = textInputClient_;
33
34 - (id)initWithView:(views::View*)viewToHost {
35   DCHECK(viewToHost);
36   gfx::Rect bounds = viewToHost->bounds();
37   // To keep things simple, assume the origin is (0, 0) until there exists a use
38   // case for something other than that.
39   DCHECK(bounds.origin().IsOrigin());
40   NSRect initialFrame = NSMakeRect(0, 0, bounds.width(), bounds.height());
41   if ((self = [super initWithFrame:initialFrame])) {
42     hostedView_ = viewToHost;
43
44     trackingArea_.reset(
45         [[CrTrackingArea alloc] initWithRect:NSZeroRect
46                                      options:NSTrackingMouseMoved |
47                                              NSTrackingActiveAlways |
48                                              NSTrackingInVisibleRect
49                                        owner:self
50                                     userInfo:nil]);
51     [self addTrackingArea:trackingArea_.get()];
52   }
53   return self;
54 }
55
56 - (void)clearView {
57   hostedView_ = NULL;
58   [trackingArea_.get() clearOwner];
59   [self removeTrackingArea:trackingArea_.get()];
60 }
61
62 // BridgedContentView private implementation.
63
64 - (void)handleMouseEvent:(NSEvent*)theEvent {
65   if (!hostedView_)
66     return;
67
68   ui::MouseEvent event(theEvent);
69   hostedView_->GetWidget()->OnMouseEvent(&event);
70 }
71
72 - (void)doCommandByID:(int)commandId {
73   if (textInputClient_ && textInputClient_->IsEditingCommandEnabled(commandId))
74     textInputClient_->ExecuteEditingCommand(commandId);
75 }
76
77 // NSView implementation.
78
79 - (BOOL)acceptsFirstResponder {
80   return YES;
81 }
82
83 - (void)setFrameSize:(NSSize)newSize {
84   [super setFrameSize:newSize];
85   if (!hostedView_)
86     return;
87
88   hostedView_->SetSize(gfx::Size(newSize.width, newSize.height));
89 }
90
91 - (void)drawRect:(NSRect)dirtyRect {
92   if (!hostedView_)
93     return;
94
95   gfx::CanvasSkiaPaint canvas(dirtyRect, false /* opaque */);
96   hostedView_->Paint(&canvas, views::CullSet());
97 }
98
99 // NSResponder implementation.
100
101 - (void)keyDown:(NSEvent*)theEvent {
102   if (textInputClient_)
103     [self interpretKeyEvents:@[ theEvent ]];
104   else
105     [super keyDown:theEvent];
106 }
107
108 - (void)mouseDown:(NSEvent*)theEvent {
109   [self handleMouseEvent:theEvent];
110 }
111
112 - (void)rightMouseDown:(NSEvent*)theEvent {
113   [self handleMouseEvent:theEvent];
114 }
115
116 - (void)otherMouseDown:(NSEvent*)theEvent {
117   [self handleMouseEvent:theEvent];
118 }
119
120 - (void)mouseUp:(NSEvent*)theEvent {
121   [self handleMouseEvent:theEvent];
122 }
123
124 - (void)rightMouseUp:(NSEvent*)theEvent {
125   [self handleMouseEvent:theEvent];
126 }
127
128 - (void)otherMouseUp:(NSEvent*)theEvent {
129   [self handleMouseEvent:theEvent];
130 }
131
132 - (void)mouseDragged:(NSEvent*)theEvent {
133   [self handleMouseEvent:theEvent];
134 }
135
136 - (void)rightMouseDragged:(NSEvent*)theEvent {
137   [self handleMouseEvent:theEvent];
138 }
139
140 - (void)otherMouseDragged:(NSEvent*)theEvent {
141   [self handleMouseEvent:theEvent];
142 }
143
144 - (void)mouseMoved:(NSEvent*)theEvent {
145   // Note: mouseEntered: and mouseExited: are not handled separately.
146   // |hostedView_| is responsible for converting the move events into entered
147   // and exited events for the view heirarchy.
148   [self handleMouseEvent:theEvent];
149 }
150
151 - (void)scrollWheel:(NSEvent*)theEvent {
152   if (!hostedView_)
153     return;
154
155   ui::MouseWheelEvent event(theEvent);
156   hostedView_->GetWidget()->OnMouseEvent(&event);
157 }
158
159 - (void)deleteBackward:(id)sender {
160   [self doCommandByID:IDS_DELETE_BACKWARD];
161 }
162
163 - (void)deleteForward:(id)sender {
164   [self doCommandByID:IDS_DELETE_FORWARD];
165 }
166
167 - (void)moveLeft:(id)sender {
168   [self doCommandByID:IDS_MOVE_LEFT];
169 }
170
171 - (void)moveRight:(id)sender {
172   [self doCommandByID:IDS_MOVE_RIGHT];
173 }
174
175 - (void)insertText:(id)text {
176   if (textInputClient_)
177     textInputClient_->InsertText(base::SysNSStringToUTF16(text));
178 }
179
180 // Support for Services in context menus.
181 // Currently we only support reading and writing plain strings.
182 - (id)validRequestorForSendType:(NSString*)sendType
183                      returnType:(NSString*)returnType {
184   BOOL canWrite = [sendType isEqualToString:NSStringPboardType] &&
185                   [self selectedRange].length > 0;
186   BOOL canRead = [returnType isEqualToString:NSStringPboardType];
187   // Valid if (sendType, returnType) is either (string, nil), (nil, string),
188   // or (string, string).
189   BOOL valid = textInputClient_ && ((canWrite && (canRead || !returnType)) ||
190                                     (canRead && (canWrite || !sendType)));
191   return valid ? self : [super validRequestorForSendType:sendType
192                                               returnType:returnType];
193 }
194
195 // NSServicesRequests informal protocol.
196
197 - (BOOL)writeSelectionToPasteboard:(NSPasteboard*)pboard types:(NSArray*)types {
198   DCHECK([types containsObject:NSStringPboardType]);
199   if (!textInputClient_)
200     return NO;
201
202   gfx::Range selectionRange;
203   if (!textInputClient_->GetSelectionRange(&selectionRange))
204     return NO;
205
206   base::string16 text;
207   textInputClient_->GetTextFromRange(selectionRange, &text);
208   return [pboard writeObjects:@[ base::SysUTF16ToNSString(text) ]];
209 }
210
211 - (BOOL)readSelectionFromPasteboard:(NSPasteboard*)pboard {
212   NSArray* objects =
213       [pboard readObjectsForClasses:@[ [NSString class] ] options:0];
214   DCHECK([objects count] == 1);
215   [self insertText:[objects lastObject]];
216   return YES;
217 }
218
219 // NSTextInputClient protocol implementation.
220
221 - (NSAttributedString*)
222     attributedSubstringForProposedRange:(NSRange)range
223                             actualRange:(NSRangePointer)actualRange {
224   base::string16 substring;
225   if (textInputClient_) {
226     gfx::Range textRange;
227     textInputClient_->GetTextRange(&textRange);
228     gfx::Range subrange = textRange.Intersect(gfx::Range(range));
229     textInputClient_->GetTextFromRange(subrange, &substring);
230     if (actualRange)
231       *actualRange = subrange.ToNSRange();
232   }
233   return [[[NSAttributedString alloc]
234       initWithString:base::SysUTF16ToNSString(substring)] autorelease];
235 }
236
237 - (NSUInteger)characterIndexForPoint:(NSPoint)aPoint {
238   NOTIMPLEMENTED();
239   return 0;
240 }
241
242 - (void)doCommandBySelector:(SEL)selector {
243   if ([self respondsToSelector:selector])
244     [self performSelector:selector withObject:nil];
245   else
246     [[self nextResponder] doCommandBySelector:selector];
247 }
248
249 - (NSRect)firstRectForCharacterRange:(NSRange)range
250                          actualRange:(NSRangePointer)actualRange {
251   NOTIMPLEMENTED();
252   return NSZeroRect;
253 }
254
255 - (BOOL)hasMarkedText {
256   return textInputClient_ && textInputClient_->HasCompositionText();
257 }
258
259 - (void)insertText:(id)text replacementRange:(NSRange)replacementRange {
260   if (!textInputClient_)
261     return;
262
263   if ([text isKindOfClass:[NSAttributedString class]])
264     text = [text string];
265   textInputClient_->DeleteRange(gfx::Range(replacementRange));
266   textInputClient_->InsertText(base::SysNSStringToUTF16(text));
267 }
268
269 - (NSRange)markedRange {
270   if (!textInputClient_)
271     return NSMakeRange(NSNotFound, 0);
272
273   gfx::Range range;
274   textInputClient_->GetCompositionTextRange(&range);
275   return range.ToNSRange();
276 }
277
278 - (NSRange)selectedRange {
279   if (!textInputClient_)
280     return NSMakeRange(NSNotFound, 0);
281
282   gfx::Range range;
283   textInputClient_->GetSelectionRange(&range);
284   return range.ToNSRange();
285 }
286
287 - (void)setMarkedText:(id)text
288         selectedRange:(NSRange)selectedRange
289      replacementRange:(NSRange)replacementRange {
290   if (!textInputClient_)
291     return;
292
293   if ([text isKindOfClass:[NSAttributedString class]])
294     text = [text string];
295   ui::CompositionText composition;
296   composition.text = base::SysNSStringToUTF16(text);
297   composition.selection = gfx::Range(selectedRange);
298   textInputClient_->SetCompositionText(composition);
299 }
300
301 - (void)unmarkText {
302   if (textInputClient_)
303     textInputClient_->ConfirmCompositionText();
304 }
305
306 - (NSArray*)validAttributesForMarkedText {
307   return @[];
308 }
309
310 // NSAccessibility informal protocol implementation.
311
312 - (id)accessibilityAttributeValue:(NSString*)attribute {
313   if ([attribute isEqualToString:NSAccessibilityChildrenAttribute]) {
314     return @[ hostedView_->GetNativeViewAccessible() ];
315   }
316
317   return [super accessibilityAttributeValue:attribute];
318 }
319
320 - (id)accessibilityHitTest:(NSPoint)point {
321   return [hostedView_->GetNativeViewAccessible() accessibilityHitTest:point];
322 }
323
324 @end