/* * Copyright (C) 2010, 2011 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. */ #import "config.h" #import "WebPopupMenuProxyMac.h" #import "NativeWebMouseEvent.h" #import "PageClientImpl.h" #import "PlatformPopupMenuData.h" #import "WKView.h" #import "WebPopupItem.h" #import <WebKitSystemInterface.h> using namespace WebCore; namespace WebKit { WebPopupMenuProxyMac::WebPopupMenuProxyMac(WKView *webView, WebPopupMenuProxy::Client* client) : WebPopupMenuProxy(client) , m_webView(webView) { } WebPopupMenuProxyMac::~WebPopupMenuProxyMac() { if (m_popup) [m_popup.get() setControlView:nil]; } void WebPopupMenuProxyMac::populate(const Vector<WebPopupItem>& items, NSFont *font, TextDirection menuTextDirection) { if (m_popup) [m_popup.get() removeAllItems]; else { m_popup.adoptNS([[NSPopUpButtonCell alloc] initTextCell:@"" pullsDown:NO]); [m_popup.get() setUsesItemFromMenu:NO]; [m_popup.get() setAutoenablesItems:NO]; } int size = items.size(); for (int i = 0; i < size; i++) { if (items[i].m_type == WebPopupItem::Separator) [[m_popup.get() menu] addItem:[NSMenuItem separatorItem]]; else { [m_popup.get() addItemWithTitle:@""]; NSMenuItem *menuItem = [m_popup.get() lastItem]; RetainPtr<NSMutableParagraphStyle> paragraphStyle(AdoptNS, [[NSParagraphStyle defaultParagraphStyle] mutableCopy]); NSWritingDirection writingDirection = items[i].m_textDirection == LTR ? NSWritingDirectionLeftToRight : NSWritingDirectionRightToLeft; [paragraphStyle.get() setBaseWritingDirection:writingDirection]; [paragraphStyle.get() setAlignment:menuTextDirection == LTR ? NSLeftTextAlignment : NSRightTextAlignment]; RetainPtr<NSMutableDictionary> attributes(AdoptNS, [[NSMutableDictionary alloc] initWithObjectsAndKeys: paragraphStyle.get(), NSParagraphStyleAttributeName, font, NSFontAttributeName, nil]); if (items[i].m_hasTextDirectionOverride) { RetainPtr<NSNumber> writingDirectionValue(AdoptNS, [[NSNumber alloc] initWithInteger:writingDirection + NSTextWritingDirectionOverride]); RetainPtr<NSArray> writingDirectionArray(AdoptNS, [[NSArray alloc] initWithObjects:writingDirectionValue.get(), nil]); [attributes.get() setObject:writingDirectionArray.get() forKey:NSWritingDirectionAttributeName]; } RetainPtr<NSAttributedString> string(AdoptNS, [[NSAttributedString alloc] initWithString:nsStringFromWebCoreString(items[i].m_text) attributes:attributes.get()]); [menuItem setAttributedTitle:string.get()]; [menuItem setEnabled:items[i].m_isEnabled]; [menuItem setToolTip:nsStringFromWebCoreString(items[i].m_toolTip)]; } } } void WebPopupMenuProxyMac::showPopupMenu(const IntRect& rect, TextDirection textDirection, double scaleFactor, const Vector<WebPopupItem>& items, const PlatformPopupMenuData& data, int32_t selectedIndex) { NSFont *font; if (data.fontInfo.fontAttributeDictionary) { NSFontDescriptor *fontDescriptor = [NSFontDescriptor fontDescriptorWithFontAttributes:(NSDictionary *)data.fontInfo.fontAttributeDictionary.get()]; font = [NSFont fontWithDescriptor:fontDescriptor size:((scaleFactor != 1) ? [fontDescriptor pointSize] * scaleFactor : 0)]; } else font = [NSFont menuFontOfSize:0]; populate(items, font, textDirection); [m_popup.get() attachPopUpWithFrame:rect inView:m_webView]; [m_popup.get() selectItemAtIndex:selectedIndex]; [m_popup.get() setUserInterfaceLayoutDirection:textDirection == LTR ? NSUserInterfaceLayoutDirectionLeftToRight : NSUserInterfaceLayoutDirectionRightToLeft]; NSMenu *menu = [m_popup.get() menu]; // These values were borrowed from AppKit to match their placement of the menu. const int popOverHorizontalAdjust = -10; const int popUnderHorizontalAdjust = 6; const int popUnderVerticalAdjust = 6; // Menus that pop-over directly obscure the node that generated the popup menu. // Menus that pop-under are offset underneath it. NSPoint location; if (data.shouldPopOver) { NSRect titleFrame = [m_popup.get() titleRectForBounds:rect]; if (titleFrame.size.width <= 0 || titleFrame.size.height <= 0) titleFrame = rect; float vertOffset = roundf((NSMaxY(rect) - NSMaxY(titleFrame)) + NSHeight(titleFrame)); location = NSMakePoint(NSMinX(rect) + popOverHorizontalAdjust, NSMaxY(rect) - vertOffset); } else location = NSMakePoint(NSMinX(rect) + popUnderHorizontalAdjust, NSMaxY(rect) + popUnderVerticalAdjust); RetainPtr<NSView> dummyView(AdoptNS, [[NSView alloc] initWithFrame:rect]); [m_webView addSubview:dummyView.get()]; location = [dummyView.get() convertPoint:location fromView:m_webView]; WKPopupMenu(menu, location, roundf(NSWidth(rect)), dummyView.get(), selectedIndex, font); [m_popup.get() dismissPopUp]; [dummyView.get() removeFromSuperview]; if (!m_client) return; m_client->valueChangedForPopupMenu(this, [m_popup.get() indexOfSelectedItem]); // <https://bugs.webkit.org/show_bug.cgi?id=57904> This code is adopted from EventHandler::sendFakeEventsAfterWidgetTracking(). if (!m_client->currentlyProcessedMouseDownEvent()) return; NSEvent* initiatingNSEvent = m_client->currentlyProcessedMouseDownEvent()->nativeEvent(); if ([initiatingNSEvent type] != NSLeftMouseDown) return; NSEvent *fakeEvent = [NSEvent mouseEventWithType:NSLeftMouseUp location:[initiatingNSEvent locationInWindow] modifierFlags:[initiatingNSEvent modifierFlags] timestamp:[initiatingNSEvent timestamp] windowNumber:[initiatingNSEvent windowNumber] context:[initiatingNSEvent context] eventNumber:[initiatingNSEvent eventNumber] clickCount:[initiatingNSEvent clickCount] pressure:[initiatingNSEvent pressure]]; [NSApp postEvent:fakeEvent atStart:YES]; fakeEvent = [NSEvent mouseEventWithType:NSMouseMoved location:[[m_webView window] convertScreenToBase:[NSEvent mouseLocation]] modifierFlags:[initiatingNSEvent modifierFlags] timestamp:[initiatingNSEvent timestamp] windowNumber:[initiatingNSEvent windowNumber] context:[initiatingNSEvent context] eventNumber:0 clickCount:0 pressure:0]; [NSApp postEvent:fakeEvent atStart:YES]; } void WebPopupMenuProxyMac::hidePopupMenu() { [m_popup.get() dismissPopUp]; } } // namespace WebKit