widget/cocoa/NativeKeyBindings.mm

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

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

mercurial