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