diff options
Diffstat (limited to 'widget/cocoa/NativeKeyBindings.mm')
-rw-r--r-- | widget/cocoa/NativeKeyBindings.mm | 605 |
1 files changed, 605 insertions, 0 deletions
diff --git a/widget/cocoa/NativeKeyBindings.mm b/widget/cocoa/NativeKeyBindings.mm new file mode 100644 index 0000000000..d3e5983259 --- /dev/null +++ b/widget/cocoa/NativeKeyBindings.mm @@ -0,0 +1,605 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "NativeKeyBindings.h" + +#include "nsTArray.h" +#include "nsCocoaUtils.h" +#include "mozilla/Logging.h" +#include "mozilla/Maybe.h" +#include "mozilla/NativeKeyBindingsType.h" +#include "mozilla/TextEvents.h" +#include "mozilla/WritingModes.h" + +#import <Cocoa/Cocoa.h> +#import <Carbon/Carbon.h> + +namespace mozilla { +namespace widget { + +static LazyLogModule gNativeKeyBindingsLog("NativeKeyBindings"); + +NativeKeyBindings* NativeKeyBindings::sInstanceForSingleLineEditor = nullptr; +NativeKeyBindings* NativeKeyBindings::sInstanceForMultiLineEditor = nullptr; + +// static +NativeKeyBindings* NativeKeyBindings::GetInstance(NativeKeyBindingsType aType) { + switch (aType) { + case NativeKeyBindingsType::SingleLineEditor: + if (!sInstanceForSingleLineEditor) { + sInstanceForSingleLineEditor = new NativeKeyBindings(); + sInstanceForSingleLineEditor->Init(aType); + } + return sInstanceForSingleLineEditor; + case NativeKeyBindingsType::MultiLineEditor: + case NativeKeyBindingsType::RichTextEditor: + if (!sInstanceForMultiLineEditor) { + sInstanceForMultiLineEditor = new NativeKeyBindings(); + sInstanceForMultiLineEditor->Init(aType); + } + return sInstanceForMultiLineEditor; + default: + MOZ_CRASH("Not implemented"); + return nullptr; + } +} + +// static +void NativeKeyBindings::Shutdown() { + delete sInstanceForSingleLineEditor; + sInstanceForSingleLineEditor = nullptr; + delete sInstanceForMultiLineEditor; + sInstanceForMultiLineEditor = nullptr; +} + +NativeKeyBindings::NativeKeyBindings() {} + +inline objc_selector* ToObjcSelectorPtr(SEL aSel) { return reinterpret_cast<objc_selector*>(aSel); } +#define SEL_TO_COMMAND(aSel, aCommand) \ + mSelectorToCommand.InsertOrUpdate(ToObjcSelectorPtr(@selector(aSel)), aCommand) + +void NativeKeyBindings::Init(NativeKeyBindingsType aType) { + MOZ_LOG(gNativeKeyBindingsLog, LogLevel::Info, ("%p NativeKeyBindings::Init", this)); + + // Many selectors have a one-to-one mapping to a Gecko command. Those mappings + // are registered in mSelectorToCommand. + + // Selectors from NSResponder's "Responding to Action Messages" section and + // from NSText's "Action Methods for Editing" section + + // TODO: Improves correctness of left / right meaning + // TODO: Add real paragraph motions + + // SEL_TO_COMMAND(cancelOperation:, ); + // SEL_TO_COMMAND(capitalizeWord:, ); + // SEL_TO_COMMAND(centerSelectionInVisibleArea:, ); + // SEL_TO_COMMAND(changeCaseOfLetter:, ); + // SEL_TO_COMMAND(complete:, ); + SEL_TO_COMMAND(copy:, Command::Copy); + // SEL_TO_COMMAND(copyFont:, ); + // SEL_TO_COMMAND(copyRuler:, ); + SEL_TO_COMMAND(cut:, Command::Cut); + SEL_TO_COMMAND(delete:, Command::Delete); + SEL_TO_COMMAND(deleteBackward:, Command::DeleteCharBackward); + // SEL_TO_COMMAND(deleteBackwardByDecomposingPreviousCharacter:, ); + SEL_TO_COMMAND(deleteForward:, Command::DeleteCharForward); + + // TODO: deleteTo* selectors are also supposed to add text to a kill buffer + SEL_TO_COMMAND(deleteToBeginningOfLine:, Command::DeleteToBeginningOfLine); + SEL_TO_COMMAND(deleteToBeginningOfParagraph:, Command::DeleteToBeginningOfLine); + SEL_TO_COMMAND(deleteToEndOfLine:, Command::DeleteToEndOfLine); + SEL_TO_COMMAND(deleteToEndOfParagraph:, Command::DeleteToEndOfLine); + // SEL_TO_COMMAND(deleteToMark:, ); + + SEL_TO_COMMAND(deleteWordBackward:, Command::DeleteWordBackward); + SEL_TO_COMMAND(deleteWordForward:, Command::DeleteWordForward); + // SEL_TO_COMMAND(indent:, ); + // SEL_TO_COMMAND(insertBacktab:, ); + // SEL_TO_COMMAND(insertContainerBreak:, ); + // SEL_TO_COMMAND(insertLineBreak:, ); + // SEL_TO_COMMAND(insertNewline:, ); + // SEL_TO_COMMAND(insertNewlineIgnoringFieldEditor:, ); + // SEL_TO_COMMAND(insertParagraphSeparator:, ); + // SEL_TO_COMMAND(insertTab:, ); + // SEL_TO_COMMAND(insertTabIgnoringFieldEditor:, ); + // SEL_TO_COMMAND(insertDoubleQuoteIgnoringSubstitution:, ); + // SEL_TO_COMMAND(insertSingleQuoteIgnoringSubstitution:, ); + // SEL_TO_COMMAND(lowercaseWord:, ); + SEL_TO_COMMAND(moveBackward:, Command::CharPrevious); + SEL_TO_COMMAND(moveBackwardAndModifySelection:, Command::SelectCharPrevious); + if (aType == NativeKeyBindingsType::SingleLineEditor) { + SEL_TO_COMMAND(moveDown:, Command::EndLine); + } else { + SEL_TO_COMMAND(moveDown:, Command::LineNext); + } + SEL_TO_COMMAND(moveDownAndModifySelection:, Command::SelectLineNext); + SEL_TO_COMMAND(moveForward:, Command::CharNext); + SEL_TO_COMMAND(moveForwardAndModifySelection:, Command::SelectCharNext); + SEL_TO_COMMAND(moveLeft:, Command::CharPrevious); + SEL_TO_COMMAND(moveLeftAndModifySelection:, Command::SelectCharPrevious); + SEL_TO_COMMAND(moveParagraphBackwardAndModifySelection:, Command::SelectBeginLine); + SEL_TO_COMMAND(moveParagraphForwardAndModifySelection:, Command::SelectEndLine); + SEL_TO_COMMAND(moveRight:, Command::CharNext); + SEL_TO_COMMAND(moveRightAndModifySelection:, Command::SelectCharNext); + SEL_TO_COMMAND(moveToBeginningOfDocument:, Command::MoveTop); + SEL_TO_COMMAND(moveToBeginningOfDocumentAndModifySelection:, Command::SelectTop); + SEL_TO_COMMAND(moveToBeginningOfLine:, Command::BeginLine); + SEL_TO_COMMAND(moveToBeginningOfLineAndModifySelection:, Command::SelectBeginLine); + SEL_TO_COMMAND(moveToBeginningOfParagraph:, Command::BeginLine); + SEL_TO_COMMAND(moveToBeginningOfParagraphAndModifySelection:, Command::SelectBeginLine); + SEL_TO_COMMAND(moveToEndOfDocument:, Command::MoveBottom); + SEL_TO_COMMAND(moveToEndOfDocumentAndModifySelection:, Command::SelectBottom); + SEL_TO_COMMAND(moveToEndOfLine:, Command::EndLine); + SEL_TO_COMMAND(moveToEndOfLineAndModifySelection:, Command::SelectEndLine); + SEL_TO_COMMAND(moveToEndOfParagraph:, Command::EndLine); + SEL_TO_COMMAND(moveToEndOfParagraphAndModifySelection:, Command::SelectEndLine); + SEL_TO_COMMAND(moveToLeftEndOfLine:, Command::BeginLine); + SEL_TO_COMMAND(moveToLeftEndOfLineAndModifySelection:, Command::SelectBeginLine); + SEL_TO_COMMAND(moveToRightEndOfLine:, Command::EndLine); + SEL_TO_COMMAND(moveToRightEndOfLineAndModifySelection:, Command::SelectEndLine); + if (aType == NativeKeyBindingsType::SingleLineEditor) { + SEL_TO_COMMAND(moveUp:, Command::BeginLine); + } else { + SEL_TO_COMMAND(moveUp:, Command::LinePrevious); + } + SEL_TO_COMMAND(moveUpAndModifySelection:, Command::SelectLinePrevious); + SEL_TO_COMMAND(moveWordBackward:, Command::WordPrevious); + SEL_TO_COMMAND(moveWordBackwardAndModifySelection:, Command::SelectWordPrevious); + SEL_TO_COMMAND(moveWordForward:, Command::WordNext); + SEL_TO_COMMAND(moveWordForwardAndModifySelection:, Command::SelectWordNext); + SEL_TO_COMMAND(moveWordLeft:, Command::WordPrevious); + SEL_TO_COMMAND(moveWordLeftAndModifySelection:, Command::SelectWordPrevious); + SEL_TO_COMMAND(moveWordRight:, Command::WordNext); + SEL_TO_COMMAND(moveWordRightAndModifySelection:, Command::SelectWordNext); + SEL_TO_COMMAND(pageDown:, Command::MovePageDown); + SEL_TO_COMMAND(pageDownAndModifySelection:, Command::SelectPageDown); + SEL_TO_COMMAND(pageUp:, Command::MovePageUp); + SEL_TO_COMMAND(pageUpAndModifySelection:, Command::SelectPageUp); + SEL_TO_COMMAND(paste:, Command::Paste); + // SEL_TO_COMMAND(pasteFont:, ); + // SEL_TO_COMMAND(pasteRuler:, ); + SEL_TO_COMMAND(scrollLineDown:, Command::ScrollLineDown); + SEL_TO_COMMAND(scrollLineUp:, Command::ScrollLineUp); + SEL_TO_COMMAND(scrollPageDown:, Command::ScrollPageDown); + SEL_TO_COMMAND(scrollPageUp:, Command::ScrollPageUp); + SEL_TO_COMMAND(scrollToBeginningOfDocument:, Command::ScrollTop); + SEL_TO_COMMAND(scrollToEndOfDocument:, Command::ScrollBottom); + SEL_TO_COMMAND(selectAll:, Command::SelectAll); + // selectLine: is complex, see KeyDown + if (aType == NativeKeyBindingsType::SingleLineEditor) { + SEL_TO_COMMAND(selectParagraph:, Command::SelectAll); + } + // SEL_TO_COMMAND(selectToMark:, ); + // selectWord: is complex, see KeyDown + // SEL_TO_COMMAND(setMark:, ); + // SEL_TO_COMMAND(showContextHelp:, ); + // SEL_TO_COMMAND(supplementalTargetForAction:sender:, ); + // SEL_TO_COMMAND(swapWithMark:, ); + // SEL_TO_COMMAND(transpose:, ); + // SEL_TO_COMMAND(transposeWords:, ); + // SEL_TO_COMMAND(uppercaseWord:, ); + // SEL_TO_COMMAND(yank:, ); +} + +#undef SEL_TO_COMMAND + +void NativeKeyBindings::GetEditCommands(const WidgetKeyboardEvent& aEvent, + const Maybe<WritingMode>& aWritingMode, + nsTArray<CommandInt>& aCommands) { + MOZ_ASSERT(!aEvent.mFlags.mIsSynthesizedForTests); + MOZ_ASSERT(aCommands.IsEmpty()); + + MOZ_LOG(gNativeKeyBindingsLog, LogLevel::Info, ("%p NativeKeyBindings::GetEditCommands", this)); + + // Recover the current event, which should always be the key down we are + // responding to. + + NSEvent* cocoaEvent = reinterpret_cast<NSEvent*>(aEvent.mNativeKeyEvent); + + if (!cocoaEvent || [cocoaEvent type] != NSEventTypeKeyDown) { + MOZ_LOG(gNativeKeyBindingsLog, LogLevel::Info, + ("%p NativeKeyBindings::GetEditCommands, no Cocoa key down event", this)); + + return; + } + + if (aWritingMode.isSome() && aEvent.NeedsToRemapNavigationKey() && + aWritingMode.ref().IsVertical()) { + NSEvent* originalEvent = cocoaEvent; + + // TODO: Use KeyNameIndex rather than legacy keyCode. + uint32_t remappedGeckoKeyCode = aEvent.GetRemappedKeyCode(aWritingMode.ref()); + uint32_t remappedCocoaKeyCode = 0; + switch (remappedGeckoKeyCode) { + case NS_VK_UP: + remappedCocoaKeyCode = kVK_UpArrow; + break; + case NS_VK_DOWN: + remappedCocoaKeyCode = kVK_DownArrow; + break; + case NS_VK_LEFT: + remappedCocoaKeyCode = kVK_LeftArrow; + break; + case NS_VK_RIGHT: + remappedCocoaKeyCode = kVK_RightArrow; + break; + default: + MOZ_ASSERT_UNREACHABLE("Add a case for the new remapped key"); + return; + } + unichar ch = nsCocoaUtils::ConvertGeckoKeyCodeToMacCharCode(remappedGeckoKeyCode); + NSString* chars = [[[NSString alloc] initWithCharacters:&ch length:1] autorelease]; + cocoaEvent = [NSEvent keyEventWithType:[originalEvent type] + location:[originalEvent locationInWindow] + modifierFlags:[originalEvent modifierFlags] + timestamp:[originalEvent timestamp] + windowNumber:[originalEvent windowNumber] + context:nil + characters:chars + charactersIgnoringModifiers:chars + isARepeat:[originalEvent isARepeat] + keyCode:remappedCocoaKeyCode]; + } + + MOZ_LOG(gNativeKeyBindingsLog, LogLevel::Info, + ("%p NativeKeyBindings::GetEditCommands, interpreting", this)); + + AutoTArray<KeyBindingsCommand, 2> bindingCommands; + nsCocoaUtils::GetCommandsFromKeyEvent(cocoaEvent, bindingCommands); + + MOZ_LOG(gNativeKeyBindingsLog, LogLevel::Info, + ("%p NativeKeyBindings::GetEditCommands, bindingCommands=%zu", this, + bindingCommands.Length())); + + for (uint32_t i = 0; i < bindingCommands.Length(); i++) { + SEL selector = bindingCommands[i].selector; + + if (MOZ_LOG_TEST(gNativeKeyBindingsLog, LogLevel::Info)) { + NSString* selectorString = NSStringFromSelector(selector); + nsAutoString nsSelectorString; + nsCocoaUtils::GetStringForNSString(selectorString, nsSelectorString); + + MOZ_LOG(gNativeKeyBindingsLog, LogLevel::Info, + ("%p NativeKeyBindings::GetEditCommands, selector=%s", this, + NS_LossyConvertUTF16toASCII(nsSelectorString).get())); + } + + AppendEditCommandsForSelector(ToObjcSelectorPtr(selector), aCommands); + } + + LogEditCommands(aCommands, "NativeKeyBindings::GetEditCommands"); +} + +void NativeKeyBindings::AppendEditCommandsForSelector(objc_selector* aSelector, + nsTArray<CommandInt>& aCommands) const { + // Try to find a simple mapping in the hashtable + Command geckoCommand = Command::DoNothing; + if (mSelectorToCommand.Get(aSelector, &geckoCommand) && geckoCommand != Command::DoNothing) { + aCommands.AppendElement(static_cast<CommandInt>(geckoCommand)); + } else if (aSelector == ToObjcSelectorPtr(@selector(selectLine:))) { + // This is functional, but Cocoa's version is direction-less in that + // selection direction is not determined until some future directed action + // is taken. See bug 282097, comment 79 for more details. + aCommands.AppendElement(static_cast<CommandInt>(Command::BeginLine)); + aCommands.AppendElement(static_cast<CommandInt>(Command::SelectEndLine)); + } else if (aSelector == ToObjcSelectorPtr(@selector(selectWord:))) { + // This is functional, but Cocoa's version is direction-less in that + // selection direction is not determined until some future directed action + // is taken. See bug 282097, comment 79 for more details. + aCommands.AppendElement(static_cast<CommandInt>(Command::WordPrevious)); + aCommands.AppendElement(static_cast<CommandInt>(Command::SelectWordNext)); + } +} + +void NativeKeyBindings::LogEditCommands(const nsTArray<CommandInt>& aCommands, + const char* aDescription) const { + if (!MOZ_LOG_TEST(gNativeKeyBindingsLog, LogLevel::Info)) { + return; + } + + if (aCommands.IsEmpty()) { + MOZ_LOG(gNativeKeyBindingsLog, LogLevel::Info, ("%p %s, no edit commands", this, aDescription)); + return; + } + + for (CommandInt commandInt : aCommands) { + Command geckoCommand = static_cast<Command>(commandInt); + MOZ_LOG(gNativeKeyBindingsLog, LogLevel::Info, + ("%p %s, command=%s", this, aDescription, + WidgetKeyboardEvent::GetCommandStr(geckoCommand))); + } +} + +// static +void NativeKeyBindings::GetEditCommandsForTests(NativeKeyBindingsType aType, + const WidgetKeyboardEvent& aEvent, + const Maybe<WritingMode>& aWritingMode, + nsTArray<CommandInt>& aCommands) { + MOZ_DIAGNOSTIC_ASSERT(aEvent.IsTrusted()); + + // The following mapping is checked on Big Sur. Some of them are defined in: + // https://support.apple.com/en-us/HT201236#text + NativeKeyBindings* instance = NativeKeyBindings::GetInstance(aType); + if (NS_WARN_IF(!instance)) { + return; + } + switch (aWritingMode.isSome() ? aEvent.GetRemappedKeyNameIndex(aWritingMode.ref()) + : aEvent.mKeyNameIndex) { + case KEY_NAME_INDEX_USE_STRING: + if (!aEvent.IsControl() || aEvent.IsAlt() || aEvent.IsMeta()) { + break; + } + switch (aEvent.PseudoCharCode()) { + case 'a': + case 'A': + instance->AppendEditCommandsForSelector( + !aEvent.IsShift() + ? ToObjcSelectorPtr(@selector(moveToBeginningOfParagraph:)) + : ToObjcSelectorPtr(@selector(moveToBeginningOfParagraphAndModifySelection:)), + aCommands); + break; + case 'b': + case 'B': + instance->AppendEditCommandsForSelector( + !aEvent.IsShift() ? ToObjcSelectorPtr(@selector(moveBackward:)) + : ToObjcSelectorPtr(@selector(moveBackwardAndModifySelection:)), + aCommands); + break; + case 'd': + case 'D': + if (!aEvent.IsShift()) { + instance->AppendEditCommandsForSelector(ToObjcSelectorPtr(@selector(deleteForward:)), + aCommands); + } + break; + case 'e': + case 'E': + instance->AppendEditCommandsForSelector( + !aEvent.IsShift() + ? ToObjcSelectorPtr(@selector(moveToEndOfParagraph:)) + : ToObjcSelectorPtr(@selector(moveToEndOfParagraphAndModifySelection:)), + aCommands); + break; + case 'f': + case 'F': + instance->AppendEditCommandsForSelector( + !aEvent.IsShift() ? ToObjcSelectorPtr(@selector(moveForward:)) + : ToObjcSelectorPtr(@selector(moveForwardAndModifySelection:)), + aCommands); + break; + case 'h': + case 'H': + if (!aEvent.IsShift()) { + instance->AppendEditCommandsForSelector(ToObjcSelectorPtr(@selector(deleteBackward:)), + aCommands); + } + break; + case 'k': + case 'K': + if (!aEvent.IsShift()) { + instance->AppendEditCommandsForSelector( + ToObjcSelectorPtr(@selector(deleteToEndOfParagraph:)), aCommands); + } + break; + case 'n': + case 'N': + instance->AppendEditCommandsForSelector( + !aEvent.IsShift() ? ToObjcSelectorPtr(@selector(moveDown:)) + : ToObjcSelectorPtr(@selector(moveDownAndModifySelection:)), + aCommands); + break; + case 'p': + case 'P': + instance->AppendEditCommandsForSelector( + !aEvent.IsShift() ? ToObjcSelectorPtr(@selector(moveUp:)) + : ToObjcSelectorPtr(@selector(moveUpAndModifySelection:)), + aCommands); + break; + default: + break; + } + break; + case KEY_NAME_INDEX_Backspace: + if (aEvent.IsMeta()) { + if (aEvent.IsAlt() || aEvent.IsControl()) { + break; + } + // Shift is ignored. + instance->AppendEditCommandsForSelector( + ToObjcSelectorPtr(@selector(deleteToBeginningOfLine:)), aCommands); + break; + } + if (aEvent.IsAlt()) { + // Shift and Control are ignored. + instance->AppendEditCommandsForSelector(ToObjcSelectorPtr(@selector(deleteWordBackward:)), + aCommands); + break; + } + if (aEvent.IsControl()) { + if (aEvent.IsShift()) { + instance->AppendEditCommandsForSelector( + ToObjcSelectorPtr(@selector(deleteBackwardByDecomposingPreviousCharacter:)), + aCommands); + } + break; + } + // Shift is ignored. + instance->AppendEditCommandsForSelector(ToObjcSelectorPtr(@selector(deleteBackward:)), + aCommands); + break; + case KEY_NAME_INDEX_Delete: + if (aEvent.IsControl() || aEvent.IsMeta()) { + break; + } + if (aEvent.IsAlt()) { + // Shift is ignored. + instance->AppendEditCommandsForSelector(ToObjcSelectorPtr(@selector(deleteWordForward:)), + aCommands); + break; + } + // Shift is ignored. + instance->AppendEditCommandsForSelector(ToObjcSelectorPtr(@selector(deleteForward:)), + aCommands); + break; + case KEY_NAME_INDEX_PageDown: + if (aEvent.IsControl() || aEvent.IsMeta()) { + break; + } + if (aEvent.IsAlt()) { + // Shift is ignored. + instance->AppendEditCommandsForSelector(ToObjcSelectorPtr(@selector(pageDown:)), aCommands); + break; + } + instance->AppendEditCommandsForSelector( + !aEvent.IsShift() ? ToObjcSelectorPtr(@selector(scrollPageDown:)) + : ToObjcSelectorPtr(@selector(pageDownAndModifySelection:)), + aCommands); + break; + case KEY_NAME_INDEX_PageUp: + if (aEvent.IsControl() || aEvent.IsMeta()) { + break; + } + if (aEvent.IsAlt()) { + // Shift is ignored. + instance->AppendEditCommandsForSelector(ToObjcSelectorPtr(@selector(pageUp:)), aCommands); + break; + } + instance->AppendEditCommandsForSelector( + !aEvent.IsShift() ? ToObjcSelectorPtr(@selector(scrollPageUp:)) + : ToObjcSelectorPtr(@selector(pageUpAndModifySelection:)), + aCommands); + break; + case KEY_NAME_INDEX_Home: + if (aEvent.IsAlt() || aEvent.IsControl() || aEvent.IsMeta()) { + break; + } + instance->AppendEditCommandsForSelector( + !aEvent.IsShift() + ? ToObjcSelectorPtr(@selector(scrollToBeginningOfDocument:)) + : ToObjcSelectorPtr(@selector(moveToBeginningOfDocumentAndModifySelection:)), + aCommands); + break; + case KEY_NAME_INDEX_End: + if (aEvent.IsAlt() || aEvent.IsControl() || aEvent.IsMeta()) { + break; + } + instance->AppendEditCommandsForSelector( + !aEvent.IsShift() ? ToObjcSelectorPtr(@selector(scrollToEndOfDocument:)) + : ToObjcSelectorPtr(@selector(moveToEndOfDocumentAndModifySelection:)), + aCommands); + break; + case KEY_NAME_INDEX_ArrowLeft: + if (aEvent.IsAlt()) { + break; + } + if (aEvent.IsMeta() || (aEvent.IsControl() && aEvent.IsShift())) { + instance->AppendEditCommandsForSelector( + !aEvent.IsShift() + ? ToObjcSelectorPtr(@selector(moveToLeftEndOfLine:)) + : ToObjcSelectorPtr(@selector(moveToLeftEndOfLineAndModifySelection:)), + aCommands); + break; + } + if (aEvent.IsControl()) { + break; + } + instance->AppendEditCommandsForSelector( + !aEvent.IsShift() ? ToObjcSelectorPtr(@selector(moveLeft:)) + : ToObjcSelectorPtr(@selector(moveLeftAndModifySelection:)), + aCommands); + break; + case KEY_NAME_INDEX_ArrowRight: + if (aEvent.IsAlt()) { + break; + } + if (aEvent.IsMeta() || (aEvent.IsControl() && aEvent.IsShift())) { + instance->AppendEditCommandsForSelector( + !aEvent.IsShift() + ? ToObjcSelectorPtr(@selector(moveToRightEndOfLine:)) + : ToObjcSelectorPtr(@selector(moveToRightEndOfLineAndModifySelection:)), + aCommands); + break; + } + if (aEvent.IsControl()) { + break; + } + instance->AppendEditCommandsForSelector( + !aEvent.IsShift() ? ToObjcSelectorPtr(@selector(moveRight:)) + : ToObjcSelectorPtr(@selector(moveRightAndModifySelection:)), + aCommands); + break; + case KEY_NAME_INDEX_ArrowUp: + if (aEvent.IsControl()) { + break; + } + if (aEvent.IsMeta()) { + if (aEvent.IsAlt()) { + break; + } + instance->AppendEditCommandsForSelector( + !aEvent.IsShift() + ? ToObjcSelectorPtr(@selector(moveToBeginningOfDocument:)) + : ToObjcSelectorPtr(@selector(moveToBegginingOfDocumentAndModifySelection:)), + aCommands); + break; + } + if (aEvent.IsAlt()) { + if (!aEvent.IsShift()) { + instance->AppendEditCommandsForSelector(ToObjcSelectorPtr(@selector(moveBackward:)), + aCommands); + instance->AppendEditCommandsForSelector( + ToObjcSelectorPtr(@selector(moveToBeginningOfParagraph:)), aCommands); + break; + } + instance->AppendEditCommandsForSelector( + ToObjcSelectorPtr(@selector(moveParagraphBackwardAndModifySelection:)), aCommands); + break; + } + instance->AppendEditCommandsForSelector( + !aEvent.IsShift() ? ToObjcSelectorPtr(@selector(moveUp:)) + : ToObjcSelectorPtr(@selector(moveUpAndModifySelection:)), + aCommands); + break; + case KEY_NAME_INDEX_ArrowDown: + if (aEvent.IsControl()) { + break; + } + if (aEvent.IsMeta()) { + if (aEvent.IsAlt()) { + break; + } + instance->AppendEditCommandsForSelector( + !aEvent.IsShift() + ? ToObjcSelectorPtr(@selector(moveToEndOfDocument:)) + : ToObjcSelectorPtr(@selector(moveToEndOfDocumentAndModifySelection:)), + aCommands); + break; + } + if (aEvent.IsAlt()) { + if (!aEvent.IsShift()) { + instance->AppendEditCommandsForSelector(ToObjcSelectorPtr(@selector(moveForward:)), + aCommands); + instance->AppendEditCommandsForSelector( + ToObjcSelectorPtr(@selector(moveToEndOfParagraph:)), aCommands); + break; + } + instance->AppendEditCommandsForSelector( + ToObjcSelectorPtr(@selector(moveParagraphForwardAndModifySelection:)), aCommands); + break; + } + instance->AppendEditCommandsForSelector( + !aEvent.IsShift() ? ToObjcSelectorPtr(@selector(moveDown:)) + : ToObjcSelectorPtr(@selector(moveDownAndModifySelection:)), + aCommands); + break; + default: + break; + } + + instance->LogEditCommands(aCommands, "NativeKeyBindings::GetEditCommandsForTests"); +} + +} // namespace widget +} // namespace mozilla |