1 /* 2 * This file is part of the popup menu implementation for <select> elements in WebCore. 3 * 4 * Copyright (C) 2010 Stephan Amus <superstippi (at) 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: 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 72 private: 73 PopupMenuClient* m_popupClient; 74 }; 75 76 class HaikuPopup : public BPopUpMenu { 77 public: 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 146 private: 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 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 165 PopupMenuHaiku::~PopupMenuHaiku() 166 { 167 delete m_menu; 168 } 169 170 void PopupMenuHaiku::disconnectClient() 171 { 172 m_popupClient = 0; 173 } 174 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 181 void PopupMenuHaiku::hide() 182 { 183 m_menu->hide(); 184 } 185 186 void PopupMenuHaiku::updateFromElement() 187 { 188 client()->setTextFromItem(m_popupClient->selectedIndex()); 189 } 190 191 } // namespace WebCore 192 193