Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "chrome/browser/renderer_host/render_widget_host_view_mac.h" | |
| 6 | |
| 5 #include <QuartzCore/QuartzCore.h> | 7 #include <QuartzCore/QuartzCore.h> |
| 6 | 8 |
| 7 #include "chrome/browser/renderer_host/render_widget_host_view_mac.h" | 9 #include <cmath> |
| 8 | 10 |
| 9 #include "base/debug/trace_event.h" | 11 #include "base/debug/trace_event.h" |
| 10 #include "base/logging.h" | 12 #include "base/logging.h" |
| 11 #include "base/mac/mac_util.h" | 13 #include "base/mac/mac_util.h" |
| 12 #include "base/mac/scoped_cftyperef.h" | 14 #include "base/mac/scoped_cftyperef.h" |
| 13 #import "base/mac/scoped_nsautorelease_pool.h" | 15 #import "base/mac/scoped_nsautorelease_pool.h" |
| 14 #include "base/metrics/histogram.h" | 16 #include "base/metrics/histogram.h" |
| 15 #import "base/memory/scoped_nsobject.h" | 17 #import "base/memory/scoped_nsobject.h" |
| 16 #include "base/string_util.h" | 18 #include "base/string_util.h" |
| 17 #include "base/sys_info.h" | 19 #include "base/sys_info.h" |
| 18 #include "base/sys_string_conversions.h" | 20 #include "base/sys_string_conversions.h" |
| 19 #include "chrome/browser/browser_trial.h" | 21 #include "chrome/browser/browser_trial.h" |
| 22 #include "chrome/browser/mac/closure_blocks_leopard_compat.h" | |
| 20 #import "chrome/browser/renderer_host/accelerated_plugin_view_mac.h" | 23 #import "chrome/browser/renderer_host/accelerated_plugin_view_mac.h" |
| 21 #import "chrome/browser/renderer_host/text_input_client_mac.h" | 24 #import "chrome/browser/renderer_host/text_input_client_mac.h" |
| 22 #include "chrome/browser/spellchecker/spellchecker_platform_engine.h" | 25 #include "chrome/browser/spellchecker/spellchecker_platform_engine.h" |
| 26 #include "chrome/browser/ui/browser.h" | |
| 27 #include "chrome/browser/ui/browser_list.h" | |
| 23 #import "chrome/browser/ui/cocoa/rwhvm_editcommand_helper.h" | 28 #import "chrome/browser/ui/cocoa/rwhvm_editcommand_helper.h" |
| 24 #import "chrome/browser/ui/cocoa/view_id_util.h" | 29 #import "chrome/browser/ui/cocoa/view_id_util.h" |
| 25 #include "chrome/common/render_messages.h" | 30 #include "chrome/common/render_messages.h" |
| 26 #include "chrome/common/spellcheck_messages.h" | 31 #include "chrome/common/spellcheck_messages.h" |
| 27 #import "content/browser/accessibility/browser_accessibility_cocoa.h" | 32 #import "content/browser/accessibility/browser_accessibility_cocoa.h" |
| 28 #include "content/browser/browser_thread.h" | 33 #include "content/browser/browser_thread.h" |
| 29 #include "content/browser/gpu/gpu_process_host.h" | 34 #include "content/browser/gpu/gpu_process_host.h" |
| 30 #include "content/browser/gpu/gpu_process_host_ui_shim.h" | 35 #include "content/browser/gpu/gpu_process_host_ui_shim.h" |
| 31 #include "content/browser/plugin_process_host.h" | 36 #include "content/browser/plugin_process_host.h" |
| 32 #include "content/browser/renderer_host/backing_store_mac.h" | 37 #include "content/browser/renderer_host/backing_store_mac.h" |
| (...skipping 16 matching lines...) Expand all Loading... | |
| 49 #include "ui/gfx/surface/io_surface_support_mac.h" | 54 #include "ui/gfx/surface/io_surface_support_mac.h" |
| 50 #include "webkit/glue/webaccessibility.h" | 55 #include "webkit/glue/webaccessibility.h" |
| 51 #include "webkit/plugins/npapi/webplugin.h" | 56 #include "webkit/plugins/npapi/webplugin.h" |
| 52 | 57 |
| 53 using WebKit::WebInputEvent; | 58 using WebKit::WebInputEvent; |
| 54 using WebKit::WebInputEventFactory; | 59 using WebKit::WebInputEventFactory; |
| 55 using WebKit::WebMouseEvent; | 60 using WebKit::WebMouseEvent; |
| 56 using WebKit::WebMouseWheelEvent; | 61 using WebKit::WebMouseWheelEvent; |
| 57 using WebKit::WebGestureEvent; | 62 using WebKit::WebGestureEvent; |
| 58 | 63 |
| 64 // Forward-declare symbols that are part of the 10.7 SDK. | |
|
Mark Mentovai
2011/08/15 16:55:36
These are just declarations. Not too much about th
Nico
2011/08/15 17:05:30
Removed "Forward-".
| |
| 65 #if !defined(MAC_OS_X_VERSION_10_7) || \ | |
| 66 MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_7 | |
| 67 | |
| 68 enum { | |
| 69 NSEventPhaseNone = 0, // event not associated with a phase. | |
| 70 NSEventPhaseBegan = 0x1 << 0, | |
| 71 NSEventPhaseStationary = 0x1 << 1, | |
| 72 NSEventPhaseChanged = 0x1 << 2, | |
| 73 NSEventPhaseEnded = 0x1 << 3, | |
| 74 NSEventPhaseCancelled = 0x1 << 4, | |
| 75 }; | |
| 76 typedef NSUInteger NSEventPhase; | |
| 77 | |
| 78 enum { | |
| 79 NSEventSwipeTrackingLockDirection = 0x1 << 0, | |
| 80 NSEventSwipeTrackingClampGestureAmount = 0x1 << 1 | |
| 81 }; | |
| 82 typedef NSUInteger NSEventSwipeTrackingOptions; | |
| 83 | |
| 84 | |
| 85 @interface NSEvent (LionAPI) | |
| 86 + (BOOL)isSwipeTrackingFromScrollEventsEnabled; | |
| 87 | |
| 88 - (NSEventPhase)phase; | |
| 89 - (CGFloat)scrollingDeltaX; | |
| 90 - (CGFloat)scrollingDeltaY; | |
| 91 - (void)trackSwipeEventWithOptions:(NSEventSwipeTrackingOptions)options | |
| 92 dampenAmountThresholdMin:(CGFloat)minDampenThreshold | |
| 93 max:(CGFloat)maxDampenThreshold | |
| 94 usingHandler:(void (^)(CGFloat gestureAmount, | |
| 95 NSEventPhase phase, | |
| 96 BOOL isComplete, | |
| 97 BOOL *stop))trackingHandler; | |
| 98 @end | |
| 99 | |
| 100 #endif // 10.7 | |
| 101 | |
| 59 static inline int ToWebKitModifiers(NSUInteger flags) { | 102 static inline int ToWebKitModifiers(NSUInteger flags) { |
| 60 int modifiers = 0; | 103 int modifiers = 0; |
| 61 if (flags & NSControlKeyMask) modifiers |= WebInputEvent::ControlKey; | 104 if (flags & NSControlKeyMask) modifiers |= WebInputEvent::ControlKey; |
| 62 if (flags & NSShiftKeyMask) modifiers |= WebInputEvent::ShiftKey; | 105 if (flags & NSShiftKeyMask) modifiers |= WebInputEvent::ShiftKey; |
| 63 if (flags & NSAlternateKeyMask) modifiers |= WebInputEvent::AltKey; | 106 if (flags & NSAlternateKeyMask) modifiers |= WebInputEvent::AltKey; |
| 64 if (flags & NSCommandKeyMask) modifiers |= WebInputEvent::MetaKey; | 107 if (flags & NSCommandKeyMask) modifiers |= WebInputEvent::MetaKey; |
| 65 return modifiers; | 108 return modifiers; |
| 66 } | 109 } |
| 67 | 110 |
| 68 // Private methods: | 111 // Private methods: |
| (...skipping 942 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1011 | 1054 |
| 1012 void RenderWidgetHostViewMac::ForceTextureReload() { | 1055 void RenderWidgetHostViewMac::ForceTextureReload() { |
| 1013 plugin_container_manager_.ForceTextureReload(); | 1056 plugin_container_manager_.ForceTextureReload(); |
| 1014 } | 1057 } |
| 1015 | 1058 |
| 1016 void RenderWidgetHostViewMac::SetVisuallyDeemphasized(const SkColor* color, | 1059 void RenderWidgetHostViewMac::SetVisuallyDeemphasized(const SkColor* color, |
| 1017 bool animate) { | 1060 bool animate) { |
| 1018 // This is not used on mac. | 1061 // This is not used on mac. |
| 1019 } | 1062 } |
| 1020 | 1063 |
| 1064 void RenderWidgetHostViewMac::UnhandledWheelEvent( | |
| 1065 const WebKit::WebMouseWheelEvent& event) { | |
| 1066 [cocoa_view_ setGotUnhandledWheelEvent:YES]; | |
| 1067 } | |
| 1068 | |
| 1069 void RenderWidgetHostViewMac::SetHasHorizontalScrollbar( | |
| 1070 bool has_horizontal_scrollbar) { | |
| 1071 [cocoa_view_ setHasHorizontalScrollbar:has_horizontal_scrollbar]; | |
| 1072 } | |
| 1073 | |
| 1074 void RenderWidgetHostViewMac::SetScrollOffsetPinning( | |
| 1075 bool is_pinned_to_left, bool is_pinned_to_right) { | |
| 1076 [cocoa_view_ setPinnedLeft:is_pinned_to_left]; | |
| 1077 [cocoa_view_ setPinnedRight:is_pinned_to_right]; | |
| 1078 } | |
| 1079 | |
| 1021 void RenderWidgetHostViewMac::ShutdownHost() { | 1080 void RenderWidgetHostViewMac::ShutdownHost() { |
| 1022 shutdown_factory_.RevokeAll(); | 1081 shutdown_factory_.RevokeAll(); |
| 1023 render_widget_host_->Shutdown(); | 1082 render_widget_host_->Shutdown(); |
| 1024 // Do not touch any members at this point, |this| has been deleted. | 1083 // Do not touch any members at this point, |this| has been deleted. |
| 1025 } | 1084 } |
| 1026 | 1085 |
| 1027 gfx::Rect RenderWidgetHostViewMac::GetViewCocoaBounds() const { | 1086 gfx::Rect RenderWidgetHostViewMac::GetViewCocoaBounds() const { |
| 1028 return gfx::Rect(NSRectToCGRect([cocoa_view_ bounds])); | 1087 return gfx::Rect(NSRectToCGRect([cocoa_view_ bounds])); |
| 1029 } | 1088 } |
| 1030 | 1089 |
| (...skipping 63 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1094 DisablePasswordInput(); | 1153 DisablePasswordInput(); |
| 1095 } | 1154 } |
| 1096 } | 1155 } |
| 1097 | 1156 |
| 1098 // RenderWidgetHostViewCocoa --------------------------------------------------- | 1157 // RenderWidgetHostViewCocoa --------------------------------------------------- |
| 1099 | 1158 |
| 1100 @implementation RenderWidgetHostViewCocoa | 1159 @implementation RenderWidgetHostViewCocoa |
| 1101 | 1160 |
| 1102 @synthesize selectedRange = selectedRange_; | 1161 @synthesize selectedRange = selectedRange_; |
| 1103 @synthesize markedRange = markedRange_; | 1162 @synthesize markedRange = markedRange_; |
| 1163 @synthesize gotUnhandledWheelEvent = gotUnhandledWheelEvent_; | |
| 1164 @synthesize isPinnedLeft = isPinnedLeft_; | |
| 1165 @synthesize isPinnedRight = isPinnedRight_; | |
| 1166 @synthesize hasHorizontalScrollbar = hasHorizontalScrollbar_; | |
| 1104 | 1167 |
| 1105 - (id)initWithRenderWidgetHostViewMac:(RenderWidgetHostViewMac*)r { | 1168 - (id)initWithRenderWidgetHostViewMac:(RenderWidgetHostViewMac*)r { |
| 1106 self = [super initWithFrame:NSZeroRect]; | 1169 self = [super initWithFrame:NSZeroRect]; |
| 1107 if (self) { | 1170 if (self) { |
| 1108 editCommand_helper_.reset(new RWHVMEditCommandHelper); | 1171 editCommand_helper_.reset(new RWHVMEditCommandHelper); |
| 1109 editCommand_helper_->AddEditingSelectorsToClass([self class]); | 1172 editCommand_helper_->AddEditingSelectorsToClass([self class]); |
| 1110 | 1173 |
| 1111 renderWidgetHostView_.reset(r); | 1174 renderWidgetHostView_.reset(r); |
| 1112 canBeKeyView_ = YES; | 1175 canBeKeyView_ = YES; |
| 1113 focusedPluginIdentifier_ = -1; | 1176 focusedPluginIdentifier_ = -1; |
| (...skipping 110 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1224 renderWidgetHostView_->render_widget_host_->ForwardMouseEvent(event); | 1287 renderWidgetHostView_->render_widget_host_->ForwardMouseEvent(event); |
| 1225 } | 1288 } |
| 1226 | 1289 |
| 1227 - (void)beginGestureWithEvent:(NSEvent*)event { | 1290 - (void)beginGestureWithEvent:(NSEvent*)event { |
| 1228 if (base::mac::IsOSLionOrLater() && | 1291 if (base::mac::IsOSLionOrLater() && |
| 1229 [event subtype] == kIOHIDEventTypeScroll && | 1292 [event subtype] == kIOHIDEventTypeScroll && |
| 1230 renderWidgetHostView_->render_widget_host_) { | 1293 renderWidgetHostView_->render_widget_host_) { |
| 1231 WebGestureEvent webEvent = WebInputEventFactory::gestureEvent(event, self); | 1294 WebGestureEvent webEvent = WebInputEventFactory::gestureEvent(event, self); |
| 1232 renderWidgetHostView_->render_widget_host_->ForwardGestureEvent(webEvent); | 1295 renderWidgetHostView_->render_widget_host_->ForwardGestureEvent(webEvent); |
| 1233 } | 1296 } |
| 1234 | |
| 1235 // Forward the gesture event to the next responder so that the browser window | |
| 1236 // controller has a chance to act on back/forward gestures. | |
| 1237 [[self nextResponder] beginGestureWithEvent:event]; | |
|
Mark Mentovai
2011/08/15 16:55:36
Nice subtraction. Thanks to Alexei for catching it
| |
| 1238 } | 1297 } |
| 1239 | 1298 |
| 1240 - (void)endGestureWithEvent:(NSEvent*)event { | 1299 - (void)endGestureWithEvent:(NSEvent*)event { |
| 1241 if (base::mac::IsOSLionOrLater() && | 1300 if (base::mac::IsOSLionOrLater() && |
| 1242 [event subtype] == kIOHIDEventTypeScroll && | 1301 [event subtype] == kIOHIDEventTypeScroll && |
| 1243 renderWidgetHostView_->render_widget_host_) { | 1302 renderWidgetHostView_->render_widget_host_) { |
| 1244 WebGestureEvent webEvent = WebInputEventFactory::gestureEvent(event, self); | 1303 WebGestureEvent webEvent = WebInputEventFactory::gestureEvent(event, self); |
| 1245 renderWidgetHostView_->render_widget_host_->ForwardGestureEvent(webEvent); | 1304 renderWidgetHostView_->render_widget_host_->ForwardGestureEvent(webEvent); |
| 1246 } | 1305 } |
| 1247 | |
| 1248 // Forward the gesture event to the next responder so that the browser window | |
| 1249 // controller has a chance to act on back/forward gestures. | |
| 1250 [[self nextResponder] endGestureWithEvent:event]; | |
| 1251 } | 1306 } |
| 1252 | 1307 |
| 1253 - (BOOL)performKeyEquivalent:(NSEvent*)theEvent { | 1308 - (BOOL)performKeyEquivalent:(NSEvent*)theEvent { |
| 1254 // |performKeyEquivalent:| is sent to all views of a window, not only down the | 1309 // |performKeyEquivalent:| is sent to all views of a window, not only down the |
| 1255 // responder chain (cf. "Handling Key Equivalents" in | 1310 // responder chain (cf. "Handling Key Equivalents" in |
| 1256 // http://developer.apple.com/mac/library/documentation/Cocoa/Conceptual/Event Overview/HandlingKeyEvents/HandlingKeyEvents.html | 1311 // http://developer.apple.com/mac/library/documentation/Cocoa/Conceptual/Event Overview/HandlingKeyEvents/HandlingKeyEvents.html |
| 1257 // ). We only want to handle key equivalents if we're first responder. | 1312 // ). We only want to handle key equivalents if we're first responder. |
| 1258 if ([[self window] firstResponder] != self) | 1313 if ([[self window] firstResponder] != self) |
| 1259 return NO; | 1314 return NO; |
| 1260 | 1315 |
| (...skipping 229 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1490 event.skip_in_browser = true; | 1545 event.skip_in_browser = true; |
| 1491 widgetHost->ForwardKeyboardEvent(event); | 1546 widgetHost->ForwardKeyboardEvent(event); |
| 1492 } | 1547 } |
| 1493 } | 1548 } |
| 1494 | 1549 |
| 1495 // Possibly autohide the cursor. | 1550 // Possibly autohide the cursor. |
| 1496 if ([RenderWidgetHostViewCocoa shouldAutohideCursorForEvent:theEvent]) | 1551 if ([RenderWidgetHostViewCocoa shouldAutohideCursorForEvent:theEvent]) |
| 1497 [NSCursor setHiddenUntilMouseMoves:YES]; | 1552 [NSCursor setHiddenUntilMouseMoves:YES]; |
| 1498 } | 1553 } |
| 1499 | 1554 |
| 1500 - (void)scrollWheel:(NSEvent *)theEvent { | 1555 - (BOOL)maybeHandleHistorySwiping:(NSEvent*)theEvent { |
|
Mark Mentovai
2011/08/15 16:55:36
Since you don’t declare this in an @interface, you
Nico
2011/08/15 17:05:30
Done.
| |
| 1556 BOOL canUseLionApis = [theEvent respondsToSelector:@selector(phase)]; | |
| 1557 if (!canUseLionApis) | |
| 1558 return NO; | |
| 1559 | |
| 1560 // Scroll events always go to the web first, and can only trigger history | |
| 1561 // swiping if they come back unhandled. | |
| 1562 if ([theEvent phase] == NSEventPhaseBegan) { | |
| 1563 totalScrollDelta_ = NSZeroSize; | |
| 1564 gotUnhandledWheelEvent_ = false; | |
| 1565 } | |
| 1566 | |
| 1567 if (gotUnhandledWheelEvent_ && | |
| 1568 [NSEvent isSwipeTrackingFromScrollEventsEnabled] && | |
| 1569 [theEvent phase] == NSEventPhaseChanged) { | |
| 1570 totalScrollDelta_.width += [theEvent scrollingDeltaX]; | |
| 1571 totalScrollDelta_.height += [theEvent scrollingDeltaY]; | |
| 1572 | |
| 1573 bool isHorizontalGesture = | |
| 1574 std::abs(totalScrollDelta_.width) > std::abs(totalScrollDelta_.height); | |
|
Mark Mentovai
2011/08/15 16:55:36
Are the |abs| calls helpful here at all? Can’t we
Nico
2011/08/15 17:05:30
Yes, width and height can be both positive or nega
Mark Mentovai
2011/08/15 17:58:24
Nico wrote:
| |
| 1575 | |
| 1576 bool isRightScroll = [theEvent scrollingDeltaX] < 0; | |
| 1577 bool goForward = isRightScroll; | |
| 1578 | |
| 1579 if (isHorizontalGesture && | |
| 1580 // If "forward" is inactive and the user rubber-bands to the right, | |
| 1581 // "isPinnedLeft" will be false. When the user then rubber-bands to | |
| 1582 // the left in the same gesture, that should trigger history | |
| 1583 // immediately if there's no scrollbar, hence the check for | |
| 1584 // hasHorizontalScrollbar_. | |
| 1585 (!hasHorizontalScrollbar_ || | |
| 1586 // One would think we have to check canGoBack / canGoForward here, but | |
| 1587 // that's actually done in the renderer | |
| 1588 // (ChromeClientImpl::shouldRubberBand()), when it decides if it should | |
| 1589 // rubberband or send back an event unhandled. | |
| 1590 (isPinnedLeft_ && !isRightScroll) || | |
| 1591 (isPinnedRight_ && isRightScroll))) { | |
| 1592 // The way this api works: gestureAmount is between -1 and 1 (float). If | |
| 1593 // the user does the gesture for more than about 25% (i.e. < -0.25 or > | |
| 1594 // 0.25) and then lets go, it is accepted, we get a NSEventPhaseEnded, | |
| 1595 // and after that the block is called with amounts animating towards 1 | |
| 1596 // (or -1, depending on the direction). If the user lets go below that | |
| 1597 // threshold, we get NSEventPhaseCancelled, and the amount animates | |
| 1598 // toward 0. | |
| 1599 [theEvent trackSwipeEventWithOptions:0 | |
| 1600 dampenAmountThresholdMin:-1 | |
| 1601 max:1 | |
| 1602 usingHandler:^(CGFloat gestureAmount, | |
| 1603 NSEventPhase phase, | |
| 1604 BOOL isComplete, | |
| 1605 BOOL *stop) { | |
| 1606 // |gestureAmount| obeys -[NSEvent isDirectionInvertedFromDevice] | |
| 1607 // automatically. | |
| 1608 // TODO(thakis): UI. | |
| 1609 Browser* browser = BrowserList::GetLastActive(); | |
| 1610 if (phase == NSEventPhaseEnded && browser) { | |
| 1611 if (goForward) | |
| 1612 browser->GoForward(CURRENT_TAB); | |
| 1613 else | |
| 1614 browser->GoBack(CURRENT_TAB); | |
| 1615 } | |
| 1616 }]; | |
| 1617 return YES; | |
| 1618 } | |
| 1619 } | |
| 1620 return NO; | |
| 1621 } | |
| 1622 | |
| 1623 - (void)scrollWheel:(NSEvent*)theEvent { | |
| 1501 [self cancelChildPopups]; | 1624 [self cancelChildPopups]; |
| 1502 | 1625 |
| 1626 if ([self maybeHandleHistorySwiping:theEvent]) | |
| 1627 return; | |
| 1628 | |
| 1503 const WebMouseWheelEvent& event = | 1629 const WebMouseWheelEvent& event = |
| 1504 WebInputEventFactory::mouseWheelEvent(theEvent, self); | 1630 WebInputEventFactory::mouseWheelEvent(theEvent, self); |
| 1505 if (renderWidgetHostView_->render_widget_host_) | 1631 if (renderWidgetHostView_->render_widget_host_) |
| 1506 renderWidgetHostView_->render_widget_host_->ForwardWheelEvent(event); | 1632 renderWidgetHostView_->render_widget_host_->ForwardWheelEvent(event); |
| 1507 } | 1633 } |
| 1508 | 1634 |
| 1509 // See the comment in RenderWidgetHostViewMac::Destroy() about cancellation | 1635 // See the comment in RenderWidgetHostViewMac::Destroy() about cancellation |
| 1510 // events. On the Mac we must kill popups on outside events, thus this lovely | 1636 // events. On the Mac we must kill popups on outside events, thus this lovely |
| 1511 // case of filicide caused by events on parent views. | 1637 // case of filicide caused by events on parent views. |
| 1512 - (void)cancelChildPopups { | 1638 - (void)cancelChildPopups { |
| (...skipping 1248 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 2761 if (!string) return NO; | 2887 if (!string) return NO; |
| 2762 | 2888 |
| 2763 // If the user is currently using an IME, confirm the IME input, | 2889 // If the user is currently using an IME, confirm the IME input, |
| 2764 // and then insert the text from the service, the same as TextEdit and Safari. | 2890 // and then insert the text from the service, the same as TextEdit and Safari. |
| 2765 [self confirmComposition]; | 2891 [self confirmComposition]; |
| 2766 [self insertText:string]; | 2892 [self insertText:string]; |
| 2767 return YES; | 2893 return YES; |
| 2768 } | 2894 } |
| 2769 | 2895 |
| 2770 @end | 2896 @end |
| OLD | NEW |