• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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