|
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/. */ |
|
5 |
|
6 #include "NativeKeyBindings.h" |
|
7 |
|
8 #include "nsTArray.h" |
|
9 #include "nsCocoaUtils.h" |
|
10 #include "prlog.h" |
|
11 #include "mozilla/TextEvents.h" |
|
12 |
|
13 namespace mozilla { |
|
14 namespace widget { |
|
15 |
|
16 #ifdef PR_LOGGING |
|
17 PRLogModuleInfo* gNativeKeyBindingsLog = nullptr; |
|
18 #endif |
|
19 |
|
20 NativeKeyBindings* NativeKeyBindings::sInstanceForSingleLineEditor = nullptr; |
|
21 NativeKeyBindings* NativeKeyBindings::sInstanceForMultiLineEditor = nullptr; |
|
22 |
|
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 } |
|
46 |
|
47 // static |
|
48 void |
|
49 NativeKeyBindings::Shutdown() |
|
50 { |
|
51 delete sInstanceForSingleLineEditor; |
|
52 sInstanceForSingleLineEditor = nullptr; |
|
53 delete sInstanceForMultiLineEditor; |
|
54 sInstanceForMultiLineEditor = nullptr; |
|
55 } |
|
56 |
|
57 NativeKeyBindings::NativeKeyBindings() |
|
58 { |
|
59 } |
|
60 |
|
61 #define SEL_TO_COMMAND(aSel, aCommand) \ |
|
62 mSelectorToCommand.Put( \ |
|
63 reinterpret_cast<struct objc_selector *>(@selector(aSel)), aCommand) |
|
64 |
|
65 void |
|
66 NativeKeyBindings::Init(NativeKeyBindingsType aType) |
|
67 { |
|
68 #ifdef PR_LOGGING |
|
69 if (!gNativeKeyBindingsLog) { |
|
70 gNativeKeyBindingsLog = PR_NewLogModule("NativeKeyBindings"); |
|
71 } |
|
72 #endif |
|
73 |
|
74 PR_LOG(gNativeKeyBindingsLog, PR_LOG_ALWAYS, |
|
75 ("%p NativeKeyBindings::Init", this)); |
|
76 |
|
77 // Many selectors have a one-to-one mapping to a Gecko command. Those mappings |
|
78 // are registered in mSelectorToCommand. |
|
79 |
|
80 // Selectors from NSResponder's "Responding to Action Messages" section and |
|
81 // from NSText's "Action Methods for Editing" section |
|
82 |
|
83 // TODO: Improves correctness of left / right meaning |
|
84 // TODO: Add real paragraph motions |
|
85 |
|
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); |
|
99 |
|
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:, ); |
|
106 |
|
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 } |
|
202 |
|
203 #undef SEL_TO_COMMAND |
|
204 |
|
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)); |
|
212 |
|
213 // Recover the current event, which should always be the key down we are |
|
214 // responding to. |
|
215 |
|
216 NSEvent* cocoaEvent = reinterpret_cast<NSEvent*>(aEvent.mNativeKeyEvent); |
|
217 |
|
218 if (!cocoaEvent || [cocoaEvent type] != NSKeyDown) { |
|
219 PR_LOG(gNativeKeyBindingsLog, PR_LOG_ALWAYS, |
|
220 ("%p NativeKeyBindings::KeyPress, no Cocoa key down event", this)); |
|
221 |
|
222 return false; |
|
223 } |
|
224 |
|
225 PR_LOG(gNativeKeyBindingsLog, PR_LOG_ALWAYS, |
|
226 ("%p NativeKeyBindings::KeyPress, interpreting", this)); |
|
227 |
|
228 nsAutoTArray<KeyBindingsCommand, 2> bindingCommands; |
|
229 nsCocoaUtils::GetCommandsFromKeyEvent(cocoaEvent, bindingCommands); |
|
230 |
|
231 PR_LOG(gNativeKeyBindingsLog, PR_LOG_ALWAYS, |
|
232 ("%p NativeKeyBindings::KeyPress, bindingCommands=%u", |
|
233 this, bindingCommands.Length())); |
|
234 |
|
235 nsAutoTArray<Command, 4> geckoCommands; |
|
236 |
|
237 for (uint32_t i = 0; i < bindingCommands.Length(); i++) { |
|
238 SEL selector = bindingCommands[i].selector; |
|
239 |
|
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); |
|
245 |
|
246 PR_LOG(gNativeKeyBindingsLog, PR_LOG_ALWAYS, |
|
247 ("%p NativeKeyBindings::KeyPress, selector=%s", |
|
248 this, ToNewCString(nsSelectorString))); |
|
249 } |
|
250 #endif |
|
251 |
|
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))); |
|
255 |
|
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 } |
|
272 |
|
273 if (geckoCommands.IsEmpty()) { |
|
274 PR_LOG(gNativeKeyBindingsLog, PR_LOG_ALWAYS, |
|
275 ("%p NativeKeyBindings::KeyPress, handled=false", this)); |
|
276 |
|
277 return false; |
|
278 } |
|
279 |
|
280 for (uint32_t i = 0; i < geckoCommands.Length(); i++) { |
|
281 Command geckoCommand = geckoCommands[i]; |
|
282 |
|
283 PR_LOG(gNativeKeyBindingsLog, PR_LOG_ALWAYS, |
|
284 ("%p NativeKeyBindings::KeyPress, command=%s", |
|
285 this, geckoCommand)); |
|
286 |
|
287 // Execute the Gecko command |
|
288 aCallback(geckoCommand, aCallbackData); |
|
289 } |
|
290 |
|
291 PR_LOG(gNativeKeyBindingsLog, PR_LOG_ALWAYS, |
|
292 ("%p NativeKeyBindings::KeyPress, handled=true", this)); |
|
293 |
|
294 return true; |
|
295 } |
|
296 |
|
297 } // namespace widget |
|
298 } // namespace mozilla |