Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright 2014 The Chromium Authors. All rights reserved. | 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 | 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 #import "ui/views/cocoa/bridged_content_view.h" | 5 #import "ui/views/cocoa/bridged_content_view.h" |
| 6 | 6 |
| 7 #include "base/logging.h" | 7 #include "base/logging.h" |
| 8 #import "base/mac/mac_util.h" | 8 #import "base/mac/mac_util.h" |
| 9 #import "base/mac/scoped_nsobject.h" | 9 #import "base/mac/scoped_nsobject.h" |
| 10 #include "base/strings/sys_string_conversions.h" | 10 #include "base/strings/sys_string_conversions.h" |
| (...skipping 52 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 63 : point; | 63 : point; |
| 64 | 64 |
| 65 NSPoint point_in_window = | 65 NSPoint point_in_window = |
| 66 ui::ConvertPointFromScreenToWindow(target_window, point_in_screen); | 66 ui::ConvertPointFromScreenToWindow(target_window, point_in_screen); |
| 67 NSRect content_rect = | 67 NSRect content_rect = |
| 68 [target_window contentRectForFrameRect:[target_window frame]]; | 68 [target_window contentRectForFrameRect:[target_window frame]]; |
| 69 return gfx::Point(point_in_window.x, | 69 return gfx::Point(point_in_window.x, |
| 70 NSHeight(content_rect) - point_in_window.y); | 70 NSHeight(content_rect) - point_in_window.y); |
| 71 } | 71 } |
| 72 | 72 |
| 73 // Checks if there's an active MenuController during key event dispatch. If | 73 // Dispatch |event| to |menu_controller| and return true if |event| is |
| 74 // there is one, it gets preference, and it will likely swallow the event. | 74 // swallowed. |
| 75 bool DispatchEventToMenu(views::Widget* widget, ui::KeyEvent* event) { | 75 bool DispatchEventToMenu(MenuController* menu_controller, ui::KeyEvent* event) { |
| 76 MenuController* menuController = MenuController::GetActiveInstance(); | 76 return menu_controller && |
| 77 if (menuController && menuController->owner() == widget) { | 77 menu_controller->OnWillDispatchKeyEvent(event) == |
| 78 if (menuController->OnWillDispatchKeyEvent(event) == ui::POST_DISPATCH_NONE) | 78 ui::POST_DISPATCH_NONE; |
| 79 return true; | |
| 80 } | |
| 81 return false; | |
| 82 } | 79 } |
| 83 | 80 |
| 84 // Returns true if |client| has RTL text. | 81 // Returns true if |client| has RTL text. |
| 85 bool IsTextRTL(const ui::TextInputClient* client) { | 82 bool IsTextRTL(const ui::TextInputClient* client) { |
| 86 return client && client->GetTextDirection() == base::i18n::RIGHT_TO_LEFT; | 83 return client && client->GetTextDirection() == base::i18n::RIGHT_TO_LEFT; |
| 87 } | 84 } |
| 88 | 85 |
| 89 // Returns the boundary rectangle for composition characters in the | 86 // Returns the boundary rectangle for composition characters in the |
| 90 // |requested_range|. Sets |actual_range| corresponding to the returned | 87 // |requested_range|. Sets |actual_range| corresponding to the returned |
| 91 // rectangle. For cases, where there is no composition text or the | 88 // rectangle. For cases, where there is no composition text or the |
| (...skipping 115 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 207 // send ui::EF_NONE as the key modifier to the KeyEvent constructor. | 204 // send ui::EF_NONE as the key modifier to the KeyEvent constructor. |
| 208 // E.g. For Alt+S, [NSEvent characters] is 'ß' and not 'S'. | 205 // E.g. For Alt+S, [NSEvent characters] is 'ß' and not 'S'. |
| 209 return ui::KeyEvent([[event characters] characterAtIndex:0], | 206 return ui::KeyEvent([[event characters] characterAtIndex:0], |
| 210 ui::KeyboardCodeFromNSEvent(event), ui::EF_NONE); | 207 ui::KeyboardCodeFromNSEvent(event), ui::EF_NONE); |
| 211 } | 208 } |
| 212 | 209 |
| 213 } // namespace | 210 } // namespace |
| 214 | 211 |
| 215 @interface BridgedContentView () | 212 @interface BridgedContentView () |
| 216 | 213 |
| 214 // Returns the active menu controller corresponding to |hostedView_|, | |
| 215 // nil otherwise. | |
| 216 - (MenuController*)activeMenuController; | |
| 217 | |
| 217 // Passes |event| to the InputMethod for dispatch. | 218 // Passes |event| to the InputMethod for dispatch. |
| 218 - (void)handleKeyEvent:(ui::KeyEvent*)event; | 219 - (void)handleKeyEvent:(ui::KeyEvent*)event; |
| 219 | 220 |
| 220 // Handles an NSResponder Action Message by mapping it to a corresponding text | 221 // Handles an NSResponder Action Message by mapping it to a corresponding text |
| 221 // editing command from ui_strings.grd and, when not being sent to a | 222 // editing command from ui_strings.grd and, when not being sent to a |
| 222 // TextInputClient, the keyCode that toolkit-views expects internally. | 223 // TextInputClient, the keyCode that toolkit-views expects internally. |
| 223 // For example, moveToLeftEndOfLine: would pass ui::VKEY_HOME in non-RTL locales | 224 // For example, moveToLeftEndOfLine: would pass ui::VKEY_HOME in non-RTL locales |
| 224 // even though the Home key on Mac defaults to moveToBeginningOfDocument:. | 225 // even though the Home key on Mac defaults to moveToBeginningOfDocument:. |
| 225 // This approach also allows action messages a user | 226 // This approach also allows action messages a user |
| 226 // may have remapped in ~/Library/KeyBindings/DefaultKeyBinding.dict to be | 227 // may have remapped in ~/Library/KeyBindings/DefaultKeyBinding.dict to be |
| 227 // catered for. | 228 // catered for. |
| 228 // Note: default key bindings in Mac can be read from StandardKeyBinding.dict | 229 // Note: default key bindings in Mac can be read from StandardKeyBinding.dict |
| 229 // which lives in /System/Library/Frameworks/AppKit.framework/Resources. Do | 230 // which lives in /System/Library/Frameworks/AppKit.framework/Resources. Do |
| 230 // `plutil -convert xml1 -o StandardKeyBinding.xml StandardKeyBinding.dict` to | 231 // `plutil -convert xml1 -o StandardKeyBinding.xml StandardKeyBinding.dict` to |
| 231 // get something readable. | 232 // get something readable. |
| 232 - (void)handleAction:(ui::TextEditCommand)command | 233 - (void)handleAction:(ui::TextEditCommand)command |
| 233 keyCode:(ui::KeyboardCode)keyCode | 234 keyCode:(ui::KeyboardCode)keyCode |
| 234 domCode:(ui::DomCode)domCode | 235 domCode:(ui::DomCode)domCode |
| 235 eventFlags:(int)eventFlags; | 236 eventFlags:(int)eventFlags; |
| 236 | 237 |
| 237 // Notification handler invoked when the Full Keyboard Access mode is changed. | 238 // Notification handler invoked when the Full Keyboard Access mode is changed. |
| 238 - (void)onFullKeyboardAccessModeChanged:(NSNotification*)notification; | 239 - (void)onFullKeyboardAccessModeChanged:(NSNotification*)notification; |
| 239 | 240 |
| 241 // Helper method which forwards |text| to active menu or |text_input_client_|. | |
|
tapted
2016/06/29 07:29:34
nit: "to the active menu", textInputClient_
karandeepb
2016/06/29 08:00:09
Done.
| |
| 242 - (void)insertTextInternal:(id)text; | |
| 243 | |
| 240 // Returns the native Widget's drag drop client. Possibly null. | 244 // Returns the native Widget's drag drop client. Possibly null. |
| 241 - (views::DragDropClientMac*)dragDropClient; | 245 - (views::DragDropClientMac*)dragDropClient; |
| 242 | 246 |
| 243 // Menu action handlers. | 247 // Menu action handlers. |
| 244 - (void)undo:(id)sender; | 248 - (void)undo:(id)sender; |
| 245 - (void)redo:(id)sender; | 249 - (void)redo:(id)sender; |
| 246 - (void)cut:(id)sender; | 250 - (void)cut:(id)sender; |
| 247 - (void)copy:(id)sender; | 251 - (void)copy:(id)sender; |
| 248 - (void)paste:(id)sender; | 252 - (void)paste:(id)sender; |
| 249 - (void)selectAll:(id)sender; | 253 - (void)selectAll:(id)sender; |
| (...skipping 117 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 367 return; | 371 return; |
| 368 | 372 |
| 369 views::FocusManager* focusManager = | 373 views::FocusManager* focusManager = |
| 370 hostedView_->GetWidget()->GetFocusManager(); | 374 hostedView_->GetWidget()->GetFocusManager(); |
| 371 if (focusManager) | 375 if (focusManager) |
| 372 focusManager->SetKeyboardAccessible([NSApp isFullKeyboardAccessEnabled]); | 376 focusManager->SetKeyboardAccessible([NSApp isFullKeyboardAccessEnabled]); |
| 373 } | 377 } |
| 374 | 378 |
| 375 // BridgedContentView private implementation. | 379 // BridgedContentView private implementation. |
| 376 | 380 |
| 381 - (MenuController*)activeMenuController { | |
| 382 MenuController* menuController = MenuController::GetActiveInstance(); | |
| 383 return menuController && menuController->owner() == hostedView_->GetWidget() | |
| 384 ? menuController | |
| 385 : nullptr; | |
| 386 } | |
| 387 | |
| 377 - (void)handleKeyEvent:(ui::KeyEvent*)event { | 388 - (void)handleKeyEvent:(ui::KeyEvent*)event { |
| 378 if (!hostedView_) | 389 if (!hostedView_) |
| 379 return; | 390 return; |
| 380 | 391 |
| 381 DCHECK(event); | 392 DCHECK(event); |
| 382 if (DispatchEventToMenu(hostedView_->GetWidget(), event)) | 393 if (DispatchEventToMenu([self activeMenuController], event)) |
| 383 return; | 394 return; |
| 384 | 395 |
| 385 hostedView_->GetWidget()->GetInputMethod()->DispatchKeyEvent(event); | 396 hostedView_->GetWidget()->GetInputMethod()->DispatchKeyEvent(event); |
| 386 } | 397 } |
| 387 | 398 |
| 388 - (void)handleAction:(ui::TextEditCommand)command | 399 - (void)handleAction:(ui::TextEditCommand)command |
| 389 keyCode:(ui::KeyboardCode)keyCode | 400 keyCode:(ui::KeyboardCode)keyCode |
| 390 domCode:(ui::DomCode)domCode | 401 domCode:(ui::DomCode)domCode |
| 391 eventFlags:(int)eventFlags { | 402 eventFlags:(int)eventFlags { |
| 392 if (!hostedView_) | 403 if (!hostedView_) |
| 393 return; | 404 return; |
| 394 | 405 |
| 395 // Generate a synthetic event with the keycode toolkit-views expects. | 406 // Generate a synthetic event with the keycode toolkit-views expects. |
| 396 ui::KeyEvent event(ui::ET_KEY_PRESSED, keyCode, domCode, eventFlags); | 407 ui::KeyEvent event(ui::ET_KEY_PRESSED, keyCode, domCode, eventFlags); |
| 397 | 408 |
| 398 if (DispatchEventToMenu(hostedView_->GetWidget(), &event)) | 409 if (DispatchEventToMenu([self activeMenuController], &event)) |
| 399 return; | 410 return; |
| 400 | 411 |
| 401 // If there's an active TextInputClient, schedule the editing command to be | 412 // If there's an active TextInputClient, schedule the editing command to be |
| 402 // performed. | 413 // performed. |
| 403 if (textInputClient_ && textInputClient_->IsTextEditCommandEnabled(command)) | 414 if (textInputClient_ && textInputClient_->IsTextEditCommandEnabled(command)) |
| 404 textInputClient_->SetTextEditCommandForNextKeyEvent(command); | 415 textInputClient_->SetTextEditCommandForNextKeyEvent(command); |
| 405 | 416 |
| 406 hostedView_->GetWidget()->GetInputMethod()->DispatchKeyEvent(&event); | 417 hostedView_->GetWidget()->GetInputMethod()->DispatchKeyEvent(&event); |
| 407 } | 418 } |
| 408 | 419 |
| 409 - (void)onFullKeyboardAccessModeChanged:(NSNotification*)notification { | 420 - (void)onFullKeyboardAccessModeChanged:(NSNotification*)notification { |
| 410 DCHECK([[notification name] | 421 DCHECK([[notification name] |
| 411 isEqualToString:kFullKeyboardAccessChangedNotification]); | 422 isEqualToString:kFullKeyboardAccessChangedNotification]); |
| 412 [self updateFullKeyboardAccess]; | 423 [self updateFullKeyboardAccess]; |
| 413 } | 424 } |
| 414 | 425 |
| 426 - (void)insertTextInternal:(id)text { | |
| 427 if (!hostedView_) | |
| 428 return; | |
| 429 | |
| 430 if ([text isKindOfClass:[NSAttributedString class]]) | |
| 431 text = [text string]; | |
| 432 | |
| 433 bool character_event = keyDownEvent_ && [text length] == 1; | |
|
tapted
2016/06/29 07:29:33
character_event -> isCharacterEvent
karandeepb
2016/06/29 08:00:09
Done.
| |
| 434 | |
| 435 // Forward the |text| to |textInputClient_| if no menu is active. | |
| 436 if (textInputClient_ && ![self activeMenuController]) { | |
| 437 // If a single character is inserted by keyDown's call to | |
| 438 // interpretKeyEvents: then use InsertChar() to allow editing events to be | |
| 439 // merged. | |
| 440 if (character_event) | |
| 441 textInputClient_->InsertChar(GetCharacterEventFromNSEvent(keyDownEvent_)); | |
| 442 else | |
| 443 textInputClient_->InsertText(base::SysNSStringToUTF16(text)); | |
| 444 return; | |
| 445 } | |
| 446 | |
| 447 // Only handle the case where no. of characters is 1. Cases not handled (not | |
| 448 // an exhaustive list): | |
| 449 // - |text| contains a unicode surrogate pair, i.e. a single grapheme which | |
| 450 // requires two 16 bit characters. Currently Views menu only supports | |
| 451 // mnemonics using a single 16 bit character, so it is ok to ignore this | |
| 452 // case. | |
| 453 // - Programmatically created events. | |
| 454 // - Input from IME. But this case should not occur since inputContext is | |
| 455 // nil. | |
| 456 if (character_event) { | |
| 457 ui::KeyEvent charEvent = GetCharacterEventFromNSEvent(keyDownEvent_); | |
| 458 [self handleKeyEvent:&charEvent]; | |
| 459 } | |
| 460 } | |
| 461 | |
| 415 - (views::DragDropClientMac*)dragDropClient { | 462 - (views::DragDropClientMac*)dragDropClient { |
| 416 views::BridgedNativeWidget* bridge = | 463 views::BridgedNativeWidget* bridge = |
| 417 views::NativeWidgetMac::GetBridgeForNativeWindow([self window]); | 464 views::NativeWidgetMac::GetBridgeForNativeWindow([self window]); |
| 418 return bridge ? bridge->drag_drop_client() : nullptr; | 465 return bridge ? bridge->drag_drop_client() : nullptr; |
| 419 } | 466 } |
| 420 | 467 |
| 421 - (void)undo:(id)sender { | 468 - (void)undo:(id)sender { |
| 422 // This DCHECK is more strict than a similar check in handleAction:. It can be | 469 // This DCHECK is more strict than a similar check in handleAction:. It can be |
| 423 // done here because the actors sending these actions should be calling | 470 // done here because the actors sending these actions should be calling |
| 424 // validateUserInterfaceItem: before enabling UI that allows these messages to | 471 // validateUserInterfaceItem: before enabling UI that allows these messages to |
| (...skipping 202 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 627 | 674 |
| 628 - (NSTextInputContext*)inputContext { | 675 - (NSTextInputContext*)inputContext { |
| 629 // If the textInputClient_ does not exist, return nil since this view does not | 676 // If the textInputClient_ does not exist, return nil since this view does not |
| 630 // conform to NSTextInputClient protocol. | 677 // conform to NSTextInputClient protocol. |
| 631 if (!textInputClient_) | 678 if (!textInputClient_) |
| 632 return nil; | 679 return nil; |
| 633 | 680 |
| 634 // If a menu is active, and -[NSView interpretKeyEvents:] asks for the | 681 // If a menu is active, and -[NSView interpretKeyEvents:] asks for the |
| 635 // input context, return nil. This ensures the action message is sent to | 682 // input context, return nil. This ensures the action message is sent to |
| 636 // the view, rather than any NSTextInputClient a subview has installed. | 683 // the view, rather than any NSTextInputClient a subview has installed. |
| 637 MenuController* menuController = MenuController::GetActiveInstance(); | 684 if ([self activeMenuController]) |
| 638 if (menuController && menuController->owner() == hostedView_->GetWidget()) | |
| 639 return nil; | 685 return nil; |
| 640 | 686 |
| 641 // When not in an editable mode, or while entering passwords | 687 // When not in an editable mode, or while entering passwords |
| 642 // (http://crbug.com/23219), we don't want to show IME candidate windows. | 688 // (http://crbug.com/23219), we don't want to show IME candidate windows. |
| 643 // Returning nil prevents this view from getting messages defined as part of | 689 // Returning nil prevents this view from getting messages defined as part of |
| 644 // the NSTextInputClient protocol. | 690 // the NSTextInputClient protocol. |
| 645 switch (textInputClient_->GetTextInputType()) { | 691 switch (textInputClient_->GetTextInputType()) { |
| 646 case ui::TEXT_INPUT_TYPE_NONE: | 692 case ui::TEXT_INPUT_TYPE_NONE: |
| 647 case ui::TEXT_INPUT_TYPE_PASSWORD: | 693 case ui::TEXT_INPUT_TYPE_PASSWORD: |
| 648 return nil; | 694 return nil; |
| (...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 683 // views::Textfields are single-line only, map Paragraph and Document commands | 729 // views::Textfields are single-line only, map Paragraph and Document commands |
| 684 // to Line. Also, Up/Down commands correspond to beginning/end of line. | 730 // to Line. Also, Up/Down commands correspond to beginning/end of line. |
| 685 | 731 |
| 686 // The insertText action message forwards to the TextInputClient unless a menu | 732 // The insertText action message forwards to the TextInputClient unless a menu |
| 687 // is active. Note that NSResponder's interpretKeyEvents: implementation doesn't | 733 // is active. Note that NSResponder's interpretKeyEvents: implementation doesn't |
| 688 // direct insertText: through doCommandBySelector:, so this is still needed to | 734 // direct insertText: through doCommandBySelector:, so this is still needed to |
| 689 // handle the case when inputContext: is nil. When inputContext: returns non-nil | 735 // handle the case when inputContext: is nil. When inputContext: returns non-nil |
| 690 // text goes directly to insertText:replacementRange:. | 736 // text goes directly to insertText:replacementRange:. |
| 691 - (void)insertText:(id)text { | 737 - (void)insertText:(id)text { |
| 692 DCHECK_EQ(nil, [self inputContext]); | 738 DCHECK_EQ(nil, [self inputContext]); |
| 693 | 739 [self insertTextInternal:text]; |
| 694 // Only handle the case where no. of characters is 1. Cases not handled (not | |
| 695 // an exhaustive list): | |
| 696 // - |text| contains a unicode surrogate pair, i.e. a single grapheme which | |
| 697 // requires two 16 bit characters. Currently Views menu only supports | |
| 698 // mnemonics using a single 16 bit character, so it is ok to ignore this | |
| 699 // case. | |
| 700 // - Programmatically created events. | |
| 701 // - Input from IME. But this case should not occur since inputContext is nil. | |
| 702 if (keyDownEvent_ && [text length] == 1) { | |
| 703 ui::KeyEvent charEvent = GetCharacterEventFromNSEvent(keyDownEvent_); | |
| 704 [self handleKeyEvent:&charEvent]; | |
| 705 } | |
| 706 } | 740 } |
| 707 | 741 |
| 708 // Selection movement and scrolling. | 742 // Selection movement and scrolling. |
| 709 | 743 |
| 710 - (void)moveForward:(id)sender { | 744 - (void)moveForward:(id)sender { |
| 711 IsTextRTL(textInputClient_) ? [self moveLeft:sender] | 745 IsTextRTL(textInputClient_) ? [self moveLeft:sender] |
| 712 : [self moveRight:sender]; | 746 : [self moveRight:sender]; |
| 713 } | 747 } |
| 714 | 748 |
| 715 - (void)moveRight:(id)sender { | 749 - (void)moveRight:(id)sender { |
| (...skipping 413 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1129 } | 1163 } |
| 1130 | 1164 |
| 1131 - (BOOL)hasMarkedText { | 1165 - (BOOL)hasMarkedText { |
| 1132 return textInputClient_ && textInputClient_->HasCompositionText(); | 1166 return textInputClient_ && textInputClient_->HasCompositionText(); |
| 1133 } | 1167 } |
| 1134 | 1168 |
| 1135 - (void)insertText:(id)text replacementRange:(NSRange)replacementRange { | 1169 - (void)insertText:(id)text replacementRange:(NSRange)replacementRange { |
| 1136 if (!hostedView_) | 1170 if (!hostedView_) |
| 1137 return; | 1171 return; |
| 1138 | 1172 |
| 1139 if ([text isKindOfClass:[NSAttributedString class]]) | |
| 1140 text = [text string]; | |
| 1141 | |
| 1142 // Verify inputContext is not nil, i.e. |textInputClient_| is valid and no | 1173 // Verify inputContext is not nil, i.e. |textInputClient_| is valid and no |
| 1143 // menu is active. | 1174 // menu is active. |
| 1144 DCHECK([self inputContext]); | 1175 DCHECK([self inputContext]); |
| 1145 | 1176 |
| 1146 textInputClient_->DeleteRange(gfx::Range(replacementRange)); | 1177 textInputClient_->DeleteRange(gfx::Range(replacementRange)); |
| 1147 | 1178 [self insertTextInternal:text]; |
| 1148 // If a single character is inserted by keyDown's call to interpretKeyEvents: | |
| 1149 // then use InsertChar() to allow editing events to be merged. | |
| 1150 if (keyDownEvent_ && [text length] == 1) | |
| 1151 textInputClient_->InsertChar(GetCharacterEventFromNSEvent(keyDownEvent_)); | |
| 1152 else | |
| 1153 textInputClient_->InsertText(base::SysNSStringToUTF16(text)); | |
| 1154 } | 1179 } |
| 1155 | 1180 |
| 1156 - (NSRange)markedRange { | 1181 - (NSRange)markedRange { |
| 1157 if (!textInputClient_) | 1182 if (!textInputClient_) |
| 1158 return NSMakeRange(NSNotFound, 0); | 1183 return NSMakeRange(NSNotFound, 0); |
| 1159 | 1184 |
| 1160 gfx::Range range; | 1185 gfx::Range range; |
| 1161 textInputClient_->GetCompositionTextRange(&range); | 1186 textInputClient_->GetCompositionTextRange(&range); |
| 1162 return range.ToNSRange(); | 1187 return range.ToNSRange(); |
| 1163 } | 1188 } |
| (...skipping 82 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1246 } | 1271 } |
| 1247 | 1272 |
| 1248 return [super accessibilityAttributeValue:attribute]; | 1273 return [super accessibilityAttributeValue:attribute]; |
| 1249 } | 1274 } |
| 1250 | 1275 |
| 1251 - (id)accessibilityHitTest:(NSPoint)point { | 1276 - (id)accessibilityHitTest:(NSPoint)point { |
| 1252 return [hostedView_->GetNativeViewAccessible() accessibilityHitTest:point]; | 1277 return [hostedView_->GetNativeViewAccessible() accessibilityHitTest:point]; |
| 1253 } | 1278 } |
| 1254 | 1279 |
| 1255 @end | 1280 @end |
| OLD | NEW |