1 /*
2 * This file is part of the popup menu implementation for <select> elements in WebCore.
3 *
4 * Copyright (C) 2010 Stephan Aßmus <superstippi@gmx.de>
5 * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Library General Public
9 * License as published by the Free Software Foundation; either
10 * version 2 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Library General Public License for more details.
16 *
17 * You should have received a copy of the GNU Library General Public License
18 * along with this library; see the file COPYING.LIB. If not, write to
19 * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20 * Boston, MA 02111-1307, USA.
21 *
22 */
23
24 #include "config.h"
25 #include "PopupMenuHaiku.h"
26
27 #include "FrameView.h"
28
29 #include "NotImplemented.h"
30 #include <Application.h>
31 #include <Handler.h>
32 #include <MenuItem.h>
33 #include <Message.h>
34 #include <PopUpMenu.h>
35 #include <String.h>
36 #include <Window.h>
37 #include <support/Autolock.h>
38 #include <support/Locker.h>
39
40 namespace WebCore {
41
42 static const uint32 kPopupResult = 'pmrs';
43 static const uint32 kPopupHidden = 'pmhd';
44
45 // This BHandler is added to the application (main) thread, so that we
46 // invoke methods on the PopupMenuClient within the main thread.
47 class PopupMenuHandler : public BHandler {
48 public:
PopupMenuHandler(PopupMenuClient * popupClient)49 PopupMenuHandler(PopupMenuClient* popupClient)
50 : m_popupClient(popupClient)
51 {
52 }
53
MessageReceived(BMessage * message)54 virtual void MessageReceived(BMessage* message)
55 {
56 switch (message->what) {
57 case kPopupResult: {
58 int32 index = 0;
59 message->FindInt32("index", &index);
60 m_popupClient->valueChanged(index);
61 break;
62 }
63 case kPopupHidden:
64 m_popupClient->popupDidHide();
65 break;
66
67 default:
68 BHandler::MessageReceived(message);
69 }
70 }
71
72 private:
73 PopupMenuClient* m_popupClient;
74 };
75
76 class HaikuPopup : public BPopUpMenu {
77 public:
HaikuPopup(PopupMenuClient * popupClient)78 HaikuPopup(PopupMenuClient* popupClient)
79 : BPopUpMenu("WebCore Popup", true, false)
80 , m_popupClient(popupClient)
81 , m_Handler(popupClient)
82 {
83 if (be_app->Lock()) {
84 be_app->AddHandler(&m_Handler);
85 be_app->Unlock();
86 }
87 SetAsyncAutoDestruct(false);
88 }
89
~HaikuPopup()90 virtual ~HaikuPopup()
91 {
92 if (be_app->Lock()) {
93 be_app->RemoveHandler(&m_Handler);
94 be_app->Unlock();
95 }
96 }
97
show(const IntRect & rect,FrameView * view,int index)98 void show(const IntRect& rect, FrameView* view, int index)
99 {
100 // Clean out the menu first
101 for (int32 i = CountItems() - 1; i >= 0; i--)
102 delete RemoveItem(i);
103
104 // Popuplate the menu from the client
105 int itemCount = m_popupClient->listSize();
106 for (int i = 0; i < itemCount; i++) {
107 if (m_popupClient->itemIsSeparator(i))
108 AddSeparatorItem();
109 else {
110 // NOTE: WebCore distinguishes between "Group" and "Label"
111 // here, but both types of item (radio or check mark) currently
112 // look the same on Haiku.
113 BString label(m_popupClient->itemText(i));
114 BMessage* message = new BMessage(kPopupResult);
115 message->AddInt32("index", i);
116 BMenuItem* item = new BMenuItem(label.String(), message);
117 AddItem(item);
118 item->SetTarget(BMessenger(&m_Handler));
119 item->SetEnabled(m_popupClient->itemIsEnabled(i));
120 item->SetMarked(i == index);
121 }
122 }
123
124 // We need to force a layout now, or the item frames will not be
125 // computed yet, so we cannot move the current item under the mouse.
126 DoLayout();
127
128 // Account for frame of menu field
129 BRect screenRect(view->contentsToScreen(rect));
130 screenRect.OffsetBy(2, 2);
131 // Move currently selected item under the mouse.
132 if (BMenuItem* item = ItemAt(index))
133 screenRect.OffsetBy(0, -item->Frame().top);
134
135 BRect openRect = Bounds().OffsetToSelf(screenRect.LeftTop());
136
137 Go(screenRect.LeftTop(), true, true, openRect, true);
138 }
139
hide()140 void hide()
141 {
142 if (!IsHidden())
143 Hide();
144 }
145
146 private:
Hide()147 virtual void Hide()
148 {
149 BPopUpMenu::Hide();
150 be_app->PostMessage(kPopupHidden, &m_Handler);
151 }
152
153 PopupMenuClient* m_popupClient;
154 PopupMenuHandler m_Handler;
155 };
156
PopupMenuHaiku(PopupMenuClient * client)157 PopupMenuHaiku::PopupMenuHaiku(PopupMenuClient* client)
158 : m_popupClient(client)
159 , m_menu(new HaikuPopup(client))
160 {
161 // We don't need additional references to the client, since we completely
162 // control any sub-objects we create that need it as well.
163 }
164
~PopupMenuHaiku()165 PopupMenuHaiku::~PopupMenuHaiku()
166 {
167 delete m_menu;
168 }
169
disconnectClient()170 void PopupMenuHaiku::disconnectClient()
171 {
172 m_popupClient = 0;
173 }
174
show(const IntRect & rect,FrameView * view,int index)175 void PopupMenuHaiku::show(const IntRect& rect, FrameView* view, int index)
176 {
177 // The menu will update itself from the PopupMenuClient before showing.
178 m_menu->show(rect, view, index);
179 }
180
hide()181 void PopupMenuHaiku::hide()
182 {
183 m_menu->hide();
184 }
185
updateFromElement()186 void PopupMenuHaiku::updateFromElement()
187 {
188 client()->setTextFromItem(m_popupClient->selectedIndex());
189 }
190
191 } // namespace WebCore
192
193