Home | History | Annotate | Download | only in mac
      1 /*
      2  * Copyright (C) 2010, 2011 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 INC. AND ITS CONTRIBUTORS ``AS IS''
     14  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
     15  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     16  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
     17  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     18  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
     19  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     20  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
     21  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     22  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
     23  * THE POSSIBILITY OF SUCH DAMAGE.
     24  */
     25 
     26 #import "config.h"
     27 #import "WebPopupMenuProxyMac.h"
     28 
     29 #import "NativeWebMouseEvent.h"
     30 #import "PageClientImpl.h"
     31 #import "PlatformPopupMenuData.h"
     32 #import "WKView.h"
     33 #import "WebPopupItem.h"
     34 #import <WebKitSystemInterface.h>
     35 
     36 using namespace WebCore;
     37 
     38 namespace WebKit {
     39 
     40 WebPopupMenuProxyMac::WebPopupMenuProxyMac(WKView *webView, WebPopupMenuProxy::Client* client)
     41     : WebPopupMenuProxy(client)
     42     , m_webView(webView)
     43 {
     44 }
     45 
     46 WebPopupMenuProxyMac::~WebPopupMenuProxyMac()
     47 {
     48     if (m_popup)
     49         [m_popup.get() setControlView:nil];
     50 }
     51 
     52 void WebPopupMenuProxyMac::populate(const Vector<WebPopupItem>& items, NSFont *font, TextDirection menuTextDirection)
     53 {
     54     if (m_popup)
     55         [m_popup.get() removeAllItems];
     56     else {
     57         m_popup.adoptNS([[NSPopUpButtonCell alloc] initTextCell:@"" pullsDown:NO]);
     58         [m_popup.get() setUsesItemFromMenu:NO];
     59         [m_popup.get() setAutoenablesItems:NO];
     60     }
     61 
     62     int size = items.size();
     63 
     64     for (int i = 0; i < size; i++) {
     65         if (items[i].m_type == WebPopupItem::Separator)
     66             [[m_popup.get() menu] addItem:[NSMenuItem separatorItem]];
     67         else {
     68             [m_popup.get() addItemWithTitle:@""];
     69             NSMenuItem *menuItem = [m_popup.get() lastItem];
     70 
     71             RetainPtr<NSMutableParagraphStyle> paragraphStyle(AdoptNS, [[NSParagraphStyle defaultParagraphStyle] mutableCopy]);
     72             NSWritingDirection writingDirection = items[i].m_textDirection == LTR ? NSWritingDirectionLeftToRight : NSWritingDirectionRightToLeft;
     73             [paragraphStyle.get() setBaseWritingDirection:writingDirection];
     74             [paragraphStyle.get() setAlignment:menuTextDirection == LTR ? NSLeftTextAlignment : NSRightTextAlignment];
     75             RetainPtr<NSMutableDictionary> attributes(AdoptNS, [[NSMutableDictionary alloc] initWithObjectsAndKeys:
     76                 paragraphStyle.get(), NSParagraphStyleAttributeName,
     77                 font, NSFontAttributeName,
     78             nil]);
     79             if (items[i].m_hasTextDirectionOverride) {
     80                 RetainPtr<NSNumber> writingDirectionValue(AdoptNS, [[NSNumber alloc] initWithInteger:writingDirection + NSTextWritingDirectionOverride]);
     81                 RetainPtr<NSArray> writingDirectionArray(AdoptNS, [[NSArray alloc] initWithObjects:writingDirectionValue.get(), nil]);
     82                 [attributes.get() setObject:writingDirectionArray.get() forKey:NSWritingDirectionAttributeName];
     83             }
     84             RetainPtr<NSAttributedString> string(AdoptNS, [[NSAttributedString alloc] initWithString:nsStringFromWebCoreString(items[i].m_text) attributes:attributes.get()]);
     85 
     86             [menuItem setAttributedTitle:string.get()];
     87             [menuItem setEnabled:items[i].m_isEnabled];
     88             [menuItem setToolTip:nsStringFromWebCoreString(items[i].m_toolTip)];
     89         }
     90     }
     91 }
     92 
     93 void WebPopupMenuProxyMac::showPopupMenu(const IntRect& rect, TextDirection textDirection, double scaleFactor, const Vector<WebPopupItem>& items, const PlatformPopupMenuData& data, int32_t selectedIndex)
     94 {
     95     NSFont *font;
     96     if (data.fontInfo.fontAttributeDictionary) {
     97         NSFontDescriptor *fontDescriptor = [NSFontDescriptor fontDescriptorWithFontAttributes:(NSDictionary *)data.fontInfo.fontAttributeDictionary.get()];
     98         font = [NSFont fontWithDescriptor:fontDescriptor size:((scaleFactor != 1) ? [fontDescriptor pointSize] * scaleFactor : 0)];
     99     } else
    100         font = [NSFont menuFontOfSize:0];
    101 
    102     populate(items, font, textDirection);
    103 
    104     [m_popup.get() attachPopUpWithFrame:rect inView:m_webView];
    105     [m_popup.get() selectItemAtIndex:selectedIndex];
    106     [m_popup.get() setUserInterfaceLayoutDirection:textDirection == LTR ? NSUserInterfaceLayoutDirectionLeftToRight : NSUserInterfaceLayoutDirectionRightToLeft];
    107 
    108     NSMenu *menu = [m_popup.get() menu];
    109 
    110     // These values were borrowed from AppKit to match their placement of the menu.
    111     const int popOverHorizontalAdjust = -10;
    112     const int popUnderHorizontalAdjust = 6;
    113     const int popUnderVerticalAdjust = 6;
    114 
    115     // Menus that pop-over directly obscure the node that generated the popup menu.
    116     // Menus that pop-under are offset underneath it.
    117     NSPoint location;
    118     if (data.shouldPopOver) {
    119         NSRect titleFrame = [m_popup.get()  titleRectForBounds:rect];
    120         if (titleFrame.size.width <= 0 || titleFrame.size.height <= 0)
    121             titleFrame = rect;
    122         float vertOffset = roundf((NSMaxY(rect) - NSMaxY(titleFrame)) + NSHeight(titleFrame));
    123         location = NSMakePoint(NSMinX(rect) + popOverHorizontalAdjust, NSMaxY(rect) - vertOffset);
    124     } else
    125         location = NSMakePoint(NSMinX(rect) + popUnderHorizontalAdjust, NSMaxY(rect) + popUnderVerticalAdjust);
    126 
    127     RetainPtr<NSView> dummyView(AdoptNS, [[NSView alloc] initWithFrame:rect]);
    128     [m_webView addSubview:dummyView.get()];
    129     location = [dummyView.get() convertPoint:location fromView:m_webView];
    130 
    131     WKPopupMenu(menu, location, roundf(NSWidth(rect)), dummyView.get(), selectedIndex, font);
    132 
    133     [m_popup.get() dismissPopUp];
    134     [dummyView.get() removeFromSuperview];
    135 
    136     if (!m_client)
    137         return;
    138 
    139     m_client->valueChangedForPopupMenu(this, [m_popup.get() indexOfSelectedItem]);
    140 
    141     // <https://bugs.webkit.org/show_bug.cgi?id=57904> This code is adopted from EventHandler::sendFakeEventsAfterWidgetTracking().
    142     if (!m_client->currentlyProcessedMouseDownEvent())
    143         return;
    144 
    145     NSEvent* initiatingNSEvent = m_client->currentlyProcessedMouseDownEvent()->nativeEvent();
    146     if ([initiatingNSEvent type] != NSLeftMouseDown)
    147         return;
    148 
    149     NSEvent *fakeEvent = [NSEvent mouseEventWithType:NSLeftMouseUp
    150                                             location:[initiatingNSEvent locationInWindow]
    151                                        modifierFlags:[initiatingNSEvent modifierFlags]
    152                                            timestamp:[initiatingNSEvent timestamp]
    153                                         windowNumber:[initiatingNSEvent windowNumber]
    154                                              context:[initiatingNSEvent context]
    155                                          eventNumber:[initiatingNSEvent eventNumber]
    156                                           clickCount:[initiatingNSEvent clickCount]
    157                                             pressure:[initiatingNSEvent pressure]];
    158 
    159     [NSApp postEvent:fakeEvent atStart:YES];
    160     fakeEvent = [NSEvent mouseEventWithType:NSMouseMoved
    161                                    location:[[m_webView window] convertScreenToBase:[NSEvent mouseLocation]]
    162                               modifierFlags:[initiatingNSEvent modifierFlags]
    163                                   timestamp:[initiatingNSEvent timestamp]
    164                                windowNumber:[initiatingNSEvent windowNumber]
    165                                     context:[initiatingNSEvent context]
    166                                 eventNumber:0
    167                                  clickCount:0
    168                                    pressure:0];
    169     [NSApp postEvent:fakeEvent atStart:YES];
    170 }
    171 
    172 void WebPopupMenuProxyMac::hidePopupMenu()
    173 {
    174     [m_popup.get() dismissPopUp];
    175 }
    176 
    177 } // namespace WebKit
    178