widget/cocoa/NativeKeyBindings.mm

changeset 0
6474c204b198
     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

mercurial