1 /* 2 * This file is part of the popup menu implementation for <select> elements in WebCore. 3 * 4 * Copyright (C) 2006, 2007, 2008 Apple Inc. All rights reserved. 5 * Copyright (C) 2006 Michael Emmel mike.emmel (at) gmail.com 6 * Copyright (C) 2008 Collabora Ltd. 7 * 8 * This library is free software; you can redistribute it and/or 9 * modify it under the terms of the GNU Library General Public 10 * License as published by the Free Software Foundation; either 11 * version 2 of the License, or (at your option) any later version. 12 * 13 * This library is distributed in the hope that it will be useful, 14 * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16 * Library General Public License for more details. 17 * 18 * You should have received a copy of the GNU Library General Public License 19 * along with this library; see the file COPYING.LIB. If not, write to 20 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 21 * Boston, MA 02110-1301, USA. 22 * 23 */ 24 25 #include "config.h" 26 #include "PopupMenu.h" 27 28 #include "CString.h" 29 #include "FrameView.h" 30 #include "HostWindow.h" 31 #include "PlatformString.h" 32 #include <gtk/gtk.h> 33 34 namespace WebCore { 35 36 PopupMenu::PopupMenu(PopupMenuClient* client) 37 : m_popupClient(client) 38 { 39 } 40 41 PopupMenu::~PopupMenu() 42 { 43 if (m_popup) { 44 g_signal_handlers_disconnect_matched(m_popup.get(), G_SIGNAL_MATCH_DATA, 0, 0, 0, 0, this); 45 hide(); 46 } 47 } 48 49 void PopupMenu::show(const IntRect& rect, FrameView* view, int index) 50 { 51 ASSERT(client()); 52 53 if (!m_popup) { 54 m_popup = GTK_MENU(gtk_menu_new()); 55 g_signal_connect(m_popup.get(), "unmap", G_CALLBACK(menuUnmapped), this); 56 } else 57 gtk_container_foreach(GTK_CONTAINER(m_popup.get()), reinterpret_cast<GtkCallback>(menuRemoveItem), this); 58 59 int x, y; 60 gdk_window_get_origin(GTK_WIDGET(view->hostWindow()->platformPageClient())->window, &x, &y); 61 m_menuPosition = view->contentsToWindow(rect.location()); 62 m_menuPosition = IntPoint(m_menuPosition.x() + x, m_menuPosition.y() + y + rect.height()); 63 m_indexMap.clear(); 64 65 const int size = client()->listSize(); 66 for (int i = 0; i < size; ++i) { 67 GtkWidget* item; 68 if (client()->itemIsSeparator(i)) 69 item = gtk_separator_menu_item_new(); 70 else 71 item = gtk_menu_item_new_with_label(client()->itemText(i).utf8().data()); 72 73 m_indexMap.add(item, i); 74 g_signal_connect(item, "activate", G_CALLBACK(menuItemActivated), this); 75 76 // FIXME: Apply the PopupMenuStyle from client()->itemStyle(i) 77 gtk_widget_set_sensitive(item, client()->itemIsEnabled(i)); 78 gtk_menu_shell_append(GTK_MENU_SHELL(m_popup.get()), item); 79 gtk_widget_show(item); 80 } 81 82 gtk_menu_set_active(m_popup.get(), index); 83 84 85 // The size calls are directly copied from gtkcombobox.c which is LGPL 86 GtkRequisition requisition; 87 gtk_widget_set_size_request(GTK_WIDGET(m_popup.get()), -1, -1); 88 gtk_widget_size_request(GTK_WIDGET(m_popup.get()), &requisition); 89 gtk_widget_set_size_request(GTK_WIDGET(m_popup.get()), std::max(rect.width(), requisition.width), -1); 90 91 GList* children = GTK_MENU_SHELL(m_popup.get())->children; 92 if (size) 93 for (int i = 0; i < size; i++) { 94 if (i > index) 95 break; 96 97 GtkWidget* item = reinterpret_cast<GtkWidget*>(children->data); 98 GtkRequisition itemRequisition; 99 gtk_widget_get_child_requisition(item, &itemRequisition); 100 m_menuPosition.setY(m_menuPosition.y() - itemRequisition.height); 101 102 children = g_list_next(children); 103 } else 104 // Center vertically the empty popup in the combo box area 105 m_menuPosition.setY(m_menuPosition.y() - rect.height() / 2); 106 107 gtk_menu_popup(m_popup.get(), 0, 0, reinterpret_cast<GtkMenuPositionFunc>(menuPositionFunction), this, 0, gtk_get_current_event_time()); 108 } 109 110 void PopupMenu::hide() 111 { 112 ASSERT(m_popup); 113 gtk_menu_popdown(m_popup.get()); 114 } 115 116 void PopupMenu::updateFromElement() 117 { 118 client()->setTextFromItem(client()->selectedIndex()); 119 } 120 121 bool PopupMenu::itemWritingDirectionIsNatural() 122 { 123 return true; 124 } 125 126 void PopupMenu::menuItemActivated(GtkMenuItem* item, PopupMenu* that) 127 { 128 ASSERT(that->client()); 129 ASSERT(that->m_indexMap.contains(GTK_WIDGET(item))); 130 that->client()->valueChanged(that->m_indexMap.get(GTK_WIDGET(item))); 131 } 132 133 void PopupMenu::menuUnmapped(GtkWidget*, PopupMenu* that) 134 { 135 ASSERT(that->client()); 136 that->client()->popupDidHide(); 137 } 138 139 void PopupMenu::menuPositionFunction(GtkMenu*, gint* x, gint* y, gboolean* pushIn, PopupMenu* that) 140 { 141 *x = that->m_menuPosition.x(); 142 *y = that->m_menuPosition.y(); 143 *pushIn = true; 144 } 145 146 void PopupMenu::menuRemoveItem(GtkWidget* widget, PopupMenu* that) 147 { 148 ASSERT(that->m_popup); 149 gtk_container_remove(GTK_CONTAINER(that->m_popup.get()), widget); 150 } 151 152 } 153 154