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 40namespace WebCore { 41 42static const uint32 kPopupResult = 'pmrs'; 43static 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. 47class PopupMenuHandler : public BHandler { 48public: 49 PopupMenuHandler(PopupMenuClient* popupClient) 50 : m_popupClient(popupClient) 51 { 52 } 53 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 72private: 73 PopupMenuClient* m_popupClient; 74}; 75 76class HaikuPopup : public BPopUpMenu { 77public: 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 90 virtual ~HaikuPopup() 91 { 92 if (be_app->Lock()) { 93 be_app->RemoveHandler(&m_Handler); 94 be_app->Unlock(); 95 } 96 } 97 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 140 void hide() 141 { 142 if (!IsHidden()) 143 Hide(); 144 } 145 146private: 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 157PopupMenuHaiku::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 165PopupMenuHaiku::~PopupMenuHaiku() 166{ 167 delete m_menu; 168} 169 170void PopupMenuHaiku::disconnectClient() 171{ 172 m_popupClient = 0; 173} 174 175void 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 181void PopupMenuHaiku::hide() 182{ 183 m_menu->hide(); 184} 185 186void PopupMenuHaiku::updateFromElement() 187{ 188 client()->setTextFromItem(m_popupClient->selectedIndex()); 189} 190 191} // namespace WebCore 192 193