1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/widget/cocoa/NativeKeyBindings.mm Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,298 @@ 1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +#include "NativeKeyBindings.h" 1.10 + 1.11 +#include "nsTArray.h" 1.12 +#include "nsCocoaUtils.h" 1.13 +#include "prlog.h" 1.14 +#include "mozilla/TextEvents.h" 1.15 + 1.16 +namespace mozilla { 1.17 +namespace widget { 1.18 + 1.19 +#ifdef PR_LOGGING 1.20 +PRLogModuleInfo* gNativeKeyBindingsLog = nullptr; 1.21 +#endif 1.22 + 1.23 +NativeKeyBindings* NativeKeyBindings::sInstanceForSingleLineEditor = nullptr; 1.24 +NativeKeyBindings* NativeKeyBindings::sInstanceForMultiLineEditor = nullptr; 1.25 + 1.26 +// static 1.27 +NativeKeyBindings* 1.28 +NativeKeyBindings::GetInstance(NativeKeyBindingsType aType) 1.29 +{ 1.30 + switch (aType) { 1.31 + case nsIWidget::NativeKeyBindingsForSingleLineEditor: 1.32 + if (!sInstanceForSingleLineEditor) { 1.33 + sInstanceForSingleLineEditor = new NativeKeyBindings(); 1.34 + sInstanceForSingleLineEditor->Init(aType); 1.35 + } 1.36 + return sInstanceForSingleLineEditor; 1.37 + case nsIWidget::NativeKeyBindingsForMultiLineEditor: 1.38 + case nsIWidget::NativeKeyBindingsForRichTextEditor: 1.39 + if (!sInstanceForMultiLineEditor) { 1.40 + sInstanceForMultiLineEditor = new NativeKeyBindings(); 1.41 + sInstanceForMultiLineEditor->Init(aType); 1.42 + } 1.43 + return sInstanceForMultiLineEditor; 1.44 + default: 1.45 + MOZ_CRASH("Not implemented"); 1.46 + return nullptr; 1.47 + } 1.48 +} 1.49 + 1.50 +// static 1.51 +void 1.52 +NativeKeyBindings::Shutdown() 1.53 +{ 1.54 + delete sInstanceForSingleLineEditor; 1.55 + sInstanceForSingleLineEditor = nullptr; 1.56 + delete sInstanceForMultiLineEditor; 1.57 + sInstanceForMultiLineEditor = nullptr; 1.58 +} 1.59 + 1.60 +NativeKeyBindings::NativeKeyBindings() 1.61 +{ 1.62 +} 1.63 + 1.64 +#define SEL_TO_COMMAND(aSel, aCommand) \ 1.65 + mSelectorToCommand.Put( \ 1.66 + reinterpret_cast<struct objc_selector *>(@selector(aSel)), aCommand) 1.67 + 1.68 +void 1.69 +NativeKeyBindings::Init(NativeKeyBindingsType aType) 1.70 +{ 1.71 +#ifdef PR_LOGGING 1.72 + if (!gNativeKeyBindingsLog) { 1.73 + gNativeKeyBindingsLog = PR_NewLogModule("NativeKeyBindings"); 1.74 + } 1.75 +#endif 1.76 + 1.77 + PR_LOG(gNativeKeyBindingsLog, PR_LOG_ALWAYS, 1.78 + ("%p NativeKeyBindings::Init", this)); 1.79 + 1.80 + // Many selectors have a one-to-one mapping to a Gecko command. Those mappings 1.81 + // are registered in mSelectorToCommand. 1.82 + 1.83 + // Selectors from NSResponder's "Responding to Action Messages" section and 1.84 + // from NSText's "Action Methods for Editing" section 1.85 + 1.86 + // TODO: Improves correctness of left / right meaning 1.87 + // TODO: Add real paragraph motions 1.88 + 1.89 + // SEL_TO_COMMAND(cancelOperation:, ); 1.90 + // SEL_TO_COMMAND(capitalizeWord:, ); 1.91 + // SEL_TO_COMMAND(centerSelectionInVisibleArea:, ); 1.92 + // SEL_TO_COMMAND(changeCaseOfLetter:, ); 1.93 + // SEL_TO_COMMAND(complete:, ); 1.94 + SEL_TO_COMMAND(copy:, CommandCopy); 1.95 + // SEL_TO_COMMAND(copyFont:, ); 1.96 + // SEL_TO_COMMAND(copyRuler:, ); 1.97 + SEL_TO_COMMAND(cut:, CommandCut); 1.98 + SEL_TO_COMMAND(delete:, CommandDelete); 1.99 + SEL_TO_COMMAND(deleteBackward:, CommandDeleteCharBackward); 1.100 + // SEL_TO_COMMAND(deleteBackwardByDecomposingPreviousCharacter:, ); 1.101 + SEL_TO_COMMAND(deleteForward:, CommandDeleteCharForward); 1.102 + 1.103 + // TODO: deleteTo* selectors are also supposed to add text to a kill buffer 1.104 + SEL_TO_COMMAND(deleteToBeginningOfLine:, CommandDeleteToBeginningOfLine); 1.105 + SEL_TO_COMMAND(deleteToBeginningOfParagraph:, CommandDeleteToBeginningOfLine); 1.106 + SEL_TO_COMMAND(deleteToEndOfLine:, CommandDeleteToEndOfLine); 1.107 + SEL_TO_COMMAND(deleteToEndOfParagraph:, CommandDeleteToEndOfLine); 1.108 + // SEL_TO_COMMAND(deleteToMark:, ); 1.109 + 1.110 + SEL_TO_COMMAND(deleteWordBackward:, CommandDeleteWordBackward); 1.111 + SEL_TO_COMMAND(deleteWordForward:, CommandDeleteWordForward); 1.112 + // SEL_TO_COMMAND(indent:, ); 1.113 + // SEL_TO_COMMAND(insertBacktab:, ); 1.114 + // SEL_TO_COMMAND(insertContainerBreak:, ); 1.115 + // SEL_TO_COMMAND(insertLineBreak:, ); 1.116 + // SEL_TO_COMMAND(insertNewline:, ); 1.117 + // SEL_TO_COMMAND(insertNewlineIgnoringFieldEditor:, ); 1.118 + // SEL_TO_COMMAND(insertParagraphSeparator:, ); 1.119 + // SEL_TO_COMMAND(insertTab:, ); 1.120 + // SEL_TO_COMMAND(insertTabIgnoringFieldEditor:, ); 1.121 + // SEL_TO_COMMAND(insertDoubleQuoteIgnoringSubstitution:, ); 1.122 + // SEL_TO_COMMAND(insertSingleQuoteIgnoringSubstitution:, ); 1.123 + // SEL_TO_COMMAND(lowercaseWord:, ); 1.124 + SEL_TO_COMMAND(moveBackward:, CommandCharPrevious); 1.125 + SEL_TO_COMMAND(moveBackwardAndModifySelection:, CommandSelectCharPrevious); 1.126 + if (aType == nsIWidget::NativeKeyBindingsForSingleLineEditor) { 1.127 + SEL_TO_COMMAND(moveDown:, CommandEndLine); 1.128 + } else { 1.129 + SEL_TO_COMMAND(moveDown:, CommandLineNext); 1.130 + } 1.131 + SEL_TO_COMMAND(moveDownAndModifySelection:, CommandSelectLineNext); 1.132 + SEL_TO_COMMAND(moveForward:, CommandCharNext); 1.133 + SEL_TO_COMMAND(moveForwardAndModifySelection:, CommandSelectCharNext); 1.134 + SEL_TO_COMMAND(moveLeft:, CommandCharPrevious); 1.135 + SEL_TO_COMMAND(moveLeftAndModifySelection:, CommandSelectCharPrevious); 1.136 + SEL_TO_COMMAND(moveParagraphBackwardAndModifySelection:, 1.137 + CommandSelectBeginLine); 1.138 + SEL_TO_COMMAND(moveParagraphForwardAndModifySelection:, CommandSelectEndLine); 1.139 + SEL_TO_COMMAND(moveRight:, CommandCharNext); 1.140 + SEL_TO_COMMAND(moveRightAndModifySelection:, CommandSelectCharNext); 1.141 + SEL_TO_COMMAND(moveToBeginningOfDocument:, CommandMoveTop); 1.142 + SEL_TO_COMMAND(moveToBeginningOfDocumentAndModifySelection:, 1.143 + CommandSelectTop); 1.144 + SEL_TO_COMMAND(moveToBeginningOfLine:, CommandBeginLine); 1.145 + SEL_TO_COMMAND(moveToBeginningOfLineAndModifySelection:, 1.146 + CommandSelectBeginLine); 1.147 + SEL_TO_COMMAND(moveToBeginningOfParagraph:, CommandBeginLine); 1.148 + SEL_TO_COMMAND(moveToBeginningOfParagraphAndModifySelection:, 1.149 + CommandSelectBeginLine); 1.150 + SEL_TO_COMMAND(moveToEndOfDocument:, CommandMoveBottom); 1.151 + SEL_TO_COMMAND(moveToEndOfDocumentAndModifySelection:, CommandSelectBottom); 1.152 + SEL_TO_COMMAND(moveToEndOfLine:, CommandEndLine); 1.153 + SEL_TO_COMMAND(moveToEndOfLineAndModifySelection:, CommandSelectEndLine); 1.154 + SEL_TO_COMMAND(moveToEndOfParagraph:, CommandEndLine); 1.155 + SEL_TO_COMMAND(moveToEndOfParagraphAndModifySelection:, CommandSelectEndLine); 1.156 + SEL_TO_COMMAND(moveToLeftEndOfLine:, CommandBeginLine); 1.157 + SEL_TO_COMMAND(moveToLeftEndOfLineAndModifySelection:, 1.158 + CommandSelectBeginLine); 1.159 + SEL_TO_COMMAND(moveToRightEndOfLine:, CommandEndLine); 1.160 + SEL_TO_COMMAND(moveToRightEndOfLineAndModifySelection:, CommandSelectEndLine); 1.161 + if (aType == nsIWidget::NativeKeyBindingsForSingleLineEditor) { 1.162 + SEL_TO_COMMAND(moveUp:, CommandBeginLine); 1.163 + } else { 1.164 + SEL_TO_COMMAND(moveUp:, CommandLinePrevious); 1.165 + } 1.166 + SEL_TO_COMMAND(moveUpAndModifySelection:, CommandSelectLinePrevious); 1.167 + SEL_TO_COMMAND(moveWordBackward:, CommandWordPrevious); 1.168 + SEL_TO_COMMAND(moveWordBackwardAndModifySelection:, 1.169 + CommandSelectWordPrevious); 1.170 + SEL_TO_COMMAND(moveWordForward:, CommandWordNext); 1.171 + SEL_TO_COMMAND(moveWordForwardAndModifySelection:, CommandSelectWordNext); 1.172 + SEL_TO_COMMAND(moveWordLeft:, CommandWordPrevious); 1.173 + SEL_TO_COMMAND(moveWordLeftAndModifySelection:, CommandSelectWordPrevious); 1.174 + SEL_TO_COMMAND(moveWordRight:, CommandWordNext); 1.175 + SEL_TO_COMMAND(moveWordRightAndModifySelection:, CommandSelectWordNext); 1.176 + SEL_TO_COMMAND(pageDown:, CommandMovePageDown); 1.177 + SEL_TO_COMMAND(pageDownAndModifySelection:, CommandSelectPageDown); 1.178 + SEL_TO_COMMAND(pageUp:, CommandMovePageUp); 1.179 + SEL_TO_COMMAND(pageUpAndModifySelection:, CommandSelectPageUp); 1.180 + SEL_TO_COMMAND(paste:, CommandPaste); 1.181 + // SEL_TO_COMMAND(pasteFont:, ); 1.182 + // SEL_TO_COMMAND(pasteRuler:, ); 1.183 + SEL_TO_COMMAND(scrollLineDown:, CommandScrollLineDown); 1.184 + SEL_TO_COMMAND(scrollLineUp:, CommandScrollLineUp); 1.185 + SEL_TO_COMMAND(scrollPageDown:, CommandScrollPageDown); 1.186 + SEL_TO_COMMAND(scrollPageUp:, CommandScrollPageUp); 1.187 + SEL_TO_COMMAND(scrollToBeginningOfDocument:, CommandScrollTop); 1.188 + SEL_TO_COMMAND(scrollToEndOfDocument:, CommandScrollBottom); 1.189 + SEL_TO_COMMAND(selectAll:, CommandSelectAll); 1.190 + // selectLine: is complex, see KeyDown 1.191 + if (aType == nsIWidget::NativeKeyBindingsForSingleLineEditor) { 1.192 + SEL_TO_COMMAND(selectParagraph:, CommandSelectAll); 1.193 + } 1.194 + // SEL_TO_COMMAND(selectToMark:, ); 1.195 + // selectWord: is complex, see KeyDown 1.196 + // SEL_TO_COMMAND(setMark:, ); 1.197 + // SEL_TO_COMMAND(showContextHelp:, ); 1.198 + // SEL_TO_COMMAND(supplementalTargetForAction:sender:, ); 1.199 + // SEL_TO_COMMAND(swapWithMark:, ); 1.200 + // SEL_TO_COMMAND(transpose:, ); 1.201 + // SEL_TO_COMMAND(transposeWords:, ); 1.202 + // SEL_TO_COMMAND(uppercaseWord:, ); 1.203 + // SEL_TO_COMMAND(yank:, ); 1.204 +} 1.205 + 1.206 +#undef SEL_TO_COMMAND 1.207 + 1.208 +bool 1.209 +NativeKeyBindings::Execute(const WidgetKeyboardEvent& aEvent, 1.210 + DoCommandCallback aCallback, 1.211 + void* aCallbackData) 1.212 +{ 1.213 + PR_LOG(gNativeKeyBindingsLog, PR_LOG_ALWAYS, 1.214 + ("%p NativeKeyBindings::KeyPress", this)); 1.215 + 1.216 + // Recover the current event, which should always be the key down we are 1.217 + // responding to. 1.218 + 1.219 + NSEvent* cocoaEvent = reinterpret_cast<NSEvent*>(aEvent.mNativeKeyEvent); 1.220 + 1.221 + if (!cocoaEvent || [cocoaEvent type] != NSKeyDown) { 1.222 + PR_LOG(gNativeKeyBindingsLog, PR_LOG_ALWAYS, 1.223 + ("%p NativeKeyBindings::KeyPress, no Cocoa key down event", this)); 1.224 + 1.225 + return false; 1.226 + } 1.227 + 1.228 + PR_LOG(gNativeKeyBindingsLog, PR_LOG_ALWAYS, 1.229 + ("%p NativeKeyBindings::KeyPress, interpreting", this)); 1.230 + 1.231 + nsAutoTArray<KeyBindingsCommand, 2> bindingCommands; 1.232 + nsCocoaUtils::GetCommandsFromKeyEvent(cocoaEvent, bindingCommands); 1.233 + 1.234 + PR_LOG(gNativeKeyBindingsLog, PR_LOG_ALWAYS, 1.235 + ("%p NativeKeyBindings::KeyPress, bindingCommands=%u", 1.236 + this, bindingCommands.Length())); 1.237 + 1.238 + nsAutoTArray<Command, 4> geckoCommands; 1.239 + 1.240 + for (uint32_t i = 0; i < bindingCommands.Length(); i++) { 1.241 + SEL selector = bindingCommands[i].selector; 1.242 + 1.243 +#ifdef PR_LOGGING 1.244 + if (PR_LOG_TEST(gNativeKeyBindingsLog, PR_LOG_ALWAYS)) { 1.245 + NSString* selectorString = NSStringFromSelector(selector); 1.246 + nsAutoString nsSelectorString; 1.247 + nsCocoaUtils::GetStringForNSString(selectorString, nsSelectorString); 1.248 + 1.249 + PR_LOG(gNativeKeyBindingsLog, PR_LOG_ALWAYS, 1.250 + ("%p NativeKeyBindings::KeyPress, selector=%s", 1.251 + this, ToNewCString(nsSelectorString))); 1.252 + } 1.253 +#endif 1.254 + 1.255 + // Try to find a simple mapping in the hashtable 1.256 + Command geckoCommand = static_cast<Command>(mSelectorToCommand.Get( 1.257 + reinterpret_cast<struct objc_selector*>(selector))); 1.258 + 1.259 + if (geckoCommand) { 1.260 + geckoCommands.AppendElement(geckoCommand); 1.261 + } else if (selector == @selector(selectLine:)) { 1.262 + // This is functional, but Cocoa's version is direction-less in that 1.263 + // selection direction is not determined until some future directed action 1.264 + // is taken. See bug 282097, comment 79 for more details. 1.265 + geckoCommands.AppendElement(CommandBeginLine); 1.266 + geckoCommands.AppendElement(CommandSelectEndLine); 1.267 + } else if (selector == @selector(selectWord:)) { 1.268 + // This is functional, but Cocoa's version is direction-less in that 1.269 + // selection direction is not determined until some future directed action 1.270 + // is taken. See bug 282097, comment 79 for more details. 1.271 + geckoCommands.AppendElement(CommandWordPrevious); 1.272 + geckoCommands.AppendElement(CommandSelectWordNext); 1.273 + } 1.274 + } 1.275 + 1.276 + if (geckoCommands.IsEmpty()) { 1.277 + PR_LOG(gNativeKeyBindingsLog, PR_LOG_ALWAYS, 1.278 + ("%p NativeKeyBindings::KeyPress, handled=false", this)); 1.279 + 1.280 + return false; 1.281 + } 1.282 + 1.283 + for (uint32_t i = 0; i < geckoCommands.Length(); i++) { 1.284 + Command geckoCommand = geckoCommands[i]; 1.285 + 1.286 + PR_LOG(gNativeKeyBindingsLog, PR_LOG_ALWAYS, 1.287 + ("%p NativeKeyBindings::KeyPress, command=%s", 1.288 + this, geckoCommand)); 1.289 + 1.290 + // Execute the Gecko command 1.291 + aCallback(geckoCommand, aCallbackData); 1.292 + } 1.293 + 1.294 + PR_LOG(gNativeKeyBindingsLog, PR_LOG_ALWAYS, 1.295 + ("%p NativeKeyBindings::KeyPress, handled=true", this)); 1.296 + 1.297 + return true; 1.298 +} 1.299 + 1.300 +} // namespace widget 1.301 +} // namespace mozilla