Wed, 31 Dec 2014 06:55:50 +0100
Added tag UPSTREAM_283F7C6 for changeset ca08bd8f51b2
michael@0 | 1 | /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
michael@0 | 2 | /* This Source Code Form is subject to the terms of the Mozilla Public |
michael@0 | 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this |
michael@0 | 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
michael@0 | 5 | |
michael@0 | 6 | #include "NativeKeyBindings.h" |
michael@0 | 7 | |
michael@0 | 8 | #include "nsTArray.h" |
michael@0 | 9 | #include "nsCocoaUtils.h" |
michael@0 | 10 | #include "prlog.h" |
michael@0 | 11 | #include "mozilla/TextEvents.h" |
michael@0 | 12 | |
michael@0 | 13 | namespace mozilla { |
michael@0 | 14 | namespace widget { |
michael@0 | 15 | |
michael@0 | 16 | #ifdef PR_LOGGING |
michael@0 | 17 | PRLogModuleInfo* gNativeKeyBindingsLog = nullptr; |
michael@0 | 18 | #endif |
michael@0 | 19 | |
michael@0 | 20 | NativeKeyBindings* NativeKeyBindings::sInstanceForSingleLineEditor = nullptr; |
michael@0 | 21 | NativeKeyBindings* NativeKeyBindings::sInstanceForMultiLineEditor = nullptr; |
michael@0 | 22 | |
michael@0 | 23 | // static |
michael@0 | 24 | NativeKeyBindings* |
michael@0 | 25 | NativeKeyBindings::GetInstance(NativeKeyBindingsType aType) |
michael@0 | 26 | { |
michael@0 | 27 | switch (aType) { |
michael@0 | 28 | case nsIWidget::NativeKeyBindingsForSingleLineEditor: |
michael@0 | 29 | if (!sInstanceForSingleLineEditor) { |
michael@0 | 30 | sInstanceForSingleLineEditor = new NativeKeyBindings(); |
michael@0 | 31 | sInstanceForSingleLineEditor->Init(aType); |
michael@0 | 32 | } |
michael@0 | 33 | return sInstanceForSingleLineEditor; |
michael@0 | 34 | case nsIWidget::NativeKeyBindingsForMultiLineEditor: |
michael@0 | 35 | case nsIWidget::NativeKeyBindingsForRichTextEditor: |
michael@0 | 36 | if (!sInstanceForMultiLineEditor) { |
michael@0 | 37 | sInstanceForMultiLineEditor = new NativeKeyBindings(); |
michael@0 | 38 | sInstanceForMultiLineEditor->Init(aType); |
michael@0 | 39 | } |
michael@0 | 40 | return sInstanceForMultiLineEditor; |
michael@0 | 41 | default: |
michael@0 | 42 | MOZ_CRASH("Not implemented"); |
michael@0 | 43 | return nullptr; |
michael@0 | 44 | } |
michael@0 | 45 | } |
michael@0 | 46 | |
michael@0 | 47 | // static |
michael@0 | 48 | void |
michael@0 | 49 | NativeKeyBindings::Shutdown() |
michael@0 | 50 | { |
michael@0 | 51 | delete sInstanceForSingleLineEditor; |
michael@0 | 52 | sInstanceForSingleLineEditor = nullptr; |
michael@0 | 53 | delete sInstanceForMultiLineEditor; |
michael@0 | 54 | sInstanceForMultiLineEditor = nullptr; |
michael@0 | 55 | } |
michael@0 | 56 | |
michael@0 | 57 | NativeKeyBindings::NativeKeyBindings() |
michael@0 | 58 | { |
michael@0 | 59 | } |
michael@0 | 60 | |
michael@0 | 61 | #define SEL_TO_COMMAND(aSel, aCommand) \ |
michael@0 | 62 | mSelectorToCommand.Put( \ |
michael@0 | 63 | reinterpret_cast<struct objc_selector *>(@selector(aSel)), aCommand) |
michael@0 | 64 | |
michael@0 | 65 | void |
michael@0 | 66 | NativeKeyBindings::Init(NativeKeyBindingsType aType) |
michael@0 | 67 | { |
michael@0 | 68 | #ifdef PR_LOGGING |
michael@0 | 69 | if (!gNativeKeyBindingsLog) { |
michael@0 | 70 | gNativeKeyBindingsLog = PR_NewLogModule("NativeKeyBindings"); |
michael@0 | 71 | } |
michael@0 | 72 | #endif |
michael@0 | 73 | |
michael@0 | 74 | PR_LOG(gNativeKeyBindingsLog, PR_LOG_ALWAYS, |
michael@0 | 75 | ("%p NativeKeyBindings::Init", this)); |
michael@0 | 76 | |
michael@0 | 77 | // Many selectors have a one-to-one mapping to a Gecko command. Those mappings |
michael@0 | 78 | // are registered in mSelectorToCommand. |
michael@0 | 79 | |
michael@0 | 80 | // Selectors from NSResponder's "Responding to Action Messages" section and |
michael@0 | 81 | // from NSText's "Action Methods for Editing" section |
michael@0 | 82 | |
michael@0 | 83 | // TODO: Improves correctness of left / right meaning |
michael@0 | 84 | // TODO: Add real paragraph motions |
michael@0 | 85 | |
michael@0 | 86 | // SEL_TO_COMMAND(cancelOperation:, ); |
michael@0 | 87 | // SEL_TO_COMMAND(capitalizeWord:, ); |
michael@0 | 88 | // SEL_TO_COMMAND(centerSelectionInVisibleArea:, ); |
michael@0 | 89 | // SEL_TO_COMMAND(changeCaseOfLetter:, ); |
michael@0 | 90 | // SEL_TO_COMMAND(complete:, ); |
michael@0 | 91 | SEL_TO_COMMAND(copy:, CommandCopy); |
michael@0 | 92 | // SEL_TO_COMMAND(copyFont:, ); |
michael@0 | 93 | // SEL_TO_COMMAND(copyRuler:, ); |
michael@0 | 94 | SEL_TO_COMMAND(cut:, CommandCut); |
michael@0 | 95 | SEL_TO_COMMAND(delete:, CommandDelete); |
michael@0 | 96 | SEL_TO_COMMAND(deleteBackward:, CommandDeleteCharBackward); |
michael@0 | 97 | // SEL_TO_COMMAND(deleteBackwardByDecomposingPreviousCharacter:, ); |
michael@0 | 98 | SEL_TO_COMMAND(deleteForward:, CommandDeleteCharForward); |
michael@0 | 99 | |
michael@0 | 100 | // TODO: deleteTo* selectors are also supposed to add text to a kill buffer |
michael@0 | 101 | SEL_TO_COMMAND(deleteToBeginningOfLine:, CommandDeleteToBeginningOfLine); |
michael@0 | 102 | SEL_TO_COMMAND(deleteToBeginningOfParagraph:, CommandDeleteToBeginningOfLine); |
michael@0 | 103 | SEL_TO_COMMAND(deleteToEndOfLine:, CommandDeleteToEndOfLine); |
michael@0 | 104 | SEL_TO_COMMAND(deleteToEndOfParagraph:, CommandDeleteToEndOfLine); |
michael@0 | 105 | // SEL_TO_COMMAND(deleteToMark:, ); |
michael@0 | 106 | |
michael@0 | 107 | SEL_TO_COMMAND(deleteWordBackward:, CommandDeleteWordBackward); |
michael@0 | 108 | SEL_TO_COMMAND(deleteWordForward:, CommandDeleteWordForward); |
michael@0 | 109 | // SEL_TO_COMMAND(indent:, ); |
michael@0 | 110 | // SEL_TO_COMMAND(insertBacktab:, ); |
michael@0 | 111 | // SEL_TO_COMMAND(insertContainerBreak:, ); |
michael@0 | 112 | // SEL_TO_COMMAND(insertLineBreak:, ); |
michael@0 | 113 | // SEL_TO_COMMAND(insertNewline:, ); |
michael@0 | 114 | // SEL_TO_COMMAND(insertNewlineIgnoringFieldEditor:, ); |
michael@0 | 115 | // SEL_TO_COMMAND(insertParagraphSeparator:, ); |
michael@0 | 116 | // SEL_TO_COMMAND(insertTab:, ); |
michael@0 | 117 | // SEL_TO_COMMAND(insertTabIgnoringFieldEditor:, ); |
michael@0 | 118 | // SEL_TO_COMMAND(insertDoubleQuoteIgnoringSubstitution:, ); |
michael@0 | 119 | // SEL_TO_COMMAND(insertSingleQuoteIgnoringSubstitution:, ); |
michael@0 | 120 | // SEL_TO_COMMAND(lowercaseWord:, ); |
michael@0 | 121 | SEL_TO_COMMAND(moveBackward:, CommandCharPrevious); |
michael@0 | 122 | SEL_TO_COMMAND(moveBackwardAndModifySelection:, CommandSelectCharPrevious); |
michael@0 | 123 | if (aType == nsIWidget::NativeKeyBindingsForSingleLineEditor) { |
michael@0 | 124 | SEL_TO_COMMAND(moveDown:, CommandEndLine); |
michael@0 | 125 | } else { |
michael@0 | 126 | SEL_TO_COMMAND(moveDown:, CommandLineNext); |
michael@0 | 127 | } |
michael@0 | 128 | SEL_TO_COMMAND(moveDownAndModifySelection:, CommandSelectLineNext); |
michael@0 | 129 | SEL_TO_COMMAND(moveForward:, CommandCharNext); |
michael@0 | 130 | SEL_TO_COMMAND(moveForwardAndModifySelection:, CommandSelectCharNext); |
michael@0 | 131 | SEL_TO_COMMAND(moveLeft:, CommandCharPrevious); |
michael@0 | 132 | SEL_TO_COMMAND(moveLeftAndModifySelection:, CommandSelectCharPrevious); |
michael@0 | 133 | SEL_TO_COMMAND(moveParagraphBackwardAndModifySelection:, |
michael@0 | 134 | CommandSelectBeginLine); |
michael@0 | 135 | SEL_TO_COMMAND(moveParagraphForwardAndModifySelection:, CommandSelectEndLine); |
michael@0 | 136 | SEL_TO_COMMAND(moveRight:, CommandCharNext); |
michael@0 | 137 | SEL_TO_COMMAND(moveRightAndModifySelection:, CommandSelectCharNext); |
michael@0 | 138 | SEL_TO_COMMAND(moveToBeginningOfDocument:, CommandMoveTop); |
michael@0 | 139 | SEL_TO_COMMAND(moveToBeginningOfDocumentAndModifySelection:, |
michael@0 | 140 | CommandSelectTop); |
michael@0 | 141 | SEL_TO_COMMAND(moveToBeginningOfLine:, CommandBeginLine); |
michael@0 | 142 | SEL_TO_COMMAND(moveToBeginningOfLineAndModifySelection:, |
michael@0 | 143 | CommandSelectBeginLine); |
michael@0 | 144 | SEL_TO_COMMAND(moveToBeginningOfParagraph:, CommandBeginLine); |
michael@0 | 145 | SEL_TO_COMMAND(moveToBeginningOfParagraphAndModifySelection:, |
michael@0 | 146 | CommandSelectBeginLine); |
michael@0 | 147 | SEL_TO_COMMAND(moveToEndOfDocument:, CommandMoveBottom); |
michael@0 | 148 | SEL_TO_COMMAND(moveToEndOfDocumentAndModifySelection:, CommandSelectBottom); |
michael@0 | 149 | SEL_TO_COMMAND(moveToEndOfLine:, CommandEndLine); |
michael@0 | 150 | SEL_TO_COMMAND(moveToEndOfLineAndModifySelection:, CommandSelectEndLine); |
michael@0 | 151 | SEL_TO_COMMAND(moveToEndOfParagraph:, CommandEndLine); |
michael@0 | 152 | SEL_TO_COMMAND(moveToEndOfParagraphAndModifySelection:, CommandSelectEndLine); |
michael@0 | 153 | SEL_TO_COMMAND(moveToLeftEndOfLine:, CommandBeginLine); |
michael@0 | 154 | SEL_TO_COMMAND(moveToLeftEndOfLineAndModifySelection:, |
michael@0 | 155 | CommandSelectBeginLine); |
michael@0 | 156 | SEL_TO_COMMAND(moveToRightEndOfLine:, CommandEndLine); |
michael@0 | 157 | SEL_TO_COMMAND(moveToRightEndOfLineAndModifySelection:, CommandSelectEndLine); |
michael@0 | 158 | if (aType == nsIWidget::NativeKeyBindingsForSingleLineEditor) { |
michael@0 | 159 | SEL_TO_COMMAND(moveUp:, CommandBeginLine); |
michael@0 | 160 | } else { |
michael@0 | 161 | SEL_TO_COMMAND(moveUp:, CommandLinePrevious); |
michael@0 | 162 | } |
michael@0 | 163 | SEL_TO_COMMAND(moveUpAndModifySelection:, CommandSelectLinePrevious); |
michael@0 | 164 | SEL_TO_COMMAND(moveWordBackward:, CommandWordPrevious); |
michael@0 | 165 | SEL_TO_COMMAND(moveWordBackwardAndModifySelection:, |
michael@0 | 166 | CommandSelectWordPrevious); |
michael@0 | 167 | SEL_TO_COMMAND(moveWordForward:, CommandWordNext); |
michael@0 | 168 | SEL_TO_COMMAND(moveWordForwardAndModifySelection:, CommandSelectWordNext); |
michael@0 | 169 | SEL_TO_COMMAND(moveWordLeft:, CommandWordPrevious); |
michael@0 | 170 | SEL_TO_COMMAND(moveWordLeftAndModifySelection:, CommandSelectWordPrevious); |
michael@0 | 171 | SEL_TO_COMMAND(moveWordRight:, CommandWordNext); |
michael@0 | 172 | SEL_TO_COMMAND(moveWordRightAndModifySelection:, CommandSelectWordNext); |
michael@0 | 173 | SEL_TO_COMMAND(pageDown:, CommandMovePageDown); |
michael@0 | 174 | SEL_TO_COMMAND(pageDownAndModifySelection:, CommandSelectPageDown); |
michael@0 | 175 | SEL_TO_COMMAND(pageUp:, CommandMovePageUp); |
michael@0 | 176 | SEL_TO_COMMAND(pageUpAndModifySelection:, CommandSelectPageUp); |
michael@0 | 177 | SEL_TO_COMMAND(paste:, CommandPaste); |
michael@0 | 178 | // SEL_TO_COMMAND(pasteFont:, ); |
michael@0 | 179 | // SEL_TO_COMMAND(pasteRuler:, ); |
michael@0 | 180 | SEL_TO_COMMAND(scrollLineDown:, CommandScrollLineDown); |
michael@0 | 181 | SEL_TO_COMMAND(scrollLineUp:, CommandScrollLineUp); |
michael@0 | 182 | SEL_TO_COMMAND(scrollPageDown:, CommandScrollPageDown); |
michael@0 | 183 | SEL_TO_COMMAND(scrollPageUp:, CommandScrollPageUp); |
michael@0 | 184 | SEL_TO_COMMAND(scrollToBeginningOfDocument:, CommandScrollTop); |
michael@0 | 185 | SEL_TO_COMMAND(scrollToEndOfDocument:, CommandScrollBottom); |
michael@0 | 186 | SEL_TO_COMMAND(selectAll:, CommandSelectAll); |
michael@0 | 187 | // selectLine: is complex, see KeyDown |
michael@0 | 188 | if (aType == nsIWidget::NativeKeyBindingsForSingleLineEditor) { |
michael@0 | 189 | SEL_TO_COMMAND(selectParagraph:, CommandSelectAll); |
michael@0 | 190 | } |
michael@0 | 191 | // SEL_TO_COMMAND(selectToMark:, ); |
michael@0 | 192 | // selectWord: is complex, see KeyDown |
michael@0 | 193 | // SEL_TO_COMMAND(setMark:, ); |
michael@0 | 194 | // SEL_TO_COMMAND(showContextHelp:, ); |
michael@0 | 195 | // SEL_TO_COMMAND(supplementalTargetForAction:sender:, ); |
michael@0 | 196 | // SEL_TO_COMMAND(swapWithMark:, ); |
michael@0 | 197 | // SEL_TO_COMMAND(transpose:, ); |
michael@0 | 198 | // SEL_TO_COMMAND(transposeWords:, ); |
michael@0 | 199 | // SEL_TO_COMMAND(uppercaseWord:, ); |
michael@0 | 200 | // SEL_TO_COMMAND(yank:, ); |
michael@0 | 201 | } |
michael@0 | 202 | |
michael@0 | 203 | #undef SEL_TO_COMMAND |
michael@0 | 204 | |
michael@0 | 205 | bool |
michael@0 | 206 | NativeKeyBindings::Execute(const WidgetKeyboardEvent& aEvent, |
michael@0 | 207 | DoCommandCallback aCallback, |
michael@0 | 208 | void* aCallbackData) |
michael@0 | 209 | { |
michael@0 | 210 | PR_LOG(gNativeKeyBindingsLog, PR_LOG_ALWAYS, |
michael@0 | 211 | ("%p NativeKeyBindings::KeyPress", this)); |
michael@0 | 212 | |
michael@0 | 213 | // Recover the current event, which should always be the key down we are |
michael@0 | 214 | // responding to. |
michael@0 | 215 | |
michael@0 | 216 | NSEvent* cocoaEvent = reinterpret_cast<NSEvent*>(aEvent.mNativeKeyEvent); |
michael@0 | 217 | |
michael@0 | 218 | if (!cocoaEvent || [cocoaEvent type] != NSKeyDown) { |
michael@0 | 219 | PR_LOG(gNativeKeyBindingsLog, PR_LOG_ALWAYS, |
michael@0 | 220 | ("%p NativeKeyBindings::KeyPress, no Cocoa key down event", this)); |
michael@0 | 221 | |
michael@0 | 222 | return false; |
michael@0 | 223 | } |
michael@0 | 224 | |
michael@0 | 225 | PR_LOG(gNativeKeyBindingsLog, PR_LOG_ALWAYS, |
michael@0 | 226 | ("%p NativeKeyBindings::KeyPress, interpreting", this)); |
michael@0 | 227 | |
michael@0 | 228 | nsAutoTArray<KeyBindingsCommand, 2> bindingCommands; |
michael@0 | 229 | nsCocoaUtils::GetCommandsFromKeyEvent(cocoaEvent, bindingCommands); |
michael@0 | 230 | |
michael@0 | 231 | PR_LOG(gNativeKeyBindingsLog, PR_LOG_ALWAYS, |
michael@0 | 232 | ("%p NativeKeyBindings::KeyPress, bindingCommands=%u", |
michael@0 | 233 | this, bindingCommands.Length())); |
michael@0 | 234 | |
michael@0 | 235 | nsAutoTArray<Command, 4> geckoCommands; |
michael@0 | 236 | |
michael@0 | 237 | for (uint32_t i = 0; i < bindingCommands.Length(); i++) { |
michael@0 | 238 | SEL selector = bindingCommands[i].selector; |
michael@0 | 239 | |
michael@0 | 240 | #ifdef PR_LOGGING |
michael@0 | 241 | if (PR_LOG_TEST(gNativeKeyBindingsLog, PR_LOG_ALWAYS)) { |
michael@0 | 242 | NSString* selectorString = NSStringFromSelector(selector); |
michael@0 | 243 | nsAutoString nsSelectorString; |
michael@0 | 244 | nsCocoaUtils::GetStringForNSString(selectorString, nsSelectorString); |
michael@0 | 245 | |
michael@0 | 246 | PR_LOG(gNativeKeyBindingsLog, PR_LOG_ALWAYS, |
michael@0 | 247 | ("%p NativeKeyBindings::KeyPress, selector=%s", |
michael@0 | 248 | this, ToNewCString(nsSelectorString))); |
michael@0 | 249 | } |
michael@0 | 250 | #endif |
michael@0 | 251 | |
michael@0 | 252 | // Try to find a simple mapping in the hashtable |
michael@0 | 253 | Command geckoCommand = static_cast<Command>(mSelectorToCommand.Get( |
michael@0 | 254 | reinterpret_cast<struct objc_selector*>(selector))); |
michael@0 | 255 | |
michael@0 | 256 | if (geckoCommand) { |
michael@0 | 257 | geckoCommands.AppendElement(geckoCommand); |
michael@0 | 258 | } else if (selector == @selector(selectLine:)) { |
michael@0 | 259 | // This is functional, but Cocoa's version is direction-less in that |
michael@0 | 260 | // selection direction is not determined until some future directed action |
michael@0 | 261 | // is taken. See bug 282097, comment 79 for more details. |
michael@0 | 262 | geckoCommands.AppendElement(CommandBeginLine); |
michael@0 | 263 | geckoCommands.AppendElement(CommandSelectEndLine); |
michael@0 | 264 | } else if (selector == @selector(selectWord:)) { |
michael@0 | 265 | // This is functional, but Cocoa's version is direction-less in that |
michael@0 | 266 | // selection direction is not determined until some future directed action |
michael@0 | 267 | // is taken. See bug 282097, comment 79 for more details. |
michael@0 | 268 | geckoCommands.AppendElement(CommandWordPrevious); |
michael@0 | 269 | geckoCommands.AppendElement(CommandSelectWordNext); |
michael@0 | 270 | } |
michael@0 | 271 | } |
michael@0 | 272 | |
michael@0 | 273 | if (geckoCommands.IsEmpty()) { |
michael@0 | 274 | PR_LOG(gNativeKeyBindingsLog, PR_LOG_ALWAYS, |
michael@0 | 275 | ("%p NativeKeyBindings::KeyPress, handled=false", this)); |
michael@0 | 276 | |
michael@0 | 277 | return false; |
michael@0 | 278 | } |
michael@0 | 279 | |
michael@0 | 280 | for (uint32_t i = 0; i < geckoCommands.Length(); i++) { |
michael@0 | 281 | Command geckoCommand = geckoCommands[i]; |
michael@0 | 282 | |
michael@0 | 283 | PR_LOG(gNativeKeyBindingsLog, PR_LOG_ALWAYS, |
michael@0 | 284 | ("%p NativeKeyBindings::KeyPress, command=%s", |
michael@0 | 285 | this, geckoCommand)); |
michael@0 | 286 | |
michael@0 | 287 | // Execute the Gecko command |
michael@0 | 288 | aCallback(geckoCommand, aCallbackData); |
michael@0 | 289 | } |
michael@0 | 290 | |
michael@0 | 291 | PR_LOG(gNativeKeyBindingsLog, PR_LOG_ALWAYS, |
michael@0 | 292 | ("%p NativeKeyBindings::KeyPress, handled=true", this)); |
michael@0 | 293 | |
michael@0 | 294 | return true; |
michael@0 | 295 | } |
michael@0 | 296 | |
michael@0 | 297 | } // namespace widget |
michael@0 | 298 | } // namespace mozilla |