WebCore:
[WebKit-https.git] / WebCore / platform / ContextMenu.cpp
1 /*
2  * Copyright (C) 2006 Apple Computer, Inc.  All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
24  */
25
26 #include "config.h"
27 #include "ContextMenu.h"
28
29 #include "ContextMenuController.h"
30 #include "Document.h"
31 #include "Editor.h"
32 #include "Frame.h"
33 #include "FrameLoader.h"
34 #include "KURL.h"
35 #include "Node.h"
36 #include "Page.h"
37 #include "ResourceRequest.h"
38 #include "SelectionController.h"
39
40 namespace WebCore {
41
42 #define MENU_ACTION_ITEM(action, title) static ContextMenuItem action##Item(ActionType, ContextMenuItemTag##action, String(title))
43
44 ContextMenuController* ContextMenu::controller() const
45 {
46     if (Node* node = m_hitTestResult.innerNonSharedNode())
47         if (Frame* frame = node->document()->frame())
48             if (Page* page = frame->page())
49                 return page->contextMenuController();
50     return 0;
51 }
52
53 static void createFontSubMenu(const HitTestResult& result, ContextMenuItem& fontMenuItem)
54 {
55     static ContextMenuItem SeparatorItem(SeparatorType, ContextMenuItemTagNoAction, String());
56
57     MENU_ACTION_ITEM(ShowFonts, "Show Fonts");
58     MENU_ACTION_ITEM(Bold, "Bold");
59     MENU_ACTION_ITEM(Italic, "Italic");
60     MENU_ACTION_ITEM(Underline, "Underline");
61     MENU_ACTION_ITEM(Outline, "Outline");
62     MENU_ACTION_ITEM(Styles, "Styles...");
63     MENU_ACTION_ITEM(ShowColors, "Show Colors");
64     
65     ContextMenu* fontMenu = new ContextMenu(result);
66     fontMenu->appendItem(ShowFontsItem);
67     fontMenu->appendItem(BoldItem);
68     fontMenu->appendItem(ItalicItem);
69     fontMenu->appendItem(UnderlineItem);
70     fontMenu->appendItem(OutlineItem);
71     fontMenu->appendItem(StylesItem);
72     fontMenu->appendItem(SeparatorItem);
73     fontMenu->appendItem(ShowColorsItem);
74     fontMenuItem.setSubMenu(fontMenu);
75 }
76
77 #ifndef BUILDING_ON_TIGER
78 static void createSpellingAndGrammarSubMenu(const HitTestResult& result, ContextMenuItem& spellingAndGrammarMenuItem)
79 {
80     MENU_ACTION_ITEM(ShowSpellingAndGrammar, "Show Spelling and Grammar");
81     MENU_ACTION_ITEM(CheckDocumentNow, "Check Document Now");
82     MENU_ACTION_ITEM(CheckSpellingWhileTyping, "Check Spelling While Typing");
83     MENU_ACTION_ITEM(CheckGrammarWithSpelling, "Check Grammar With Spelling");
84
85     ContextMenu* spellingAndGrammarMenu = new ContextMenu(result);
86     spellingAndGrammarMenu->appendItem(ShowSpellingAndGrammarItem);
87     spellingAndGrammarMenu->appendItem(CheckDocumentNowItem);
88     spellingAndGrammarMenu->appendItem(CheckSpellingWhileTypingItem);
89     spellingAndGrammarMenu->appendItem(CheckGrammarWithSpellingItem);
90     spellingAndGrammarMenuItem.setSubMenu(spellingAndGrammarMenu);
91 }
92 #else
93 static void createSpellingSubMenu(const HitTestResult& result, ContextMenuItem& spellingMenuItem)
94 {
95     MENU_ACTION_ITEM(SpellingMenuItem, "Spelling...");
96     MENU_ACTION_ITEM(CheckSpelling, "Check Spelling");
97     MENU_ACTION_ITEM(CheckSpellingWhileTyping, "Check Spelling as You Type");
98
99     ContextMenu* spellingMenu = new ContextMenu(result);
100     spellingMenu->appendItem(SpellingMenuItemItem);
101     spellingMenu->appendItem(CheckSpellingItem);
102     spellingMenu->appendItem(CheckSpellingWhileTypingItem);
103     spellingMenuItem.setSubMenu(spellingMenu);
104 }
105 #endif
106
107 #if PLATFORM(MAC)
108 static void createSpeechSubMenu(const HitTestResult& result, ContextMenuItem& speechMenuItem)
109 {
110     MENU_ACTION_ITEM(StartSpeaking, "Start Speaking");
111     MENU_ACTION_ITEM(StopSpeaking, "Stop Speaking");
112
113     ContextMenu* speechMenu = new ContextMenu(result);
114     speechMenu->appendItem(StartSpeakingItem);
115     speechMenu->appendItem(StopSpeakingItem);
116     speechMenuItem.setSubMenu(speechMenu);
117 }
118 #endif
119
120 static void createWritingDirectionSubMenu(const HitTestResult& result, ContextMenuItem& writingDirectionMenuItem)
121 {
122     MENU_ACTION_ITEM(DefaultDirection, "Default");
123     MENU_ACTION_ITEM(LeftToRight, "Left to Right");
124     MENU_ACTION_ITEM(RightToLeft, "Right to Left");
125
126     ContextMenu* writingDirectionMenu = new ContextMenu(result);
127     writingDirectionMenu->appendItem(DefaultDirectionItem);
128     writingDirectionMenu->appendItem(LeftToRightItem);
129     writingDirectionMenu->appendItem(RightToLeftItem);
130     writingDirectionMenuItem.setSubMenu(writingDirectionMenu);
131 }
132
133 void ContextMenu::populate()
134 {
135     static ContextMenuItem SeparatorItem(SeparatorType, ContextMenuItemTagNoAction, String());
136
137     MENU_ACTION_ITEM(OpenLinkInNewWindow, "Open Link in New Window");
138     MENU_ACTION_ITEM(DownloadLinkToDisk, "Download Linked File");
139     MENU_ACTION_ITEM(CopyLinkToClipboard, "Copy Link");
140     MENU_ACTION_ITEM(OpenImageInNewWindow, "Open Image in New Window");
141     MENU_ACTION_ITEM(DownloadImageToDisk, "Download Image");
142     MENU_ACTION_ITEM(CopyImageToClipboard, "Copy Image");
143     MENU_ACTION_ITEM(OpenFrameInNewWindow, "Open Frame in New Window");
144     MENU_ACTION_ITEM(Copy, "Copy");
145     MENU_ACTION_ITEM(GoBack, "Back");
146     MENU_ACTION_ITEM(GoForward, "Forward");
147     MENU_ACTION_ITEM(Stop, "Stop");
148     MENU_ACTION_ITEM(Reload, "Reload");
149     MENU_ACTION_ITEM(Cut, "Cut");
150     MENU_ACTION_ITEM(Paste, "Paste");
151     MENU_ACTION_ITEM(SpellingGuess, "");
152     MENU_ACTION_ITEM(NoGuessesFound, "No Guesses Found");
153     MENU_ACTION_ITEM(IgnoreSpelling, "Ignore Spelling");
154     MENU_ACTION_ITEM(IgnoreGrammar, "Ignore Grammar");
155     MENU_ACTION_ITEM(LearnSpelling, "Learn Spelling");
156 #if PLATFORM(MAC)
157     MENU_ACTION_ITEM(SearchInSpotlight, "Search in Spotlight");
158 #endif
159     MENU_ACTION_ITEM(SearchWeb, "Search in Google");
160     MENU_ACTION_ITEM(LookUpInDictionary, "Look Up in Dictionary");
161     MENU_ACTION_ITEM(OpenLink, "Open Link");
162
163     HitTestResult result = hitTestResult();
164     
165     Node* node = m_hitTestResult.innerNonSharedNode();
166     if (!node)
167         return;
168     Frame* frame = node->document()->frame();
169     if (!frame)
170         return;
171
172     if (!result.isContentEditable()) {
173         FrameLoader* loader = frame->loader();
174         KURL linkURL = result.absoluteLinkURL();
175         if (!linkURL.isEmpty()) {
176             if (loader->canHandleRequest(ResourceRequest(linkURL))) {
177                 appendItem(OpenLinkItem);
178                 appendItem(OpenLinkInNewWindowItem);
179                 appendItem(DownloadLinkToDiskItem);
180             }
181             appendItem(CopyLinkToClipboardItem);
182         }
183
184         KURL imageURL = result.absoluteImageURL();
185         if (!imageURL.isEmpty()) {
186             if (!linkURL.isEmpty())
187                 appendItem(SeparatorItem);
188
189             appendItem(OpenImageInNewWindowItem);
190             appendItem(DownloadImageToDiskItem);
191             if (imageURL.isLocalFile()) // FIXME: Should be checking if the image is local or we have a file wrapper for it
192                 appendItem(CopyImageToClipboardItem);
193         }
194
195         if (imageURL.isEmpty() && linkURL.isEmpty()) {
196             if (result.isSelected()) {
197 #if PLATFORM(MAC)
198                 appendItem(SearchInSpotlightItem);
199 #endif
200                 appendItem(SearchWebItem);
201                 appendItem(SeparatorItem);
202                 appendItem(LookUpInDictionaryItem);
203                 appendItem(SeparatorItem);
204                 appendItem(CopyItem);
205             } else {
206                 if (loader->canGoBackOrForward(-1))
207                     appendItem(GoBackItem);
208
209                 if (loader->canGoBackOrForward(1))
210                     appendItem(GoForwardItem);
211                 
212                 if (loader->isLoading())
213                     appendItem(StopItem);
214                 else
215                     appendItem(ReloadItem);
216
217                 if (frame->page() && frame != frame->page()->mainFrame())
218                     appendItem(OpenFrameInNewWindowItem);
219             }
220         }
221     } else { // Make an editing context menu
222         SelectionController* selectionController = frame->selectionController();
223         bool inPasswordField = selectionController->isInPasswordField();
224         
225         if (!inPasswordField) {
226             // Consider adding spelling-related or grammar-related context menu items (never both, since a single selected range
227             // is never considered a misspelling and bad grammar at the same time)
228             bool misspelling = frame->isSelectionMisspelled();
229             bool badGrammar = !misspelling && (frame->editor()->isGrammarCheckingEnabled() && frame->isSelectionUngrammatical());
230             
231             if (misspelling || badGrammar) {
232                 Vector<String> guesses = misspelling ? frame->guessesForMisspelledSelection() : frame->guessesForUngrammaticalSelection();
233                 size_t size = guesses.size();
234                 if (size == 0) {
235                     // If there's bad grammar but no suggestions (e.g., repeated word), just leave off the suggestions
236                     // list and trailing separator rather than adding a "No Guesses Found" item (matches AppKit)
237                     if (misspelling) {
238                         appendItem(NoGuessesFoundItem);
239                         appendItem(SeparatorItem);
240                     }
241                 } else {
242                     for (unsigned i = 0; i < size; i++) {
243                         const String &guess = guesses[i];
244                         if (!guess.isEmpty()) {
245                             ContextMenuItem item(ActionType, ContextMenuItemTagSpellingGuess, guess);
246                             appendItem(item);
247                         }
248                     }
249                     appendItem(SeparatorItem);                    
250                 }
251                 
252                 if (misspelling) {
253                     appendItem(IgnoreSpellingItem);
254                     appendItem(LearnSpellingItem);
255                 } else
256                     appendItem(IgnoreGrammarItem);
257                 appendItem(SeparatorItem);
258             }
259         }
260
261         if (result.isSelected() && !inPasswordField) {
262 #if PLATFORM(MAC)
263             appendItem(SearchInSpotlightItem);
264 #endif
265             appendItem(SearchWebItem);
266             appendItem(SeparatorItem);
267      
268             appendItem(LookUpInDictionaryItem);
269             appendItem(SeparatorItem);
270         }
271
272         appendItem(CutItem);
273         appendItem(CopyItem);
274         appendItem(PasteItem);
275
276         if (!inPasswordField) {
277             appendItem(SeparatorItem);
278 #ifndef BUILDING_ON_TIGER
279             ContextMenuItem SpellingAndGrammarMenuItem(SubmenuType, ContextMenuItemTagSpellingAndGrammarMenu,
280                 "Spelling and Grammar");
281             createSpellingAndGrammarSubMenu(m_hitTestResult, SpellingAndGrammarMenuItem);
282             appendItem(SpellingAndGrammarMenuItem);
283 #else
284             ContextMenuItem SpellingMenuItem(SubmenuType, ContextMenuItemTagSpellingMenu, "Spelling");
285             createSpellingSubMenu(m_hitTestResult, SpellingMenuItem);
286             appendItem(SpellingMenuItem);
287 #endif
288             ContextMenuItem FontMenuItem(SubmenuType, ContextMenuItemTagFontMenu, "Font");
289             createFontSubMenu(m_hitTestResult, FontMenuItem);
290             appendItem(FontMenuItem);
291 #if PLATFORM(MAC)
292             ContextMenuItem SpeechMenuItem(SubmenuType, ContextMenuItemTagSpeechMenu, "Speech");
293             createSpeechSubMenu(m_hitTestResult, SpeechMenuItem);
294             appendItem(SpeechMenuItem);
295 #endif
296             ContextMenuItem WritingDirectionMenuItem(SubmenuType, ContextMenuItemTagWritingDirectionMenu,
297                 "Writing Direction");
298             createWritingDirectionSubMenu(m_hitTestResult, WritingDirectionMenuItem);
299             appendItem(WritingDirectionMenuItem);
300         }
301     }
302 }
303
304 }