| /* |
| * Copyright (C) 2006 Apple Computer, Inc. |
| * |
| * This library is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU Library General Public |
| * License as published by the Free Software Foundation; either |
| * version 2 of the License, or (at your option) any later version. |
| * |
| * This library is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| * Library General Public License for more details. |
| * |
| * You should have received a copy of the GNU Library General Public License |
| * along with this library; see the file COPYING.LIB. If not, write to |
| * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, |
| * Boston, MA 02111-1307, USA. |
| */ |
| |
| #import "config.h" |
| #import "PopupMenu.h" |
| |
| #import "EventHandler.h" |
| #import "FontData.h" |
| #import "Frame.h" |
| #import "FrameView.h" |
| #import "HTMLNames.h" |
| #import "HTMLOptGroupElement.h" |
| #import "HTMLOptionElement.h" |
| #import "HTMLSelectElement.h" |
| #import "WebCoreSystemInterface.h" |
| |
| namespace WebCore { |
| |
| using namespace HTMLNames; |
| |
| PopupMenu::PopupMenu(PopupMenuClient* client) |
| : m_popupClient(client) |
| { |
| } |
| |
| PopupMenu::~PopupMenu() |
| { |
| if (m_popup) |
| [m_popup.get() setControlView:nil]; |
| } |
| |
| void PopupMenu::clear() |
| { |
| if (m_popup) |
| [m_popup.get() removeAllItems]; |
| } |
| |
| void PopupMenu::populate() |
| { |
| if (m_popup) |
| clear(); |
| else { |
| m_popup = [[NSPopUpButtonCell alloc] initTextCell:@"" pullsDown:!client()->shouldPopOver()]; |
| [m_popup.get() release]; // release here since the RetainPtr has retained the object already |
| [m_popup.get() setUsesItemFromMenu:NO]; |
| [m_popup.get() setAutoenablesItems:NO]; |
| } |
| |
| BOOL messagesEnabled = [[m_popup.get() menu] menuChangedMessagesEnabled]; |
| [[m_popup.get() menu] setMenuChangedMessagesEnabled:NO]; |
| |
| // For pullDown menus the first item is hidden. |
| if (!client()->shouldPopOver()) |
| [m_popup.get() addItemWithTitle:@""]; |
| |
| ASSERT(client()); |
| int size = client()->listSize(); |
| |
| for (int i = 0; i < size; i++) { |
| if (client()->itemIsSeparator(i)) |
| [[m_popup.get() menu] addItem:[NSMenuItem separatorItem]]; |
| else { |
| RenderStyle* style = client()->itemStyle(i); |
| NSMutableDictionary* attributes = [[NSMutableDictionary alloc] init]; |
| if (style->font() != Font()) |
| [attributes setObject:style->font().primaryFont()->getNSFont() forKey:NSFontAttributeName]; |
| // FIXME: Add support for styling the foreground and background colors. |
| // FIXME: Find a way to customize text color when an item is highlighted. |
| NSAttributedString* string = [[NSAttributedString alloc] initWithString:client()->itemText(i) attributes:attributes]; |
| [attributes release]; |
| |
| [m_popup.get() addItemWithTitle:@""]; |
| NSMenuItem* menuItem = [m_popup.get() lastItem]; |
| [menuItem setAttributedTitle:string]; |
| [menuItem setEnabled:client()->itemIsEnabled(i)]; |
| [string release]; |
| } |
| } |
| |
| [[m_popup.get() menu] setMenuChangedMessagesEnabled:messagesEnabled]; |
| } |
| |
| void PopupMenu::show(const IntRect& r, FrameView* v, int index) |
| { |
| populate(); |
| int numItems = [m_popup.get() numberOfItems]; |
| if (numItems <= 0) { |
| if (client()) |
| client()->hidePopup(); |
| return; |
| } |
| ASSERT(numItems > index); |
| |
| // Workaround for crazy bug where a selected index of -1 for a menu with only 1 item will cause a blank menu. |
| if (index == -1 && numItems == 2 && !client()->shouldPopOver() && ![[m_popup.get() itemAtIndex:1] isEnabled]) |
| index = 0; |
| |
| NSView* view = v->getDocumentView(); |
| |
| [m_popup.get() attachPopUpWithFrame:r inView:view]; |
| [m_popup.get() selectItemAtIndex:index]; |
| |
| NSMenu* menu = [m_popup.get() menu]; |
| |
| NSPoint location; |
| NSFont* font = client()->clientStyle()->font().primaryFont()->getNSFont(); |
| |
| // 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; |
| if (client()->shouldPopOver()) { |
| NSRect titleFrame = [m_popup.get() titleRectForBounds:r]; |
| if (titleFrame.size.width <= 0 || titleFrame.size.height <= 0) |
| titleFrame = r; |
| float vertOffset = roundf((NSMaxY(r) - NSMaxY(titleFrame)) + NSHeight(titleFrame)); |
| // Adjust for fonts other than the system font. |
| NSFont* defaultFont = [NSFont systemFontOfSize:[font pointSize]]; |
| vertOffset += [font descender] - [defaultFont descender]; |
| vertOffset = fminf(NSHeight(r), vertOffset); |
| |
| location = NSMakePoint(NSMinX(r) + popOverHorizontalAdjust, NSMaxY(r) - vertOffset); |
| } else |
| location = NSMakePoint(NSMinX(r) + popUnderHorizontalAdjust, NSMaxY(r) + popUnderVerticalAdjust); |
| |
| // Save the current event that triggered the popup, so we can clean up our event |
| // state after the NSMenu goes away. |
| RefPtr<Frame> frame = v->frame(); |
| NSEvent* event = [frame->eventHandler()->currentNSEvent() retain]; |
| |
| RefPtr<PopupMenu> protector(this); |
| |
| RetainPtr<NSView> dummyView(AdoptNS, [[NSView alloc] initWithFrame:r]); |
| [view addSubview:dummyView.get()]; |
| location = [dummyView.get() convertPoint:location fromView:view]; |
| |
| frame->willPopupMenu(menu); |
| wkPopupMenu(menu, location, roundf(NSWidth(r)), dummyView.get(), index, font); |
| |
| [m_popup.get() dismissPopUp]; |
| [dummyView.get() removeFromSuperview]; |
| |
| if (client()) { |
| int newIndex = [m_popup.get() indexOfSelectedItem]; |
| client()->hidePopup(); |
| |
| // Adjust newIndex for hidden first item. |
| if (!client()->shouldPopOver()) |
| newIndex--; |
| |
| if (index != newIndex && newIndex >= 0) |
| client()->valueChanged(newIndex); |
| |
| // Give the frame a chance to fix up its event state, since the popup eats all the |
| // events during tracking. |
| frame->eventHandler()->sendFakeEventsAfterWidgetTracking(event); |
| } |
| |
| [event release]; |
| } |
| |
| void PopupMenu::hide() |
| { |
| [m_popup.get() dismissPopUp]; |
| } |
| |
| void PopupMenu::updateFromElement() |
| { |
| } |
| |
| bool PopupMenu::itemWritingDirectionIsNatural() |
| { |
| return true; |
| } |
| |
| } |