|
1 /* |
|
2 * Copyright (C) 2006, 2007 Apple Inc. All rights reserved. |
|
3 * Copyright (C) 2008 Christian Dywan <christian@imendio.com> |
|
4 * |
|
5 * Redistribution and use in source and binary forms, with or without |
|
6 * modification, are permitted provided that the following conditions |
|
7 * are met: |
|
8 * 1. Redistributions of source code must retain the above copyright |
|
9 * notice, this list of conditions and the following disclaimer. |
|
10 * 2. Redistributions in binary form must reproduce the above copyright |
|
11 * notice, this list of conditions and the following disclaimer in the |
|
12 * documentation and/or other materials provided with the distribution. |
|
13 * |
|
14 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY |
|
15 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
|
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
|
17 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR |
|
18 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
|
19 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
|
20 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
|
21 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY |
|
22 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
|
23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
|
24 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
|
25 */ |
|
26 |
|
27 #include "config.h" |
|
28 #include "ContextMenu.h" |
|
29 |
|
30 #if ENABLE(CONTEXT_MENUS) |
|
31 |
|
32 #include "ContextMenuController.h" |
|
33 #include "ContextMenuClient.h" |
|
34 #include "CSSComputedStyleDeclaration.h" |
|
35 #include "CSSProperty.h" |
|
36 #include "CSSPropertyNames.h" |
|
37 #include "Document.h" |
|
38 #include "DocumentLoader.h" |
|
39 #include "Editor.h" |
|
40 #include "Frame.h" |
|
41 #include "FrameLoader.h" |
|
42 #include "InspectorController.h" |
|
43 #include "KURL.h" |
|
44 #include "LocalizedStrings.h" |
|
45 #include "Node.h" |
|
46 #include "Page.h" |
|
47 #include "ResourceRequest.h" |
|
48 #include "SelectionController.h" |
|
49 #include "Settings.h" |
|
50 #include "TextIterator.h" |
|
51 #include <wtf/text/CString.h> |
|
52 |
|
53 using namespace std; |
|
54 using namespace WTF; |
|
55 using namespace Unicode; |
|
56 |
|
57 namespace WebCore { |
|
58 |
|
59 ContextMenuController* ContextMenu::controller() const |
|
60 { |
|
61 if (Node* node = m_hitTestResult.innerNonSharedNode()) |
|
62 if (Frame* frame = node->document()->frame()) |
|
63 if (Page* page = frame->page()) |
|
64 return page->contextMenuController(); |
|
65 return 0; |
|
66 } |
|
67 |
|
68 static PassOwnPtr<ContextMenuItem> separatorItem() |
|
69 { |
|
70 return new ContextMenuItem(SeparatorType, ContextMenuItemTagNoAction, String()); |
|
71 } |
|
72 |
|
73 static void createAndAppendFontSubMenu(const HitTestResult& result, ContextMenuItem& fontMenuItem) |
|
74 { |
|
75 ContextMenu fontMenu(result); |
|
76 |
|
77 #if PLATFORM(MAC) |
|
78 ContextMenuItem showFonts(ActionType, ContextMenuItemTagShowFonts, contextMenuItemTagShowFonts()); |
|
79 #endif |
|
80 ContextMenuItem bold(CheckableActionType, ContextMenuItemTagBold, contextMenuItemTagBold()); |
|
81 ContextMenuItem italic(CheckableActionType, ContextMenuItemTagItalic, contextMenuItemTagItalic()); |
|
82 ContextMenuItem underline(CheckableActionType, ContextMenuItemTagUnderline, contextMenuItemTagUnderline()); |
|
83 ContextMenuItem outline(ActionType, ContextMenuItemTagOutline, contextMenuItemTagOutline()); |
|
84 #if PLATFORM(MAC) |
|
85 ContextMenuItem styles(ActionType, ContextMenuItemTagStyles, contextMenuItemTagStyles()); |
|
86 ContextMenuItem showColors(ActionType, ContextMenuItemTagShowColors, contextMenuItemTagShowColors()); |
|
87 #endif |
|
88 |
|
89 #if PLATFORM(MAC) |
|
90 fontMenu.appendItem(showFonts); |
|
91 #endif |
|
92 fontMenu.appendItem(bold); |
|
93 fontMenu.appendItem(italic); |
|
94 fontMenu.appendItem(underline); |
|
95 fontMenu.appendItem(outline); |
|
96 #if PLATFORM(MAC) |
|
97 fontMenu.appendItem(styles); |
|
98 fontMenu.appendItem(*separatorItem()); |
|
99 fontMenu.appendItem(showColors); |
|
100 #endif |
|
101 |
|
102 fontMenuItem.setSubMenu(&fontMenu); |
|
103 } |
|
104 |
|
105 #if !defined(BUILDING_ON_TIGER) |
|
106 |
|
107 #if !PLATFORM(GTK) |
|
108 |
|
109 static void createAndAppendSpellingAndGrammarSubMenu(const HitTestResult& result, ContextMenuItem& spellingAndGrammarMenuItem) |
|
110 { |
|
111 ContextMenu spellingAndGrammarMenu(result); |
|
112 |
|
113 ContextMenuItem showSpellingPanel(ActionType, ContextMenuItemTagShowSpellingPanel, |
|
114 contextMenuItemTagShowSpellingPanel(true)); |
|
115 ContextMenuItem checkSpelling(ActionType, ContextMenuItemTagCheckSpelling, |
|
116 contextMenuItemTagCheckSpelling()); |
|
117 ContextMenuItem checkAsYouType(CheckableActionType, ContextMenuItemTagCheckSpellingWhileTyping, |
|
118 contextMenuItemTagCheckSpellingWhileTyping()); |
|
119 ContextMenuItem grammarWithSpelling(CheckableActionType, ContextMenuItemTagCheckGrammarWithSpelling, |
|
120 contextMenuItemTagCheckGrammarWithSpelling()); |
|
121 #if PLATFORM(MAC) && !defined(BUILDING_ON_LEOPARD) |
|
122 ContextMenuItem correctSpelling(CheckableActionType, ContextMenuItemTagCorrectSpellingAutomatically, |
|
123 contextMenuItemTagCorrectSpellingAutomatically()); |
|
124 #endif |
|
125 |
|
126 spellingAndGrammarMenu.appendItem(showSpellingPanel); |
|
127 spellingAndGrammarMenu.appendItem(checkSpelling); |
|
128 #if PLATFORM(MAC) && !defined(BUILDING_ON_LEOPARD) |
|
129 spellingAndGrammarMenu.appendItem(*separatorItem()); |
|
130 #endif |
|
131 spellingAndGrammarMenu.appendItem(checkAsYouType); |
|
132 spellingAndGrammarMenu.appendItem(grammarWithSpelling); |
|
133 #if PLATFORM(MAC) && !defined(BUILDING_ON_LEOPARD) |
|
134 spellingAndGrammarMenu.appendItem(correctSpelling); |
|
135 #endif |
|
136 |
|
137 spellingAndGrammarMenuItem.setSubMenu(&spellingAndGrammarMenu); |
|
138 } |
|
139 |
|
140 #endif // !PLATFORM(GTK) |
|
141 |
|
142 #else |
|
143 |
|
144 static void createAndAppendSpellingSubMenu(const HitTestResult& result, ContextMenuItem& spellingMenuItem) |
|
145 { |
|
146 ContextMenu spellingMenu(result); |
|
147 |
|
148 ContextMenuItem showSpellingPanel(ActionType, ContextMenuItemTagShowSpellingPanel, |
|
149 contextMenuItemTagShowSpellingPanel(true)); |
|
150 ContextMenuItem checkSpelling(ActionType, ContextMenuItemTagCheckSpelling, |
|
151 contextMenuItemTagCheckSpelling()); |
|
152 ContextMenuItem checkAsYouType(CheckableActionType, ContextMenuItemTagCheckSpellingWhileTyping, |
|
153 contextMenuItemTagCheckSpellingWhileTyping()); |
|
154 |
|
155 spellingMenu.appendItem(showSpellingPanel); |
|
156 spellingMenu.appendItem(checkSpelling); |
|
157 spellingMenu.appendItem(checkAsYouType); |
|
158 |
|
159 spellingMenuItem.setSubMenu(&spellingMenu); |
|
160 } |
|
161 |
|
162 #endif |
|
163 |
|
164 #if PLATFORM(MAC) |
|
165 |
|
166 static void createAndAppendSpeechSubMenu(const HitTestResult& result, ContextMenuItem& speechMenuItem) |
|
167 { |
|
168 ContextMenu speechMenu(result); |
|
169 |
|
170 ContextMenuItem start(ActionType, ContextMenuItemTagStartSpeaking, contextMenuItemTagStartSpeaking()); |
|
171 ContextMenuItem stop(ActionType, ContextMenuItemTagStopSpeaking, contextMenuItemTagStopSpeaking()); |
|
172 |
|
173 speechMenu.appendItem(start); |
|
174 speechMenu.appendItem(stop); |
|
175 |
|
176 speechMenuItem.setSubMenu(&speechMenu); |
|
177 } |
|
178 |
|
179 #endif |
|
180 |
|
181 #if !PLATFORM(GTK) |
|
182 |
|
183 static void createAndAppendWritingDirectionSubMenu(const HitTestResult& result, ContextMenuItem& writingDirectionMenuItem) |
|
184 { |
|
185 ContextMenu writingDirectionMenu(result); |
|
186 |
|
187 ContextMenuItem defaultItem(ActionType, ContextMenuItemTagDefaultDirection, |
|
188 contextMenuItemTagDefaultDirection()); |
|
189 ContextMenuItem ltr(CheckableActionType, ContextMenuItemTagLeftToRight, contextMenuItemTagLeftToRight()); |
|
190 ContextMenuItem rtl(CheckableActionType, ContextMenuItemTagRightToLeft, contextMenuItemTagRightToLeft()); |
|
191 |
|
192 writingDirectionMenu.appendItem(defaultItem); |
|
193 writingDirectionMenu.appendItem(ltr); |
|
194 writingDirectionMenu.appendItem(rtl); |
|
195 |
|
196 writingDirectionMenuItem.setSubMenu(&writingDirectionMenu); |
|
197 } |
|
198 |
|
199 static void createAndAppendTextDirectionSubMenu(const HitTestResult& result, ContextMenuItem& textDirectionMenuItem) |
|
200 { |
|
201 ContextMenu textDirectionMenu(result); |
|
202 |
|
203 ContextMenuItem defaultItem(ActionType, ContextMenuItemTagTextDirectionDefault, contextMenuItemTagDefaultDirection()); |
|
204 ContextMenuItem ltr(CheckableActionType, ContextMenuItemTagTextDirectionLeftToRight, contextMenuItemTagLeftToRight()); |
|
205 ContextMenuItem rtl(CheckableActionType, ContextMenuItemTagTextDirectionRightToLeft, contextMenuItemTagRightToLeft()); |
|
206 |
|
207 textDirectionMenu.appendItem(defaultItem); |
|
208 textDirectionMenu.appendItem(ltr); |
|
209 textDirectionMenu.appendItem(rtl); |
|
210 |
|
211 textDirectionMenuItem.setSubMenu(&textDirectionMenu); |
|
212 } |
|
213 |
|
214 #endif |
|
215 |
|
216 #if PLATFORM(MAC) && !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD) |
|
217 |
|
218 static void createAndAppendSubstitutionsSubMenu(const HitTestResult& result, ContextMenuItem& substitutionsMenuItem) |
|
219 { |
|
220 ContextMenu substitutionsMenu(result); |
|
221 |
|
222 ContextMenuItem showSubstitutions(ActionType, ContextMenuItemTagShowSubstitutions, contextMenuItemTagShowSubstitutions(true)); |
|
223 ContextMenuItem smartCopyPaste(CheckableActionType, ContextMenuItemTagSmartCopyPaste, contextMenuItemTagSmartCopyPaste()); |
|
224 ContextMenuItem smartQuotes(CheckableActionType, ContextMenuItemTagSmartQuotes, contextMenuItemTagSmartQuotes()); |
|
225 ContextMenuItem smartDashes(CheckableActionType, ContextMenuItemTagSmartDashes, contextMenuItemTagSmartDashes()); |
|
226 ContextMenuItem smartLinks(CheckableActionType, ContextMenuItemTagSmartLinks, contextMenuItemTagSmartLinks()); |
|
227 ContextMenuItem textReplacement(CheckableActionType, ContextMenuItemTagTextReplacement, contextMenuItemTagTextReplacement()); |
|
228 |
|
229 substitutionsMenu.appendItem(showSubstitutions); |
|
230 substitutionsMenu.appendItem(*separatorItem()); |
|
231 substitutionsMenu.appendItem(smartCopyPaste); |
|
232 substitutionsMenu.appendItem(smartQuotes); |
|
233 substitutionsMenu.appendItem(smartDashes); |
|
234 substitutionsMenu.appendItem(smartLinks); |
|
235 substitutionsMenu.appendItem(textReplacement); |
|
236 |
|
237 substitutionsMenuItem.setSubMenu(&substitutionsMenu); |
|
238 } |
|
239 |
|
240 static void createAndAppendTransformationsSubMenu(const HitTestResult& result, ContextMenuItem& transformationsMenuItem) |
|
241 { |
|
242 ContextMenu transformationsMenu(result); |
|
243 |
|
244 ContextMenuItem makeUpperCase(ActionType, ContextMenuItemTagMakeUpperCase, contextMenuItemTagMakeUpperCase()); |
|
245 ContextMenuItem makeLowerCase(ActionType, ContextMenuItemTagMakeLowerCase, contextMenuItemTagMakeLowerCase()); |
|
246 ContextMenuItem capitalize(ActionType, ContextMenuItemTagCapitalize, contextMenuItemTagCapitalize()); |
|
247 |
|
248 transformationsMenu.appendItem(makeUpperCase); |
|
249 transformationsMenu.appendItem(makeLowerCase); |
|
250 transformationsMenu.appendItem(capitalize); |
|
251 |
|
252 transformationsMenuItem.setSubMenu(&transformationsMenu); |
|
253 } |
|
254 |
|
255 #endif |
|
256 |
|
257 static bool selectionContainsPossibleWord(Frame* frame) |
|
258 { |
|
259 // Current algorithm: look for a character that's not just a separator. |
|
260 for (TextIterator it(frame->selection()->toNormalizedRange().get()); !it.atEnd(); it.advance()) { |
|
261 int length = it.length(); |
|
262 const UChar* characters = it.characters(); |
|
263 for (int i = 0; i < length; ++i) |
|
264 if (!(category(characters[i]) & (Separator_Space | Separator_Line | Separator_Paragraph))) |
|
265 return true; |
|
266 } |
|
267 return false; |
|
268 } |
|
269 |
|
270 void ContextMenu::populate() |
|
271 { |
|
272 ContextMenuItem OpenLinkItem(ActionType, ContextMenuItemTagOpenLink, contextMenuItemTagOpenLink()); |
|
273 ContextMenuItem OpenLinkInNewWindowItem(ActionType, ContextMenuItemTagOpenLinkInNewWindow, |
|
274 contextMenuItemTagOpenLinkInNewWindow()); |
|
275 ContextMenuItem DownloadFileItem(ActionType, ContextMenuItemTagDownloadLinkToDisk, |
|
276 contextMenuItemTagDownloadLinkToDisk()); |
|
277 ContextMenuItem CopyLinkItem(ActionType, ContextMenuItemTagCopyLinkToClipboard, |
|
278 contextMenuItemTagCopyLinkToClipboard()); |
|
279 ContextMenuItem OpenImageInNewWindowItem(ActionType, ContextMenuItemTagOpenImageInNewWindow, |
|
280 contextMenuItemTagOpenImageInNewWindow()); |
|
281 ContextMenuItem DownloadImageItem(ActionType, ContextMenuItemTagDownloadImageToDisk, |
|
282 contextMenuItemTagDownloadImageToDisk()); |
|
283 ContextMenuItem CopyImageItem(ActionType, ContextMenuItemTagCopyImageToClipboard, |
|
284 contextMenuItemTagCopyImageToClipboard()); |
|
285 #if PLATFORM(MAC) |
|
286 ContextMenuItem SearchSpotlightItem(ActionType, ContextMenuItemTagSearchInSpotlight, |
|
287 contextMenuItemTagSearchInSpotlight()); |
|
288 ContextMenuItem LookInDictionaryItem(ActionType, ContextMenuItemTagLookUpInDictionary, |
|
289 contextMenuItemTagLookUpInDictionary()); |
|
290 #endif |
|
291 #if !PLATFORM(GTK) |
|
292 ContextMenuItem SearchWebItem(ActionType, ContextMenuItemTagSearchWeb, contextMenuItemTagSearchWeb()); |
|
293 #endif |
|
294 ContextMenuItem CopyItem(ActionType, ContextMenuItemTagCopy, contextMenuItemTagCopy()); |
|
295 ContextMenuItem BackItem(ActionType, ContextMenuItemTagGoBack, contextMenuItemTagGoBack()); |
|
296 ContextMenuItem ForwardItem(ActionType, ContextMenuItemTagGoForward, contextMenuItemTagGoForward()); |
|
297 ContextMenuItem StopItem(ActionType, ContextMenuItemTagStop, contextMenuItemTagStop()); |
|
298 ContextMenuItem ReloadItem(ActionType, ContextMenuItemTagReload, contextMenuItemTagReload()); |
|
299 ContextMenuItem OpenFrameItem(ActionType, ContextMenuItemTagOpenFrameInNewWindow, |
|
300 contextMenuItemTagOpenFrameInNewWindow()); |
|
301 ContextMenuItem NoGuessesItem(ActionType, ContextMenuItemTagNoGuessesFound, |
|
302 contextMenuItemTagNoGuessesFound()); |
|
303 ContextMenuItem IgnoreSpellingItem(ActionType, ContextMenuItemTagIgnoreSpelling, |
|
304 contextMenuItemTagIgnoreSpelling()); |
|
305 ContextMenuItem LearnSpellingItem(ActionType, ContextMenuItemTagLearnSpelling, |
|
306 contextMenuItemTagLearnSpelling()); |
|
307 ContextMenuItem IgnoreGrammarItem(ActionType, ContextMenuItemTagIgnoreGrammar, |
|
308 contextMenuItemTagIgnoreGrammar()); |
|
309 ContextMenuItem CutItem(ActionType, ContextMenuItemTagCut, contextMenuItemTagCut()); |
|
310 ContextMenuItem PasteItem(ActionType, ContextMenuItemTagPaste, contextMenuItemTagPaste()); |
|
311 #if PLATFORM(GTK) |
|
312 ContextMenuItem DeleteItem(ActionType, ContextMenuItemTagDelete, contextMenuItemTagDelete()); |
|
313 ContextMenuItem SelectAllItem(ActionType, ContextMenuItemTagSelectAll, contextMenuItemTagSelectAll()); |
|
314 #endif |
|
315 |
|
316 HitTestResult result = hitTestResult(); |
|
317 |
|
318 Node* node = m_hitTestResult.innerNonSharedNode(); |
|
319 if (!node) |
|
320 return; |
|
321 #if PLATFORM(GTK) |
|
322 if (!result.isContentEditable() && (node->isElementNode() && static_cast<Element*>(node)->isFormControlElement())) |
|
323 return; |
|
324 #endif |
|
325 Frame* frame = node->document()->frame(); |
|
326 if (!frame) |
|
327 return; |
|
328 |
|
329 if (!result.isContentEditable()) { |
|
330 FrameLoader* loader = frame->loader(); |
|
331 KURL linkURL = result.absoluteLinkURL(); |
|
332 if (!linkURL.isEmpty()) { |
|
333 if (loader->canHandleRequest(ResourceRequest(linkURL))) { |
|
334 appendItem(OpenLinkItem); |
|
335 appendItem(OpenLinkInNewWindowItem); |
|
336 appendItem(DownloadFileItem); |
|
337 } |
|
338 appendItem(CopyLinkItem); |
|
339 } |
|
340 |
|
341 KURL imageURL = result.absoluteImageURL(); |
|
342 if (!imageURL.isEmpty()) { |
|
343 if (!linkURL.isEmpty()) |
|
344 appendItem(*separatorItem()); |
|
345 |
|
346 appendItem(OpenImageInNewWindowItem); |
|
347 appendItem(DownloadImageItem); |
|
348 if (imageURL.isLocalFile() || m_hitTestResult.image()) |
|
349 appendItem(CopyImageItem); |
|
350 } |
|
351 |
|
352 if (imageURL.isEmpty() && linkURL.isEmpty()) { |
|
353 if (result.isSelected()) { |
|
354 if (selectionContainsPossibleWord(frame)) { |
|
355 #if PLATFORM(MAC) |
|
356 appendItem(SearchSpotlightItem); |
|
357 #endif |
|
358 #if !PLATFORM(GTK) |
|
359 appendItem(SearchWebItem); |
|
360 appendItem(*separatorItem()); |
|
361 #endif |
|
362 #if PLATFORM(MAC) |
|
363 appendItem(LookInDictionaryItem); |
|
364 appendItem(*separatorItem()); |
|
365 #endif |
|
366 } |
|
367 appendItem(CopyItem); |
|
368 #if PLATFORM(MAC) |
|
369 appendItem(*separatorItem()); |
|
370 ContextMenuItem SpeechMenuItem(SubmenuType, ContextMenuItemTagSpeechMenu, contextMenuItemTagSpeechMenu()); |
|
371 createAndAppendSpeechSubMenu(m_hitTestResult, SpeechMenuItem); |
|
372 appendItem(SpeechMenuItem); |
|
373 #endif |
|
374 } else { |
|
375 #if ENABLE(INSPECTOR) |
|
376 if (!(frame->page() && frame->page()->inspectorController()->hasInspectorFrontendClient())) { |
|
377 #endif |
|
378 #if PLATFORM(GTK) |
|
379 appendItem(BackItem); |
|
380 appendItem(ForwardItem); |
|
381 appendItem(StopItem); |
|
382 appendItem(ReloadItem); |
|
383 #else |
|
384 if (frame->page() && frame->page()->canGoBackOrForward(-1)) |
|
385 appendItem(BackItem); |
|
386 |
|
387 if (frame->page() && frame->page()->canGoBackOrForward(1)) |
|
388 appendItem(ForwardItem); |
|
389 |
|
390 // use isLoadingInAPISense rather than isLoading because Stop/Reload are |
|
391 // intended to match WebKit's API, not WebCore's internal notion of loading status |
|
392 if (loader->documentLoader()->isLoadingInAPISense()) |
|
393 appendItem(StopItem); |
|
394 else |
|
395 appendItem(ReloadItem); |
|
396 #endif |
|
397 #if ENABLE(INSPECTOR) |
|
398 } |
|
399 #endif |
|
400 |
|
401 if (frame->page() && frame != frame->page()->mainFrame()) |
|
402 appendItem(OpenFrameItem); |
|
403 } |
|
404 } |
|
405 } else { // Make an editing context menu |
|
406 SelectionController* selection = frame->selection(); |
|
407 bool inPasswordField = selection->isInPasswordField(); |
|
408 bool spellCheckingEnabled = frame->editor()->spellCheckingEnabledInFocusedNode(); |
|
409 |
|
410 if (!inPasswordField && spellCheckingEnabled) { |
|
411 // Consider adding spelling-related or grammar-related context menu items (never both, since a single selected range |
|
412 // is never considered a misspelling and bad grammar at the same time) |
|
413 bool misspelling; |
|
414 bool badGrammar; |
|
415 Vector<String> guesses = frame->editor()->guessesForMisspelledOrUngrammaticalSelection(misspelling, badGrammar); |
|
416 if (misspelling || badGrammar) { |
|
417 size_t size = guesses.size(); |
|
418 if (size == 0) { |
|
419 // If there's bad grammar but no suggestions (e.g., repeated word), just leave off the suggestions |
|
420 // list and trailing separator rather than adding a "No Guesses Found" item (matches AppKit) |
|
421 if (misspelling) { |
|
422 appendItem(NoGuessesItem); |
|
423 appendItem(*separatorItem()); |
|
424 } |
|
425 } else { |
|
426 for (unsigned i = 0; i < size; i++) { |
|
427 const String &guess = guesses[i]; |
|
428 if (!guess.isEmpty()) { |
|
429 ContextMenuItem item(ActionType, ContextMenuItemTagSpellingGuess, guess); |
|
430 appendItem(item); |
|
431 } |
|
432 } |
|
433 appendItem(*separatorItem()); |
|
434 } |
|
435 |
|
436 if (misspelling) { |
|
437 appendItem(IgnoreSpellingItem); |
|
438 appendItem(LearnSpellingItem); |
|
439 } else |
|
440 appendItem(IgnoreGrammarItem); |
|
441 appendItem(*separatorItem()); |
|
442 #if PLATFORM(MAC) && !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD) |
|
443 } else { |
|
444 // If the string was autocorrected, generate a contextual menu item allowing it to be changed back. |
|
445 String replacedString = result.replacedString(); |
|
446 if (!replacedString.isEmpty()) { |
|
447 ContextMenuItem item(ActionType, ContextMenuItemTagChangeBack, contextMenuItemTagChangeBack(replacedString)); |
|
448 appendItem(item); |
|
449 appendItem(*separatorItem()); |
|
450 } |
|
451 #endif |
|
452 } |
|
453 } |
|
454 |
|
455 FrameLoader* loader = frame->loader(); |
|
456 KURL linkURL = result.absoluteLinkURL(); |
|
457 if (!linkURL.isEmpty()) { |
|
458 if (loader->canHandleRequest(ResourceRequest(linkURL))) { |
|
459 appendItem(OpenLinkItem); |
|
460 appendItem(OpenLinkInNewWindowItem); |
|
461 appendItem(DownloadFileItem); |
|
462 } |
|
463 appendItem(CopyLinkItem); |
|
464 appendItem(*separatorItem()); |
|
465 } |
|
466 |
|
467 if (result.isSelected() && !inPasswordField && selectionContainsPossibleWord(frame)) { |
|
468 #if PLATFORM(MAC) |
|
469 appendItem(SearchSpotlightItem); |
|
470 #endif |
|
471 #if !PLATFORM(GTK) |
|
472 appendItem(SearchWebItem); |
|
473 appendItem(*separatorItem()); |
|
474 #endif |
|
475 |
|
476 #if PLATFORM(MAC) |
|
477 appendItem(LookInDictionaryItem); |
|
478 appendItem(*separatorItem()); |
|
479 #endif |
|
480 } |
|
481 |
|
482 appendItem(CutItem); |
|
483 appendItem(CopyItem); |
|
484 appendItem(PasteItem); |
|
485 #if PLATFORM(GTK) |
|
486 appendItem(DeleteItem); |
|
487 appendItem(*separatorItem()); |
|
488 appendItem(SelectAllItem); |
|
489 #endif |
|
490 |
|
491 if (!inPasswordField) { |
|
492 appendItem(*separatorItem()); |
|
493 #ifndef BUILDING_ON_TIGER |
|
494 #if !PLATFORM(GTK) |
|
495 ContextMenuItem SpellingAndGrammarMenuItem(SubmenuType, ContextMenuItemTagSpellingMenu, |
|
496 contextMenuItemTagSpellingMenu()); |
|
497 createAndAppendSpellingAndGrammarSubMenu(m_hitTestResult, SpellingAndGrammarMenuItem); |
|
498 appendItem(SpellingAndGrammarMenuItem); |
|
499 #endif |
|
500 #else |
|
501 ContextMenuItem SpellingMenuItem(SubmenuType, ContextMenuItemTagSpellingMenu, |
|
502 contextMenuItemTagSpellingMenu()); |
|
503 createAndAppendSpellingSubMenu(m_hitTestResult, SpellingMenuItem); |
|
504 appendItem(SpellingMenuItem); |
|
505 #endif |
|
506 #if PLATFORM(MAC) && !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD) |
|
507 ContextMenuItem substitutionsMenuItem(SubmenuType, ContextMenuItemTagSubstitutionsMenu, |
|
508 contextMenuItemTagSubstitutionsMenu()); |
|
509 createAndAppendSubstitutionsSubMenu(m_hitTestResult, substitutionsMenuItem); |
|
510 appendItem(substitutionsMenuItem); |
|
511 ContextMenuItem transformationsMenuItem(SubmenuType, ContextMenuItemTagTransformationsMenu, |
|
512 contextMenuItemTagTransformationsMenu()); |
|
513 createAndAppendTransformationsSubMenu(m_hitTestResult, transformationsMenuItem); |
|
514 appendItem(transformationsMenuItem); |
|
515 #endif |
|
516 #if PLATFORM(GTK) |
|
517 bool shouldShowFontMenu = frame->editor()->canEditRichly(); |
|
518 #else |
|
519 bool shouldShowFontMenu = true; |
|
520 #endif |
|
521 if (shouldShowFontMenu) { |
|
522 ContextMenuItem FontMenuItem(SubmenuType, ContextMenuItemTagFontMenu, |
|
523 contextMenuItemTagFontMenu()); |
|
524 createAndAppendFontSubMenu(m_hitTestResult, FontMenuItem); |
|
525 appendItem(FontMenuItem); |
|
526 } |
|
527 #if PLATFORM(MAC) |
|
528 ContextMenuItem SpeechMenuItem(SubmenuType, ContextMenuItemTagSpeechMenu, contextMenuItemTagSpeechMenu()); |
|
529 createAndAppendSpeechSubMenu(m_hitTestResult, SpeechMenuItem); |
|
530 appendItem(SpeechMenuItem); |
|
531 #endif |
|
532 #if !PLATFORM(GTK) |
|
533 ContextMenuItem WritingDirectionMenuItem(SubmenuType, ContextMenuItemTagWritingDirectionMenu, |
|
534 contextMenuItemTagWritingDirectionMenu()); |
|
535 createAndAppendWritingDirectionSubMenu(m_hitTestResult, WritingDirectionMenuItem); |
|
536 appendItem(WritingDirectionMenuItem); |
|
537 if (Page* page = frame->page()) { |
|
538 if (Settings* settings = page->settings()) { |
|
539 bool includeTextDirectionSubmenu = settings->textDirectionSubmenuInclusionBehavior() == TextDirectionSubmenuAlwaysIncluded |
|
540 || (settings->textDirectionSubmenuInclusionBehavior() == TextDirectionSubmenuAutomaticallyIncluded && frame->editor()->hasBidiSelection()); |
|
541 if (includeTextDirectionSubmenu) { |
|
542 ContextMenuItem TextDirectionMenuItem(SubmenuType, ContextMenuItemTagTextDirectionMenu, |
|
543 contextMenuItemTagTextDirectionMenu()); |
|
544 createAndAppendTextDirectionSubMenu(m_hitTestResult, TextDirectionMenuItem); |
|
545 appendItem(TextDirectionMenuItem); |
|
546 } |
|
547 } |
|
548 } |
|
549 #endif |
|
550 } |
|
551 } |
|
552 } |
|
553 |
|
554 #if ENABLE(INSPECTOR) |
|
555 void ContextMenu::addInspectElementItem() |
|
556 { |
|
557 Node* node = m_hitTestResult.innerNonSharedNode(); |
|
558 if (!node) |
|
559 return; |
|
560 |
|
561 Frame* frame = node->document()->frame(); |
|
562 if (!frame) |
|
563 return; |
|
564 |
|
565 Page* page = frame->page(); |
|
566 if (!page) |
|
567 return; |
|
568 |
|
569 if (!page->inspectorController()) |
|
570 return; |
|
571 |
|
572 ContextMenuItem InspectElementItem(ActionType, ContextMenuItemTagInspectElement, contextMenuItemTagInspectElement()); |
|
573 appendItem(*separatorItem()); |
|
574 appendItem(InspectElementItem); |
|
575 } |
|
576 #endif // ENABLE(INSPECTOR) |
|
577 |
|
578 void ContextMenu::checkOrEnableIfNeeded(ContextMenuItem& item) const |
|
579 { |
|
580 if (item.type() == SeparatorType) |
|
581 return; |
|
582 |
|
583 Frame* frame = m_hitTestResult.innerNonSharedNode()->document()->frame(); |
|
584 if (!frame) |
|
585 return; |
|
586 |
|
587 bool shouldEnable = true; |
|
588 bool shouldCheck = false; |
|
589 |
|
590 switch (item.action()) { |
|
591 case ContextMenuItemTagCheckSpelling: |
|
592 shouldEnable = frame->editor()->canEdit(); |
|
593 break; |
|
594 case ContextMenuItemTagDefaultDirection: |
|
595 shouldCheck = false; |
|
596 shouldEnable = false; |
|
597 break; |
|
598 case ContextMenuItemTagLeftToRight: |
|
599 case ContextMenuItemTagRightToLeft: { |
|
600 ExceptionCode ec = 0; |
|
601 RefPtr<CSSStyleDeclaration> style = frame->document()->createCSSStyleDeclaration(); |
|
602 String direction = item.action() == ContextMenuItemTagLeftToRight ? "ltr" : "rtl"; |
|
603 style->setProperty(CSSPropertyDirection, direction, false, ec); |
|
604 shouldCheck = frame->editor()->selectionHasStyle(style.get()) != FalseTriState; |
|
605 shouldEnable = true; |
|
606 break; |
|
607 } |
|
608 case ContextMenuItemTagTextDirectionDefault: { |
|
609 Editor::Command command = frame->editor()->command("MakeTextWritingDirectionNatural"); |
|
610 shouldCheck = command.state() == TrueTriState; |
|
611 shouldEnable = command.isEnabled(); |
|
612 break; |
|
613 } |
|
614 case ContextMenuItemTagTextDirectionLeftToRight: { |
|
615 Editor::Command command = frame->editor()->command("MakeTextWritingDirectionLeftToRight"); |
|
616 shouldCheck = command.state() == TrueTriState; |
|
617 shouldEnable = command.isEnabled(); |
|
618 break; |
|
619 } |
|
620 case ContextMenuItemTagTextDirectionRightToLeft: { |
|
621 Editor::Command command = frame->editor()->command("MakeTextWritingDirectionRightToLeft"); |
|
622 shouldCheck = command.state() == TrueTriState; |
|
623 shouldEnable = command.isEnabled(); |
|
624 break; |
|
625 } |
|
626 case ContextMenuItemTagCopy: |
|
627 shouldEnable = frame->editor()->canDHTMLCopy() || frame->editor()->canCopy(); |
|
628 break; |
|
629 case ContextMenuItemTagCut: |
|
630 shouldEnable = frame->editor()->canDHTMLCut() || frame->editor()->canCut(); |
|
631 break; |
|
632 case ContextMenuItemTagIgnoreSpelling: |
|
633 case ContextMenuItemTagLearnSpelling: |
|
634 shouldEnable = frame->selection()->isRange(); |
|
635 break; |
|
636 case ContextMenuItemTagPaste: |
|
637 shouldEnable = frame->editor()->canDHTMLPaste() || frame->editor()->canPaste(); |
|
638 break; |
|
639 #if PLATFORM(GTK) |
|
640 case ContextMenuItemTagDelete: |
|
641 shouldEnable = frame->editor()->canDelete(); |
|
642 break; |
|
643 case ContextMenuItemTagSelectAll: |
|
644 case ContextMenuItemTagInputMethods: |
|
645 case ContextMenuItemTagUnicode: |
|
646 shouldEnable = true; |
|
647 break; |
|
648 #endif |
|
649 case ContextMenuItemTagUnderline: { |
|
650 ExceptionCode ec = 0; |
|
651 RefPtr<CSSStyleDeclaration> style = frame->document()->createCSSStyleDeclaration(); |
|
652 style->setProperty(CSSPropertyWebkitTextDecorationsInEffect, "underline", false, ec); |
|
653 shouldCheck = frame->editor()->selectionHasStyle(style.get()) != FalseTriState; |
|
654 shouldEnable = frame->editor()->canEditRichly(); |
|
655 break; |
|
656 } |
|
657 case ContextMenuItemTagLookUpInDictionary: |
|
658 shouldEnable = frame->selection()->isRange(); |
|
659 break; |
|
660 case ContextMenuItemTagCheckGrammarWithSpelling: |
|
661 #ifndef BUILDING_ON_TIGER |
|
662 if (frame->editor()->isGrammarCheckingEnabled()) |
|
663 shouldCheck = true; |
|
664 shouldEnable = true; |
|
665 #endif |
|
666 break; |
|
667 case ContextMenuItemTagItalic: { |
|
668 ExceptionCode ec = 0; |
|
669 RefPtr<CSSStyleDeclaration> style = frame->document()->createCSSStyleDeclaration(); |
|
670 style->setProperty(CSSPropertyFontStyle, "italic", false, ec); |
|
671 shouldCheck = frame->editor()->selectionHasStyle(style.get()) != FalseTriState; |
|
672 shouldEnable = frame->editor()->canEditRichly(); |
|
673 break; |
|
674 } |
|
675 case ContextMenuItemTagBold: { |
|
676 ExceptionCode ec = 0; |
|
677 RefPtr<CSSStyleDeclaration> style = frame->document()->createCSSStyleDeclaration(); |
|
678 style->setProperty(CSSPropertyFontWeight, "bold", false, ec); |
|
679 shouldCheck = frame->editor()->selectionHasStyle(style.get()) != FalseTriState; |
|
680 shouldEnable = frame->editor()->canEditRichly(); |
|
681 break; |
|
682 } |
|
683 case ContextMenuItemTagOutline: |
|
684 shouldEnable = false; |
|
685 break; |
|
686 case ContextMenuItemTagShowSpellingPanel: |
|
687 #ifndef BUILDING_ON_TIGER |
|
688 if (frame->editor()->spellingPanelIsShowing()) |
|
689 item.setTitle(contextMenuItemTagShowSpellingPanel(false)); |
|
690 else |
|
691 item.setTitle(contextMenuItemTagShowSpellingPanel(true)); |
|
692 #endif |
|
693 shouldEnable = frame->editor()->canEdit(); |
|
694 break; |
|
695 case ContextMenuItemTagNoGuessesFound: |
|
696 shouldEnable = false; |
|
697 break; |
|
698 case ContextMenuItemTagCheckSpellingWhileTyping: |
|
699 shouldCheck = frame->editor()->isContinuousSpellCheckingEnabled(); |
|
700 break; |
|
701 #if PLATFORM(MAC) |
|
702 case ContextMenuItemTagSubstitutionsMenu: |
|
703 case ContextMenuItemTagTransformationsMenu: |
|
704 break; |
|
705 case ContextMenuItemTagShowSubstitutions: |
|
706 #if !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD) |
|
707 if (frame->editor()->substitutionsPanelIsShowing()) |
|
708 item.setTitle(contextMenuItemTagShowSubstitutions(false)); |
|
709 else |
|
710 item.setTitle(contextMenuItemTagShowSubstitutions(true)); |
|
711 shouldEnable = frame->editor()->canEdit(); |
|
712 #endif |
|
713 break; |
|
714 case ContextMenuItemTagMakeUpperCase: |
|
715 case ContextMenuItemTagMakeLowerCase: |
|
716 case ContextMenuItemTagCapitalize: |
|
717 case ContextMenuItemTagChangeBack: |
|
718 shouldEnable = frame->editor()->canEdit(); |
|
719 break; |
|
720 case ContextMenuItemTagCorrectSpellingAutomatically: |
|
721 #if !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD) |
|
722 shouldCheck = frame->editor()->isAutomaticSpellingCorrectionEnabled(); |
|
723 #endif |
|
724 break; |
|
725 case ContextMenuItemTagSmartCopyPaste: |
|
726 #if !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD) |
|
727 shouldCheck = frame->editor()->smartInsertDeleteEnabled(); |
|
728 #endif |
|
729 break; |
|
730 case ContextMenuItemTagSmartQuotes: |
|
731 #if !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD) |
|
732 shouldCheck = frame->editor()->isAutomaticQuoteSubstitutionEnabled(); |
|
733 #endif |
|
734 break; |
|
735 case ContextMenuItemTagSmartDashes: |
|
736 #if !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD) |
|
737 shouldCheck = frame->editor()->isAutomaticDashSubstitutionEnabled(); |
|
738 #endif |
|
739 break; |
|
740 case ContextMenuItemTagSmartLinks: |
|
741 #if !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD) |
|
742 shouldCheck = frame->editor()->isAutomaticLinkDetectionEnabled(); |
|
743 #endif |
|
744 break; |
|
745 case ContextMenuItemTagTextReplacement: |
|
746 #if !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD) |
|
747 shouldCheck = frame->editor()->isAutomaticTextReplacementEnabled(); |
|
748 #endif |
|
749 break; |
|
750 case ContextMenuItemTagStopSpeaking: |
|
751 shouldEnable = controller() && controller()->client() && controller()->client()->isSpeaking(); |
|
752 break; |
|
753 #else // PLATFORM(MAC) ends here |
|
754 case ContextMenuItemTagStopSpeaking: |
|
755 break; |
|
756 #endif |
|
757 #if PLATFORM(GTK) |
|
758 case ContextMenuItemTagGoBack: |
|
759 shouldEnable = frame->page() && frame->page()->canGoBackOrForward(-1); |
|
760 break; |
|
761 case ContextMenuItemTagGoForward: |
|
762 shouldEnable = frame->page() && frame->page()->canGoBackOrForward(1); |
|
763 break; |
|
764 case ContextMenuItemTagStop: |
|
765 shouldEnable = frame->loader()->documentLoader()->isLoadingInAPISense(); |
|
766 break; |
|
767 case ContextMenuItemTagReload: |
|
768 shouldEnable = !frame->loader()->documentLoader()->isLoadingInAPISense(); |
|
769 break; |
|
770 case ContextMenuItemTagFontMenu: |
|
771 shouldEnable = frame->editor()->canEditRichly(); |
|
772 break; |
|
773 #else |
|
774 case ContextMenuItemTagGoBack: |
|
775 case ContextMenuItemTagGoForward: |
|
776 case ContextMenuItemTagStop: |
|
777 case ContextMenuItemTagReload: |
|
778 case ContextMenuItemTagFontMenu: |
|
779 #endif |
|
780 case ContextMenuItemTagNoAction: |
|
781 case ContextMenuItemTagOpenLinkInNewWindow: |
|
782 case ContextMenuItemTagDownloadLinkToDisk: |
|
783 case ContextMenuItemTagCopyLinkToClipboard: |
|
784 case ContextMenuItemTagOpenImageInNewWindow: |
|
785 case ContextMenuItemTagDownloadImageToDisk: |
|
786 case ContextMenuItemTagCopyImageToClipboard: |
|
787 case ContextMenuItemTagOpenFrameInNewWindow: |
|
788 case ContextMenuItemTagSpellingGuess: |
|
789 case ContextMenuItemTagOther: |
|
790 case ContextMenuItemTagSearchInSpotlight: |
|
791 case ContextMenuItemTagSearchWeb: |
|
792 case ContextMenuItemTagOpenWithDefaultApplication: |
|
793 case ContextMenuItemPDFActualSize: |
|
794 case ContextMenuItemPDFZoomIn: |
|
795 case ContextMenuItemPDFZoomOut: |
|
796 case ContextMenuItemPDFAutoSize: |
|
797 case ContextMenuItemPDFSinglePage: |
|
798 case ContextMenuItemPDFFacingPages: |
|
799 case ContextMenuItemPDFContinuous: |
|
800 case ContextMenuItemPDFNextPage: |
|
801 case ContextMenuItemPDFPreviousPage: |
|
802 case ContextMenuItemTagOpenLink: |
|
803 case ContextMenuItemTagIgnoreGrammar: |
|
804 case ContextMenuItemTagSpellingMenu: |
|
805 case ContextMenuItemTagShowFonts: |
|
806 case ContextMenuItemTagStyles: |
|
807 case ContextMenuItemTagShowColors: |
|
808 case ContextMenuItemTagSpeechMenu: |
|
809 case ContextMenuItemTagStartSpeaking: |
|
810 case ContextMenuItemTagWritingDirectionMenu: |
|
811 case ContextMenuItemTagTextDirectionMenu: |
|
812 case ContextMenuItemTagPDFSinglePageScrolling: |
|
813 case ContextMenuItemTagPDFFacingPagesScrolling: |
|
814 #if ENABLE(INSPECTOR) |
|
815 case ContextMenuItemTagInspectElement: |
|
816 #endif |
|
817 case ContextMenuItemBaseCustomTag: |
|
818 case ContextMenuItemBaseApplicationTag: |
|
819 break; |
|
820 } |
|
821 |
|
822 item.setChecked(shouldCheck); |
|
823 item.setEnabled(shouldEnable); |
|
824 } |
|
825 |
|
826 } // namespace WebCore |
|
827 |
|
828 #endif // ENABLE(CONTEXT_MENUS) |