Reviewed by Darin.
[WebKit-https.git] / WebCore / platform / mac / PopupMenuMac.mm
1 /*
2  * Copyright (C) 2006 Apple Computer, Inc.
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Library General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Library General Public License for more details.
13  *
14  * You should have received a copy of the GNU Library General Public License
15  * along with this library; see the file COPYING.LIB.  If not, write to
16  * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17  * Boston, MA 02111-1307, USA.
18  */
19
20 #import "config.h"
21 #import "PopupMenu.h"
22
23 #import "FontData.h"
24 #import "FrameMac.h"
25 #import "FrameView.h"
26 #import "HTMLNames.h"
27 #import "HTMLOptGroupElement.h"
28 #import "HTMLOptionElement.h"
29 #import "RenderMenuList.h"
30 #import "WebCoreSystemInterface.h"
31
32 namespace WebCore {
33
34 using namespace HTMLNames;
35
36 PopupMenu::PopupMenu(RenderMenuList* menuList)
37     : m_menuList(menuList)
38 {
39 }
40
41 PopupMenu::~PopupMenu()
42 {
43     if (m_popup)
44         [m_popup.get() setControlView:nil];
45 }
46
47 void PopupMenu::clear()
48 {
49     if (m_popup)
50         [m_popup.get() removeAllItems];
51 }
52
53 void PopupMenu::populate()
54 {
55     if (m_popup)
56         [m_popup.get() removeAllItems];
57     else {
58         m_popup = [[NSPopUpButtonCell alloc] initTextCell:@"" pullsDown:NO];
59         [m_popup.get() release]; // release here since the RetainPtr has retained the object already
60         [m_popup.get() setUsesItemFromMenu:NO];
61         [m_popup.get() setAutoenablesItems:NO];
62     }
63     BOOL messagesEnabled = [[m_popup.get() menu] menuChangedMessagesEnabled];
64     [[m_popup.get() menu] setMenuChangedMessagesEnabled:NO];
65     PopupMenu::addItems();
66     [[m_popup.get() menu] setMenuChangedMessagesEnabled:messagesEnabled];
67 }
68
69 void PopupMenu::show(const IntRect& r, FrameView* v, int index)
70 {
71     populate();
72     if ([m_popup.get() numberOfItems] <= 0)
73         return;
74     ASSERT([m_popup.get() numberOfItems] > index);
75
76     NSView* view = v->getDocumentView();
77
78     [m_popup.get() attachPopUpWithFrame:r inView:view];
79     [m_popup.get() selectItemAtIndex:index];
80     
81     NSFont* font = menuList()->style()->font().primaryFont()->getNSFont();
82
83     NSRect titleFrame = [m_popup.get() titleRectForBounds:r];
84     if (titleFrame.size.width <= 0 || titleFrame.size.height <= 0)
85         titleFrame = r;
86     float vertOffset = roundf((NSMaxY(r) - NSMaxY(titleFrame)) + NSHeight(titleFrame));
87     // Adjust for fonts other than the system font.
88     NSFont* defaultFont = [NSFont systemFontOfSize:[font pointSize]];
89     vertOffset += [font descender] - [defaultFont descender];
90     vertOffset = fmin(NSHeight(r), vertOffset);
91
92     NSMenu* menu = [m_popup.get() menu];
93     // FIXME: Need to document where this magic number 10 comes from.
94     NSPoint location = NSMakePoint(NSMinX(r) - 10, NSMaxY(r) - vertOffset);
95
96     // Save the current event that triggered the popup, so we can clean up our event
97     // state after the NSMenu goes away.
98     RefPtr<FrameMac> frame = Mac(v->frame());
99     NSEvent* event = [frame->currentEvent() retain];
100     
101     RefPtr<PopupMenu> protector(this);
102     
103     frame->willPopupMenu(menu);
104     wkPopupMenu(menu, location, roundf(NSWidth(r)), view, index, font);
105
106     if (menuList()) {
107         int newIndex = [m_popup.get() indexOfSelectedItem];
108         menuList()->hidePopup();
109
110         if (index != newIndex && newIndex >= 0)
111             menuList()->valueChanged(newIndex);
112
113         // Give the frame a chance to fix up its event state, since the popup eats all the
114         // events during tracking.
115         frame->sendFakeEventsAfterWidgetTracking(event);
116     }
117
118     [event release];
119 }
120
121 void PopupMenu::hide()
122 {
123     [m_popup.get() dismissPopUp];
124 }
125
126 void PopupMenu::addSeparator()
127 {
128     [[m_popup.get() menu] addItem:[NSMenuItem separatorItem]];
129 }
130
131 void PopupMenu::addGroupLabel(HTMLOptGroupElement* element)
132 {
133     String text = element->groupLabelText();
134
135     RenderStyle* s = element->renderStyle();
136     if (!s)
137         s = menuList()->style();
138
139     NSMutableDictionary* attributes = [[NSMutableDictionary alloc] init];
140     if (s->font() != Font())
141         [attributes setObject:s->font().primaryFont()->getNSFont() forKey:NSFontAttributeName];
142     // FIXME: Add support for styling the foreground and background colors.
143     NSAttributedString* string = [[NSAttributedString alloc] initWithString:text attributes:attributes];
144     [attributes release];
145
146     [m_popup.get() addItemWithTitle:@""];
147     NSMenuItem* menuItem = [m_popup.get() lastItem];
148     [menuItem setAttributedTitle:string];
149     [menuItem setEnabled:NO];
150
151     [string release];
152 }
153
154 void PopupMenu::addOption(HTMLOptionElement* element)
155 {
156     String text = element->optionText();
157     
158     bool groupEnabled = true;
159     if (element->parentNode() && element->parentNode()->hasTagName(optgroupTag))
160         groupEnabled = element->parentNode()->isEnabled();
161
162     RenderStyle* s = element->renderStyle();
163     if (!s)
164         s = menuList()->style();
165         
166     NSMutableDictionary* attributes = [[NSMutableDictionary alloc] init];
167     if (s->font() != Font())
168         [attributes setObject:s->font().primaryFont()->getNSFont() forKey:NSFontAttributeName];
169     // FIXME: Add support for styling the foreground and background colors.
170     // FIXME: Find a way to customize text color when an item is highlighted.
171     NSAttributedString* string = [[NSAttributedString alloc] initWithString:text attributes:attributes];
172     [attributes release];
173
174     [m_popup.get() addItemWithTitle:@""];
175     NSMenuItem* menuItem = [m_popup.get() lastItem];
176     [menuItem setAttributedTitle:string];
177     [menuItem setEnabled:groupEnabled && element->isEnabled()];
178
179     [string release];
180 }
181
182 }