widget/cocoa/NativeKeyBindings.mm

Wed, 31 Dec 2014 06:55:50 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:55:50 +0100
changeset 2
7e26c7da4463
permissions
-rw-r--r--

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

mercurial