|
1 /* |
|
2 * Copyright (C) 2006, 2007 Apple 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 "ContextMenuController.h" |
|
28 |
|
29 #include "Chrome.h" |
|
30 #include "ContextMenu.h" |
|
31 #include "ContextMenuClient.h" |
|
32 #include "Document.h" |
|
33 #include "DocumentFragment.h" |
|
34 #include "DocumentLoader.h" |
|
35 #include "Editor.h" |
|
36 #include "EditorClient.h" |
|
37 #include "Event.h" |
|
38 #include "EventHandler.h" |
|
39 #include "EventNames.h" |
|
40 #include "Frame.h" |
|
41 #include "FrameLoader.h" |
|
42 #include "FrameLoadRequest.h" |
|
43 #include "HitTestRequest.h" |
|
44 #include "HitTestResult.h" |
|
45 #include "InspectorController.h" |
|
46 #include "KURL.h" |
|
47 #include "MouseEvent.h" |
|
48 #include "Node.h" |
|
49 #include "Page.h" |
|
50 #include "RenderLayer.h" |
|
51 #include "RenderObject.h" |
|
52 #include "ReplaceSelectionCommand.h" |
|
53 #include "ResourceRequest.h" |
|
54 #include "SelectionController.h" |
|
55 #include "Settings.h" |
|
56 #include "TextIterator.h" |
|
57 #include "markup.h" |
|
58 |
|
59 namespace WebCore { |
|
60 |
|
61 using namespace EventNames; |
|
62 |
|
63 ContextMenuController::ContextMenuController(Page* page, ContextMenuClient* client) |
|
64 : m_page(page) |
|
65 , m_client(client) |
|
66 , m_contextMenu(0) |
|
67 { |
|
68 ASSERT_ARG(page, page); |
|
69 ASSERT_ARG(client, client); |
|
70 } |
|
71 |
|
72 ContextMenuController::~ContextMenuController() |
|
73 { |
|
74 m_client->contextMenuDestroyed(); |
|
75 } |
|
76 |
|
77 void ContextMenuController::clearContextMenu() |
|
78 { |
|
79 m_contextMenu.set(0); |
|
80 } |
|
81 |
|
82 void ContextMenuController::handleContextMenuEvent(Event* event) |
|
83 { |
|
84 ASSERT(event->type() == contextmenuEvent); |
|
85 if (!event->isMouseEvent()) |
|
86 return; |
|
87 MouseEvent* mouseEvent = static_cast<MouseEvent*>(event); |
|
88 IntPoint point = IntPoint(mouseEvent->pageX(), mouseEvent->pageY()); |
|
89 HitTestResult result(point); |
|
90 |
|
91 if (Frame* frame = event->target()->toNode()->document()->frame()) |
|
92 result = frame->eventHandler()->hitTestResultAtPoint(point, false); |
|
93 |
|
94 if (!result.innerNonSharedNode()) |
|
95 return; |
|
96 |
|
97 m_contextMenu.set(new ContextMenu(result)); |
|
98 m_contextMenu->populate(); |
|
99 if (m_page->inspectorController()->enabled()) |
|
100 m_contextMenu->addInspectElementItem(); |
|
101 |
|
102 PlatformMenuDescription customMenu = m_client->getCustomMenuFromDefaultItems(m_contextMenu.get()); |
|
103 m_contextMenu->setPlatformDescription(customMenu); |
|
104 |
|
105 event->setDefaultHandled(); |
|
106 } |
|
107 |
|
108 static void openNewWindow(const KURL& urlToLoad, Frame* frame) |
|
109 { |
|
110 if (Page* oldPage = frame->page()) |
|
111 if (Page* newPage = oldPage->chrome()->createWindow(frame, |
|
112 FrameLoadRequest(ResourceRequest(urlToLoad, frame->loader()->outgoingReferrer())))) |
|
113 newPage->chrome()->show(); |
|
114 } |
|
115 |
|
116 void ContextMenuController::contextMenuItemSelected(ContextMenuItem* item) |
|
117 { |
|
118 ASSERT(item->type() == ActionType); |
|
119 |
|
120 if (item->action() >= ContextMenuItemBaseApplicationTag) { |
|
121 m_client->contextMenuItemSelected(item, m_contextMenu.get()); |
|
122 return; |
|
123 } |
|
124 |
|
125 HitTestResult result = m_contextMenu->hitTestResult(); |
|
126 Frame* frame = result.innerNonSharedNode()->document()->frame(); |
|
127 if (!frame) |
|
128 return; |
|
129 |
|
130 switch (item->action()) { |
|
131 case ContextMenuItemTagOpenLinkInNewWindow: |
|
132 openNewWindow(result.absoluteLinkURL(), frame); |
|
133 break; |
|
134 case ContextMenuItemTagDownloadLinkToDisk: |
|
135 // FIXME: Some day we should be able to do this from within WebCore. |
|
136 m_client->downloadURL(result.absoluteLinkURL()); |
|
137 break; |
|
138 case ContextMenuItemTagCopyLinkToClipboard: |
|
139 frame->editor()->copyURL(result.absoluteLinkURL(), result.textContent()); |
|
140 break; |
|
141 case ContextMenuItemTagOpenImageInNewWindow: |
|
142 openNewWindow(result.absoluteImageURL(), frame); |
|
143 break; |
|
144 case ContextMenuItemTagDownloadImageToDisk: |
|
145 // FIXME: Some day we should be able to do this from within WebCore. |
|
146 m_client->downloadURL(result.absoluteImageURL()); |
|
147 break; |
|
148 case ContextMenuItemTagCopyImageToClipboard: |
|
149 // FIXME: The Pasteboard class is not written yet |
|
150 // For now, call into the client. This is temporary! |
|
151 frame->editor()->copyImage(result); |
|
152 break; |
|
153 case ContextMenuItemTagOpenFrameInNewWindow: { |
|
154 KURL url = frame->loader()->documentLoader()->unreachableURL(); |
|
155 if (frame && url.isEmpty()) |
|
156 url = frame->loader()->documentLoader()->URL(); |
|
157 openNewWindow(url, frame); |
|
158 break; |
|
159 } |
|
160 case ContextMenuItemTagCopy: |
|
161 frame->editor()->copy(); |
|
162 break; |
|
163 case ContextMenuItemTagGoBack: |
|
164 frame->loader()->goBackOrForward(-1); |
|
165 break; |
|
166 case ContextMenuItemTagGoForward: |
|
167 frame->loader()->goBackOrForward(1); |
|
168 break; |
|
169 case ContextMenuItemTagStop: |
|
170 frame->loader()->stop(); |
|
171 break; |
|
172 case ContextMenuItemTagReload: |
|
173 frame->loader()->reload(); |
|
174 break; |
|
175 case ContextMenuItemTagCut: |
|
176 frame->editor()->cut(); |
|
177 break; |
|
178 case ContextMenuItemTagPaste: |
|
179 frame->editor()->paste(); |
|
180 break; |
|
181 case ContextMenuItemTagSpellingGuess: |
|
182 ASSERT(frame->selectedText().length()); |
|
183 if (frame->editor()->shouldInsertText(item->title(), frame->selectionController()->toRange().get(), |
|
184 EditorInsertActionPasted)) { |
|
185 Document* document = frame->document(); |
|
186 RefPtr<ReplaceSelectionCommand> command = |
|
187 new ReplaceSelectionCommand(document, createFragmentFromMarkup(document, item->title(), ""), |
|
188 true, false, true); |
|
189 applyCommand(command); |
|
190 frame->revealSelection(RenderLayer::gAlignToEdgeIfNeeded); |
|
191 } |
|
192 break; |
|
193 case ContextMenuItemTagIgnoreSpelling: |
|
194 frame->editor()->ignoreSpelling(); |
|
195 break; |
|
196 case ContextMenuItemTagLearnSpelling: |
|
197 frame->editor()->learnSpelling(); |
|
198 break; |
|
199 case ContextMenuItemTagSearchWeb: |
|
200 m_client->searchWithGoogle(frame); |
|
201 break; |
|
202 case ContextMenuItemTagLookUpInDictionary: |
|
203 // FIXME: Some day we may be able to do this from within WebCore. |
|
204 m_client->lookUpInDictionary(frame); |
|
205 break; |
|
206 case ContextMenuItemTagOpenLink: |
|
207 if (Frame* targetFrame = result.targetFrame()) |
|
208 targetFrame->loader()->load(FrameLoadRequest(ResourceRequest(result.absoluteLinkURL(), |
|
209 frame->loader()->outgoingReferrer())), false, true, 0, 0, HashMap<String, String>()); |
|
210 else |
|
211 openNewWindow(result.absoluteLinkURL(), frame); |
|
212 break; |
|
213 case ContextMenuItemTagBold: |
|
214 frame->editor()->execCommand("ToggleBold"); |
|
215 break; |
|
216 case ContextMenuItemTagItalic: |
|
217 frame->editor()->execCommand("ToggleItalic"); |
|
218 break; |
|
219 case ContextMenuItemTagUnderline: |
|
220 frame->editor()->toggleUnderline(); |
|
221 break; |
|
222 case ContextMenuItemTagOutline: |
|
223 // We actually never enable this because CSS does not have a way to specify an outline font, |
|
224 // which may make this difficult to implement. Maybe a special case of text-shadow? |
|
225 break; |
|
226 case ContextMenuItemTagStartSpeaking: { |
|
227 ExceptionCode ec; |
|
228 RefPtr<Range> selectedRange = frame->selectionController()->toRange(); |
|
229 if (!selectedRange || selectedRange->collapsed(ec)) { |
|
230 Document* document = result.innerNonSharedNode()->document(); |
|
231 selectedRange = document->createRange(); |
|
232 selectedRange->selectNode(document->documentElement(), ec); |
|
233 } |
|
234 m_client->speak(plainText(selectedRange.get())); |
|
235 break; |
|
236 } |
|
237 case ContextMenuItemTagStopSpeaking: |
|
238 m_client->stopSpeaking(); |
|
239 break; |
|
240 case ContextMenuItemTagDefaultDirection: |
|
241 frame->editor()->setBaseWritingDirection("inherit"); |
|
242 break; |
|
243 case ContextMenuItemTagLeftToRight: |
|
244 frame->editor()->setBaseWritingDirection("ltr"); |
|
245 break; |
|
246 case ContextMenuItemTagRightToLeft: |
|
247 frame->editor()->setBaseWritingDirection("rtl"); |
|
248 break; |
|
249 #if PLATFORM(MAC) |
|
250 case ContextMenuItemTagSearchInSpotlight: |
|
251 m_client->searchWithSpotlight(); |
|
252 break; |
|
253 #endif |
|
254 case ContextMenuItemTagShowSpellingPanel: |
|
255 frame->editor()->showSpellingGuessPanel(); |
|
256 break; |
|
257 case ContextMenuItemTagCheckSpelling: |
|
258 frame->editor()->advanceToNextMisspelling(); |
|
259 break; |
|
260 case ContextMenuItemTagCheckSpellingWhileTyping: |
|
261 frame->editor()->toggleContinuousSpellChecking(); |
|
262 break; |
|
263 #ifndef BUILDING_ON_TIGER |
|
264 case ContextMenuItemTagCheckGrammarWithSpelling: |
|
265 frame->editor()->toggleGrammarChecking(); |
|
266 break; |
|
267 #endif |
|
268 #if PLATFORM(MAC) |
|
269 case ContextMenuItemTagShowFonts: |
|
270 frame->editor()->showFontPanel(); |
|
271 break; |
|
272 case ContextMenuItemTagStyles: |
|
273 frame->editor()->showStylesPanel(); |
|
274 break; |
|
275 case ContextMenuItemTagShowColors: |
|
276 frame->editor()->showColorPanel(); |
|
277 break; |
|
278 #endif |
|
279 case ContextMenuItemTagInspectElement: |
|
280 if (Page* page = frame->page()) |
|
281 page->inspectorController()->inspect(result.innerNonSharedNode()); |
|
282 break; |
|
283 default: |
|
284 break; |
|
285 } |
|
286 } |
|
287 |
|
288 } // namespace WebCore |