| 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@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 "NotImplemented.h" |
|---|
| 32 | #include "PlatformString.h" |
|---|
| 33 | #include <gtk/gtk.h> |
|---|
| 34 | |
|---|
| 35 | namespace WebCore { |
|---|
| 36 | |
|---|
| 37 | PopupMenu::PopupMenu(PopupMenuClient* client) |
|---|
| 38 | : m_popupClient(client) |
|---|
| 39 | , m_popup(0) |
|---|
| 40 | { |
|---|
| 41 | } |
|---|
| 42 | |
|---|
| 43 | PopupMenu::~PopupMenu() |
|---|
| 44 | { |
|---|
| 45 | if (m_popup) |
|---|
| 46 | g_object_unref(m_popup); |
|---|
| 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 | #if GLIB_CHECK_VERSION(2,10,0) |
|---|
| 56 | g_object_ref_sink(G_OBJECT(m_popup)); |
|---|
| 57 | #else |
|---|
| 58 | g_object_ref(G_OBJECT(m_popup)); |
|---|
| 59 | gtk_object_sink(GTK_OBJECT(m_popup)); |
|---|
| 60 | #endif |
|---|
| 61 | g_signal_connect(m_popup, "unmap", G_CALLBACK(menuUnmapped), this); |
|---|
| 62 | } else |
|---|
| 63 | gtk_container_foreach(GTK_CONTAINER(m_popup), reinterpret_cast<GtkCallback>(menuRemoveItem), this); |
|---|
| 64 | |
|---|
| 65 | int x, y; |
|---|
| 66 | gdk_window_get_origin(GTK_WIDGET(view->hostWindow()->platformWindow())->window, &x, &y); |
|---|
| 67 | m_menuPosition = view->contentsToWindow(rect.location()); |
|---|
| 68 | m_menuPosition = IntPoint(m_menuPosition.x() + x, m_menuPosition.y() + y + rect.height()); |
|---|
| 69 | m_indexMap.clear(); |
|---|
| 70 | |
|---|
| 71 | const int size = client()->listSize(); |
|---|
| 72 | for (int i = 0; i < size; ++i) { |
|---|
| 73 | GtkWidget* item; |
|---|
| 74 | if (client()->itemIsSeparator(i)) |
|---|
| 75 | item = gtk_separator_menu_item_new(); |
|---|
| 76 | else |
|---|
| 77 | item = gtk_menu_item_new_with_label(client()->itemText(i).utf8().data()); |
|---|
| 78 | |
|---|
| 79 | m_indexMap.add(item, i); |
|---|
| 80 | g_signal_connect(item, "activate", G_CALLBACK(menuItemActivated), this); |
|---|
| 81 | |
|---|
| 82 | // FIXME: Apply the PopupMenuStyle from client()->itemStyle(i) |
|---|
| 83 | gtk_widget_set_sensitive(item, client()->itemIsEnabled(i)); |
|---|
| 84 | gtk_menu_shell_append(GTK_MENU_SHELL(m_popup), item); |
|---|
| 85 | gtk_widget_show(item); |
|---|
| 86 | } |
|---|
| 87 | |
|---|
| 88 | gtk_menu_set_active(m_popup, index); |
|---|
| 89 | |
|---|
| 90 | |
|---|
| 91 | // The size calls are directly copied from gtkcombobox.c which is LGPL |
|---|
| 92 | GtkRequisition requisition; |
|---|
| 93 | gtk_widget_set_size_request(GTK_WIDGET(m_popup), -1, -1); |
|---|
| 94 | gtk_widget_size_request(GTK_WIDGET(m_popup), &requisition); |
|---|
| 95 | gtk_widget_set_size_request(GTK_WIDGET(m_popup), MAX(rect.width(), requisition.width), -1); |
|---|
| 96 | |
|---|
| 97 | GList* children = GTK_MENU_SHELL(m_popup)->children; |
|---|
| 98 | if (size) |
|---|
| 99 | for (int i = 0; i < size; i++) { |
|---|
| 100 | if (i > index) |
|---|
| 101 | break; |
|---|
| 102 | |
|---|
| 103 | GtkWidget* item = reinterpret_cast<GtkWidget*>(children->data); |
|---|
| 104 | GtkRequisition itemRequisition; |
|---|
| 105 | gtk_widget_get_child_requisition(item, &itemRequisition); |
|---|
| 106 | m_menuPosition.setY(m_menuPosition.y() - itemRequisition.height); |
|---|
| 107 | |
|---|
| 108 | children = g_list_next(children); |
|---|
| 109 | } |
|---|
| 110 | else |
|---|
| 111 | // Center vertically the empty popup in the combo box area |
|---|
| 112 | m_menuPosition.setY(m_menuPosition.y() - rect.height() / 2); |
|---|
| 113 | |
|---|
| 114 | gtk_menu_popup(m_popup, NULL, NULL, reinterpret_cast<GtkMenuPositionFunc>(menuPositionFunction), this, 0, gtk_get_current_event_time()); |
|---|
| 115 | } |
|---|
| 116 | |
|---|
| 117 | void PopupMenu::hide() |
|---|
| 118 | { |
|---|
| 119 | ASSERT(m_popup); |
|---|
| 120 | gtk_menu_popdown(m_popup); |
|---|
| 121 | } |
|---|
| 122 | |
|---|
| 123 | void PopupMenu::updateFromElement() |
|---|
| 124 | { |
|---|
| 125 | client()->setTextFromItem(client()->selectedIndex()); |
|---|
| 126 | } |
|---|
| 127 | |
|---|
| 128 | bool PopupMenu::itemWritingDirectionIsNatural() |
|---|
| 129 | { |
|---|
| 130 | return true; |
|---|
| 131 | } |
|---|
| 132 | |
|---|
| 133 | void PopupMenu::menuItemActivated(GtkMenuItem* item, PopupMenu* that) |
|---|
| 134 | { |
|---|
| 135 | ASSERT(that->client()); |
|---|
| 136 | ASSERT(that->m_indexMap.contains(GTK_WIDGET(item))); |
|---|
| 137 | that->client()->valueChanged(that->m_indexMap.get(GTK_WIDGET(item))); |
|---|
| 138 | } |
|---|
| 139 | |
|---|
| 140 | void PopupMenu::menuUnmapped(GtkWidget*, PopupMenu* that) |
|---|
| 141 | { |
|---|
| 142 | ASSERT(that->client()); |
|---|
| 143 | that->client()->hidePopup(); |
|---|
| 144 | } |
|---|
| 145 | |
|---|
| 146 | void PopupMenu::menuPositionFunction(GtkMenu*, gint* x, gint* y, gboolean* pushIn, PopupMenu* that) |
|---|
| 147 | { |
|---|
| 148 | *x = that->m_menuPosition.x(); |
|---|
| 149 | *y = that->m_menuPosition.y(); |
|---|
| 150 | *pushIn = true; |
|---|
| 151 | } |
|---|
| 152 | |
|---|
| 153 | void PopupMenu::menuRemoveItem(GtkWidget* widget, PopupMenu* that) |
|---|
| 154 | { |
|---|
| 155 | ASSERT(that->m_popup); |
|---|
| 156 | gtk_container_remove(GTK_CONTAINER(that->m_popup), widget); |
|---|
| 157 | } |
|---|
| 158 | |
|---|
| 159 | } |
|---|