Diff
Modified: trunk/Source/WebCore/ChangeLog (87929 => 87930)
--- trunk/Source/WebCore/ChangeLog 2011-06-02 17:50:48 UTC (rev 87929)
+++ trunk/Source/WebCore/ChangeLog 2011-06-02 18:01:23 UTC (rev 87930)
@@ -1,3 +1,42 @@
+2011-06-02 Carlos Garcia Campos <[email protected]>
+
+ Reviewed by Martin Robinson.
+
+ [GTK] Implement popup menus in Webkit2
+ https://bugs.webkit.org/show_bug.cgi?id=61854
+
+ Move common code into a new class to be used by both WebKit1 and
+ WebKit2.
+
+ * GNUmakefile.list.am: Add new files to compilation.
+ * platform/gtk/GtkPopupMenu.cpp: Copied from Source/WebCore/platform/gtk/PopupMenuGtk.cpp.
+ (WebCore::GtkPopupMenu::GtkPopupMenu):
+ (WebCore::GtkPopupMenu::~GtkPopupMenu):
+ (WebCore::GtkPopupMenu::clear): Remove all menu items.
+ (WebCore::GtkPopupMenu::appendSeparator): Add a new separator
+ item to the menu.
+ (WebCore::GtkPopupMenu::appendItem): Add a new item to the menu for
+ the given action.
+ (WebCore::GtkPopupMenu::popUp): Show the menu.
+ (WebCore::GtkPopupMenu::popDown): Hide the menu.
+ (WebCore::GtkPopupMenu::menuRemoveItem):
+ (WebCore::GtkPopupMenu::menuPositionFunction):
+ (WebCore::GtkPopupMenu::resetTypeAheadFindState):
+ (WebCore::GtkPopupMenu::typeAheadFind):
+ (WebCore::GtkPopupMenu::selectItemCallback):
+ (WebCore::GtkPopupMenu::keyPressEventCallback):
+ * platform/gtk/GtkPopupMenu.h: Copied from Source/WebCore/platform/gtk/PopupMenuGtk.h.
+ (WebCore::GtkPopupMenu::create):
+ (WebCore::GtkPopupMenu::platformMenu):
+ * platform/gtk/PopupMenuGtk.cpp:
+ (WebCore::PopupMenuGtk::PopupMenuGtk):
+ (WebCore::PopupMenuGtk::~PopupMenuGtk):
+ (WebCore::PopupMenuGtk::show): Use GtkPopupMenu.
+ (WebCore::PopupMenuGtk::hide): Ditto.
+ (WebCore::PopupMenuGtk::menuItemActivated):
+ (WebCore::PopupMenuGtk::menuUnmapped):
+ * platform/gtk/PopupMenuGtk.h:
+
2011-05-21 Dimitri Glazkov <[email protected]>
Reviewed by Darin Adler.
Modified: trunk/Source/WebCore/GNUmakefile.list.am (87929 => 87930)
--- trunk/Source/WebCore/GNUmakefile.list.am 2011-06-02 17:50:48 UTC (rev 87929)
+++ trunk/Source/WebCore/GNUmakefile.list.am 2011-06-02 18:01:23 UTC (rev 87930)
@@ -3796,6 +3796,8 @@
Source/WebCore/platform/gtk/GRefPtrGtk.h \
Source/WebCore/platform/gtk/GtkPluginWidget.cpp \
Source/WebCore/platform/gtk/GtkPluginWidget.h \
+ Source/WebCore/platform/gtk/GtkPopupMenu.cpp \
+ Source/WebCore/platform/gtk/GtkPopupMenu.h \
Source/WebCore/platform/gtk/GtkVersioning.c \
Source/WebCore/platform/gtk/GtkVersioning.h \
Source/WebCore/platform/gtk/KeyBindingTranslator.cpp \
Copied: trunk/Source/WebCore/platform/gtk/GtkPopupMenu.cpp (from rev 87929, trunk/Source/WebCore/platform/gtk/PopupMenuGtk.cpp) (0 => 87930)
--- trunk/Source/WebCore/platform/gtk/GtkPopupMenu.cpp (rev 0)
+++ trunk/Source/WebCore/platform/gtk/GtkPopupMenu.cpp 2011-06-02 18:01:23 UTC (rev 87930)
@@ -0,0 +1,239 @@
+/*
+ * This file is part of the popup menu implementation for <select> elements in WebCore.
+ *
+ * Copyright (C) 2006, 2007, 2008 Apple Inc. All rights reserved.
+ * Copyright (C) 2006 Michael Emmel [email protected]
+ * Copyright (C) 2008 Collabora Ltd.
+ * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+ * Copyright (C) 2010-2011 Igalia S.L.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "config.h"
+#include "GtkPopupMenu.h"
+
+#include "GOwnPtr.h"
+#include "GtkVersioning.h"
+#include <gtk/gtk.h>
+#include <wtf/text/CString.h>
+
+namespace WebCore {
+
+static const uint32_t gSearchTimeoutMs = 1000;
+
+GtkPopupMenu::GtkPopupMenu()
+ : m_popup(gtk_menu_new())
+ , m_previousKeyEventCharacter(0)
+ , m_currentlySelectedMenuItem(0)
+{
+ m_keyPressHandlerID = g_signal_connect(m_popup.get(), "key-press-event", G_CALLBACK(GtkPopupMenu::keyPressEventCallback), this);
+}
+
+GtkPopupMenu::~GtkPopupMenu()
+{
+ g_signal_handler_disconnect(m_popup.get(), m_keyPressHandlerID);
+}
+
+void GtkPopupMenu::clear()
+{
+ gtk_container_foreach(GTK_CONTAINER(m_popup.get()), reinterpret_cast<GtkCallback>(menuRemoveItem), this);
+}
+
+void GtkPopupMenu::appendSeparator()
+{
+ GtkWidget* menuItem = gtk_separator_menu_item_new();
+ gtk_menu_shell_append(GTK_MENU_SHELL(m_popup.get()), menuItem);
+ gtk_widget_show(menuItem);
+}
+
+void GtkPopupMenu::appendItem(GtkAction* action)
+{
+ GtkWidget* menuItem = gtk_action_create_menu_item(action);
+ g_signal_connect(menuItem, "select", G_CALLBACK(GtkPopupMenu::selectItemCallback), this);
+ gtk_menu_shell_append(GTK_MENU_SHELL(m_popup.get()), menuItem);
+ gtk_widget_show(menuItem);
+}
+
+void GtkPopupMenu::popUp(const IntSize& menuSize, const IntPoint& menuPosition, int itemCount, int selectedItem, const GdkEvent* event)
+{
+ resetTypeAheadFindState();
+ m_menuPosition = menuPosition;
+ gtk_menu_set_active(GTK_MENU(m_popup.get()), selectedItem);
+
+ // This approach follows the one in gtkcombobox.c.
+ GtkRequisition requisition;
+ gtk_widget_set_size_request(m_popup.get(), -1, -1);
+#ifdef GTK_API_VERSION_2
+ gtk_widget_size_request(m_popup.get(), &requisition);
+#else
+ gtk_widget_get_preferred_size(m_popup.get(), &requisition, 0);
+#endif
+
+ gtk_widget_set_size_request(m_popup.get(), std::max(menuSize.width(), requisition.width), -1);
+
+ GList* children = gtk_container_get_children(GTK_CONTAINER(m_popup.get()));
+ GList* p = children;
+ if (itemCount) {
+ for (int i = 0; i < itemCount; i++) {
+ if (i > selectedItem)
+ break;
+
+ GtkWidget* item = reinterpret_cast<GtkWidget*>(p->data);
+ GtkRequisition itemRequisition;
+#ifdef GTK_API_VERSION_2
+ gtk_widget_get_child_requisition(item, &itemRequisition);
+#else
+ gtk_widget_get_preferred_size(item, &itemRequisition, 0);
+#endif
+ m_menuPosition.setY(m_menuPosition.y() - itemRequisition.height);
+
+ p = g_list_next(p);
+ }
+ } else {
+ // Center vertically the empty popup in the combo box area.
+ m_menuPosition.setY(m_menuPosition.y() - menuSize.height() / 2);
+ }
+ g_list_free(children);
+
+ guint button;
+ guint32 activateTime;
+ if (event) {
+ button = event->type == GDK_BUTTON_PRESS ? event->button.button : 1;
+ activateTime = gdk_event_get_time(event);
+ } else {
+ button = 1;
+ activateTime = GDK_CURRENT_TIME;
+ }
+
+#ifdef GTK_API_VERSION_2
+ gtk_menu_popup(GTK_MENU(m_popup.get()), 0, 0, reinterpret_cast<GtkMenuPositionFunc>(menuPositionFunction), this, button, activateTime);
+#else
+ gtk_menu_popup_for_device(GTK_MENU(m_popup.get()), event ? gdk_event_get_device(event) : 0, 0, 0,
+ reinterpret_cast<GtkMenuPositionFunc>(menuPositionFunction), this, 0, button, activateTime);
+#endif
+}
+
+void GtkPopupMenu::popDown()
+{
+ gtk_menu_popdown(GTK_MENU(m_popup.get()));
+ resetTypeAheadFindState();
+}
+
+void GtkPopupMenu::menuRemoveItem(GtkWidget* widget, GtkPopupMenu* popupMenu)
+{
+ ASSERT(popupMenu->m_popup);
+ gtk_container_remove(GTK_CONTAINER(popupMenu->m_popup.get()), widget);
+}
+
+void GtkPopupMenu::menuPositionFunction(GtkMenu*, gint* x, gint* y, gboolean* pushIn, GtkPopupMenu* popupMenu)
+{
+ *x = popupMenu->m_menuPosition.x();
+ *y = popupMenu->m_menuPosition.y();
+ *pushIn = true;
+}
+
+void GtkPopupMenu::resetTypeAheadFindState()
+{
+ m_currentlySelectedMenuItem = 0;
+ m_previousKeyEventCharacter = 0;
+ m_currentSearchString = "";
+}
+
+bool GtkPopupMenu::typeAheadFind(GdkEventKey* event)
+{
+ // If we were given a non-printable character just skip it.
+ gunichar unicodeCharacter = gdk_keyval_to_unicode(event->keyval);
+ if (!g_unichar_isprint(unicodeCharacter)) {
+ resetTypeAheadFindState();
+ return false;
+ }
+
+ glong charactersWritten;
+ GOwnPtr<gunichar2> utf16String(g_ucs4_to_utf16(&unicodeCharacter, 1, 0, &charactersWritten, 0));
+ if (!utf16String) {
+ resetTypeAheadFindState();
+ return false;
+ }
+
+ // If the character is the same as the last character, the user is probably trying to
+ // cycle through the menulist entries. This matches the WebCore behavior for collapsed
+ // menulists.
+ bool repeatingCharacter = unicodeCharacter != m_previousKeyEventCharacter;
+ if (event->time - m_previousKeyEventTimestamp > gSearchTimeoutMs)
+ m_currentSearchString = String(static_cast<UChar*>(utf16String.get()), charactersWritten);
+ else if (repeatingCharacter)
+ m_currentSearchString.append(String(static_cast<UChar*>(utf16String.get()), charactersWritten));
+
+ m_previousKeyEventTimestamp = event->time;
+ m_previousKeyEventCharacter = unicodeCharacter;
+
+ // Like the Chromium port, we case fold before searching, because
+ // strncmp does not handle non-ASCII characters.
+ GOwnPtr<gchar> searchStringWithCaseFolded(g_utf8_casefold(m_currentSearchString.utf8().data(), -1));
+ size_t prefixLength = strlen(searchStringWithCaseFolded.get());
+
+ GList* children = gtk_container_get_children(GTK_CONTAINER(m_popup.get()));
+ if (!children)
+ return true;
+
+ // If a menu item has already been selected, start searching from the current
+ // item down the list. This will make multiple key presses of the same character
+ // advance the selection.
+ GList* currentChild = children;
+ if (m_currentlySelectedMenuItem) {
+ currentChild = g_list_find(children, m_currentlySelectedMenuItem);
+ if (!currentChild) {
+ m_currentlySelectedMenuItem = 0;
+ currentChild = children;
+ }
+
+ // Repeating characters should iterate.
+ if (repeatingCharacter) {
+ if (GList* nextChild = g_list_next(currentChild))
+ currentChild = nextChild;
+ }
+ }
+
+ GList* firstChild = currentChild;
+ do {
+ currentChild = g_list_next(currentChild);
+ if (!currentChild)
+ currentChild = children;
+
+ GOwnPtr<gchar> itemText(g_utf8_casefold(gtk_menu_item_get_label(GTK_MENU_ITEM(currentChild->data)), -1));
+ if (!strncmp(searchStringWithCaseFolded.get(), itemText.get(), prefixLength)) {
+ gtk_menu_shell_select_item(GTK_MENU_SHELL(m_popup.get()), GTK_WIDGET(currentChild->data));
+ break;
+ }
+ } while (currentChild != firstChild);
+
+ g_list_free(children);
+ return true;
+}
+
+void GtkPopupMenu::selectItemCallback(GtkMenuItem* item, GtkPopupMenu* popupMenu)
+{
+ popupMenu->m_currentlySelectedMenuItem = GTK_WIDGET(item);
+}
+
+gboolean GtkPopupMenu::keyPressEventCallback(GtkWidget* widget, GdkEventKey* event, GtkPopupMenu* popupMenu)
+{
+ return popupMenu->typeAheadFind(event);
+}
+
+} // namespace WebCore
Copied: trunk/Source/WebCore/platform/gtk/GtkPopupMenu.h (from rev 87929, trunk/Source/WebCore/platform/gtk/PopupMenuGtk.h) (0 => 87930)
--- trunk/Source/WebCore/platform/gtk/GtkPopupMenu.h (rev 0)
+++ trunk/Source/WebCore/platform/gtk/GtkPopupMenu.h 2011-06-02 18:01:23 UTC (rev 87930)
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+ * Copyright (C) 2011 Igalia S.L.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifndef GtkPopupMenu_h
+#define GtkPopupMenu_h
+
+#include "GRefPtrGtk.h"
+#include "IntPoint.h"
+#include "IntSize.h"
+#include "PlatformString.h"
+#include <wtf/FastAllocBase.h>
+#include <wtf/Noncopyable.h>
+#include <wtf/PassOwnPtr.h>
+
+typedef struct _GdkEventKey GdkEventKey;
+
+namespace WebCore {
+
+class GtkPopupMenu {
+ WTF_MAKE_NONCOPYABLE(GtkPopupMenu);
+ WTF_MAKE_FAST_ALLOCATED;
+
+public:
+ static PassOwnPtr<GtkPopupMenu> create()
+ {
+ return adoptPtr(new GtkPopupMenu());
+ }
+
+ ~GtkPopupMenu();
+
+ GtkWidget* platformMenu() const { return m_popup.get(); }
+ void clear();
+ void appendSeparator();
+ void appendItem(GtkAction*);
+ void popUp(const IntSize&, const IntPoint&, int itemsCount, int selectedItem, const GdkEvent*);
+ void popDown();
+
+private:
+ GtkPopupMenu();
+
+ void resetTypeAheadFindState();
+ bool typeAheadFind(GdkEventKey*);
+
+ static void menuItemActivated(GtkMenuItem*, GtkPopupMenu*);
+ static void menuPositionFunction(GtkMenu*, gint*, gint*, gboolean*, GtkPopupMenu*);
+ static void menuRemoveItem(GtkWidget*, GtkPopupMenu*);
+ static void selectItemCallback(GtkMenuItem*, GtkPopupMenu*);
+ static gboolean keyPressEventCallback(GtkWidget*, GdkEventKey*, GtkPopupMenu*);
+
+ GRefPtr<GtkWidget> m_popup;
+ IntPoint m_menuPosition;
+ String m_currentSearchString;
+ uint32_t m_previousKeyEventTimestamp;
+ unsigned int m_previousKeyEventCharacter;
+ GtkWidget* m_currentlySelectedMenuItem;
+ unsigned int m_keyPressHandlerID;
+};
+
+}
+
+#endif // GtkPopupMenu_h
Modified: trunk/Source/WebCore/platform/gtk/PopupMenuGtk.cpp (87929 => 87930)
--- trunk/Source/WebCore/platform/gtk/PopupMenuGtk.cpp 2011-06-02 17:50:48 UTC (rev 87929)
+++ trunk/Source/WebCore/platform/gtk/PopupMenuGtk.cpp 2011-06-02 18:01:23 UTC (rev 87930)
@@ -29,10 +29,7 @@
#include "FrameView.h"
#include "GOwnPtr.h"
-#include "GtkVersioning.h"
#include "HostWindow.h"
-#include "PlatformString.h"
-#include <gdk/gdk.h>
#include <gtk/gtk.h>
#include <wtf/text/CString.h>
@@ -42,102 +39,64 @@
PopupMenuGtk::PopupMenuGtk(PopupMenuClient* client)
: m_popupClient(client)
- , m_previousKeyEventCharacter(0)
- , m_currentlySelectedMenuItem(0)
{
}
PopupMenuGtk::~PopupMenuGtk()
{
if (m_popup) {
- g_signal_handlers_disconnect_matched(m_popup.get(), G_SIGNAL_MATCH_DATA, 0, 0, 0, 0, this);
+ g_signal_handlers_disconnect_matched(m_popup->platformMenu(), G_SIGNAL_MATCH_DATA, 0, 0, 0, 0, this);
hide();
}
}
+GtkAction* PopupMenuGtk::createGtkActionForMenuItem(int itemIndex)
+{
+ GOwnPtr<char> actionName(g_strdup_printf("popup-menu-action-%d", itemIndex));
+ GtkAction* action = "" client()->itemText(itemIndex).utf8().data(), 0, 0);
+ g_object_set_data(G_OBJECT(action), "popup-menu-action-index", GINT_TO_POINTER(itemIndex));
+ g_signal_connect(action, "activate", G_CALLBACK(menuItemActivated), this);
+ // FIXME: Apply the PopupMenuStyle from client()->itemStyle(i)
+ gtk_action_set_sensitive(action, client()->itemIsEnabled(itemIndex));
+
+ return action;
+}
+
void PopupMenuGtk::show(const IntRect& rect, FrameView* view, int index)
{
ASSERT(client());
if (!m_popup) {
- m_popup = GTK_MENU(gtk_menu_new());
- g_signal_connect(m_popup.get(), "unmap", G_CALLBACK(PopupMenuGtk::menuUnmapped), this);
- g_signal_connect(m_popup.get(), "key-press-event", G_CALLBACK(PopupMenuGtk::keyPressEventCallback), this);
+ m_popup = GtkPopupMenu::create();
+ g_signal_connect(m_popup->platformMenu(), "unmap", G_CALLBACK(PopupMenuGtk::menuUnmapped), this);
} else
- gtk_container_foreach(GTK_CONTAINER(m_popup.get()), reinterpret_cast<GtkCallback>(menuRemoveItem), this);
+ m_popup->clear();
- int x = 0;
- int y = 0;
- GdkWindow* window = gtk_widget_get_window(GTK_WIDGET(view->hostWindow()->platformPageClient()));
- if (window)
- gdk_window_get_origin(window, &x, &y);
- m_menuPosition = view->contentsToWindow(rect.location());
- m_menuPosition = IntPoint(m_menuPosition.x() + x, m_menuPosition.y() + y + rect.height());
- m_indexMap.clear();
-
const int size = client()->listSize();
for (int i = 0; i < size; ++i) {
- GtkWidget* item;
if (client()->itemIsSeparator(i))
- item = gtk_separator_menu_item_new();
- else
- item = gtk_menu_item_new_with_label(client()->itemText(i).utf8().data());
-
- m_indexMap.add(item, i);
- g_signal_connect(item, "activate", G_CALLBACK(PopupMenuGtk::menuItemActivated), this);
- g_signal_connect(item, "select", G_CALLBACK(PopupMenuGtk::selectItemCallback), this);
-
- // FIXME: Apply the PopupMenuStyle from client()->itemStyle(i)
- gtk_widget_set_sensitive(item, client()->itemIsEnabled(i));
- gtk_menu_shell_append(GTK_MENU_SHELL(m_popup.get()), item);
- gtk_widget_show(item);
- }
-
- gtk_menu_set_active(m_popup.get(), index);
-
-
- // The size calls are directly copied from gtkcombobox.c which is LGPL
- GtkRequisition requisition;
- gtk_widget_set_size_request(GTK_WIDGET(m_popup.get()), -1, -1);
-#ifdef GTK_API_VERSION_2
- gtk_widget_size_request(GTK_WIDGET(m_popup.get()), &requisition);
-#else
- gtk_widget_get_preferred_size(GTK_WIDGET(m_popup.get()), &requisition, 0);
-#endif
-
- gtk_widget_set_size_request(GTK_WIDGET(m_popup.get()), std::max(rect.width(), requisition.width), -1);
-
- GList* children = gtk_container_get_children(GTK_CONTAINER(m_popup.get()));
- GList* p = children;
- if (size) {
- for (int i = 0; i < size; i++) {
- if (i > index)
- break;
-
- GtkWidget* item = reinterpret_cast<GtkWidget*>(p->data);
- GtkRequisition itemRequisition;
-#ifdef GTK_API_VERSION_2
- gtk_widget_get_child_requisition(item, &itemRequisition);
-#else
- gtk_widget_get_preferred_size(item, &itemRequisition, 0);
-#endif
- m_menuPosition.setY(m_menuPosition.y() - itemRequisition.height);
-
- p = g_list_next(p);
+ m_popup->appendSeparator();
+ else {
+ GRefPtr<GtkAction> action = ""
+ m_popup->appendItem(action.get());
}
- } else {
- // Center vertically the empty popup in the combo box area
- m_menuPosition.setY(m_menuPosition.y() - rect.height() / 2);
}
- g_list_free(children);
- gtk_menu_popup(m_popup.get(), 0, 0, reinterpret_cast<GtkMenuPositionFunc>(menuPositionFunction), this, 0, gtk_get_current_event_time());
+ int x = 0;
+ int y = 0;
+ GdkWindow* window = gtk_widget_get_window(GTK_WIDGET(view->hostWindow()->platformPageClient()));
+ if (window)
+ gdk_window_get_origin(window, &x, &y);
+ IntPoint menuPosition(view->contentsToWindow(rect.location()));
+ menuPosition.move(x, y + rect.height());
+
+ m_popup->popUp(rect.size(), menuPosition, size, index, gtk_get_current_event());
}
void PopupMenuGtk::hide()
{
ASSERT(m_popup);
- gtk_menu_popdown(m_popup.get());
+ m_popup->popDown();
}
void PopupMenuGtk::updateFromElement()
@@ -150,121 +109,17 @@
m_popupClient = 0;
}
-bool PopupMenuGtk::typeAheadFind(GdkEventKey* event)
+void PopupMenuGtk::menuItemActivated(GtkAction* action, PopupMenuGtk* that)
{
- // If we were given a non-printable character just skip it.
- gunichar unicodeCharacter = gdk_keyval_to_unicode(event->keyval);
- if (!unicodeCharacter) {
- resetTypeAheadFindState();
- return false;
- }
-
- glong charactersWritten;
- GOwnPtr<gunichar2> utf16String(g_ucs4_to_utf16(&unicodeCharacter, 1, 0, &charactersWritten, 0));
- if (!utf16String) {
- resetTypeAheadFindState();
- return false;
- }
-
- // If the character is the same as the last character, the user is probably trying to
- // cycle through the menulist entries. This matches the WebCore behavior for collapsed
- // menulists.
- bool repeatingCharacter = unicodeCharacter != m_previousKeyEventCharacter;
- if (event->time - m_previousKeyEventTimestamp > gSearchTimeoutMs)
- m_currentSearchString = String(static_cast<UChar*>(utf16String.get()), charactersWritten);
- else if (repeatingCharacter)
- m_currentSearchString.append(String(static_cast<UChar*>(utf16String.get()), charactersWritten));
-
- m_previousKeyEventTimestamp = event->time;
- m_previousKeyEventCharacter = unicodeCharacter;
-
- // Like the Chromium port, we case fold before searching, because
- // strncmp does not handle non-ASCII characters.
- GOwnPtr<gchar> searchStringWithCaseFolded(g_utf8_casefold(m_currentSearchString.utf8().data(), -1));
- size_t prefixLength = strlen(searchStringWithCaseFolded.get());
-
- GList* children = gtk_container_get_children(GTK_CONTAINER(m_popup.get()));
- if (!children)
- return true;
-
- // If a menu item has already been selected, start searching from the current
- // item down the list. This will make multiple key presses of the same character
- // advance the selection.
- GList* currentChild = children;
- if (m_currentlySelectedMenuItem) {
- currentChild = g_list_find(children, m_currentlySelectedMenuItem);
- if (!currentChild) {
- m_currentlySelectedMenuItem = 0;
- currentChild = children;
- }
-
- // Repeating characters should iterate.
- if (repeatingCharacter) {
- if (GList* nextChild = g_list_next(currentChild))
- currentChild = nextChild;
- }
- }
-
- GList* firstChild = currentChild;
- do {
- currentChild = g_list_next(currentChild);
- if (!currentChild)
- currentChild = children;
-
- GOwnPtr<gchar> itemText(g_utf8_casefold(gtk_menu_item_get_label(GTK_MENU_ITEM(currentChild->data)), -1));
- if (!strncmp(searchStringWithCaseFolded.get(), itemText.get(), prefixLength)) {
- gtk_menu_shell_select_item(GTK_MENU_SHELL(m_popup.get()), GTK_WIDGET(currentChild->data));
- return true;
- }
- } while (currentChild != firstChild);
-
- return true;
-}
-
-void PopupMenuGtk::menuItemActivated(GtkMenuItem* item, PopupMenuGtk* that)
-{
ASSERT(that->client());
- ASSERT(that->m_indexMap.contains(GTK_WIDGET(item)));
- that->client()->valueChanged(that->m_indexMap.get(GTK_WIDGET(item)));
+ that->client()->valueChanged(GPOINTER_TO_INT(g_object_get_data(G_OBJECT(action), "popup-menu-action-index")));
}
void PopupMenuGtk::menuUnmapped(GtkWidget*, PopupMenuGtk* that)
{
ASSERT(that->client());
- that->resetTypeAheadFindState();
that->client()->popupDidHide();
}
-void PopupMenuGtk::menuPositionFunction(GtkMenu*, gint* x, gint* y, gboolean* pushIn, PopupMenuGtk* that)
-{
- *x = that->m_menuPosition.x();
- *y = that->m_menuPosition.y();
- *pushIn = true;
}
-void PopupMenuGtk::resetTypeAheadFindState()
-{
- m_currentlySelectedMenuItem = 0;
- m_previousKeyEventCharacter = 0;
- m_currentSearchString = "";
-}
-
-void PopupMenuGtk::menuRemoveItem(GtkWidget* widget, PopupMenuGtk* that)
-{
- ASSERT(that->m_popup);
- gtk_container_remove(GTK_CONTAINER(that->m_popup.get()), widget);
-}
-
-int PopupMenuGtk::selectItemCallback(GtkMenuItem* item, PopupMenuGtk* that)
-{
- that->m_currentlySelectedMenuItem = GTK_WIDGET(item);
- return FALSE;
-}
-
-int PopupMenuGtk::keyPressEventCallback(GtkWidget* widget, GdkEventKey* event, PopupMenuGtk* that)
-{
- return that->typeAheadFind(event);
-}
-
-}
-
Modified: trunk/Source/WebCore/platform/gtk/PopupMenuGtk.h (87929 => 87930)
--- trunk/Source/WebCore/platform/gtk/PopupMenuGtk.h 2011-06-02 17:50:48 UTC (rev 87929)
+++ trunk/Source/WebCore/platform/gtk/PopupMenuGtk.h 2011-06-02 18:01:23 UTC (rev 87930)
@@ -20,16 +20,11 @@
#ifndef PopupMenuGtk_h
#define PopupMenuGtk_h
-#include "GRefPtrGtk.h"
+#include "GtkPopupMenu.h"
#include "IntRect.h"
#include "PopupMenu.h"
#include "PopupMenuClient.h"
-#include <wtf/HashMap.h>
-#include <wtf/PassRefPtr.h>
-#include <wtf/RefCounted.h>
-typedef struct _GdkEventKey GdkEventKey;
-
namespace WebCore {
class FrameView;
@@ -44,27 +39,16 @@
virtual void hide();
virtual void updateFromElement();
virtual void disconnectClient();
- bool typeAheadFind(GdkEventKey*);
private:
PopupMenuClient* client() const { return m_popupClient; }
- void resetTypeAheadFindState();
+ GtkAction* createGtkActionForMenuItem(int itemIndex);
- static void menuItemActivated(GtkMenuItem* item, PopupMenuGtk*);
static void menuUnmapped(GtkWidget*, PopupMenuGtk*);
- static void menuPositionFunction(GtkMenu*, gint*, gint*, gboolean*, PopupMenuGtk*);
- static void menuRemoveItem(GtkWidget*, PopupMenuGtk*);
- static int selectItemCallback(GtkMenuItem*, PopupMenuGtk*);
- static int keyPressEventCallback(GtkWidget*, GdkEventKey*, PopupMenuGtk*);
+ static void menuItemActivated(GtkAction*, PopupMenuGtk*);
PopupMenuClient* m_popupClient;
- IntPoint m_menuPosition;
- GRefPtr<GtkMenu> m_popup;
- HashMap<GtkWidget*, int> m_indexMap;
- String m_currentSearchString;
- uint32_t m_previousKeyEventTimestamp;
- unsigned int m_previousKeyEventCharacter;
- GtkWidget* m_currentlySelectedMenuItem;
+ OwnPtr<GtkPopupMenu> m_popup;
};
}
Modified: trunk/Source/WebKit2/ChangeLog (87929 => 87930)
--- trunk/Source/WebKit2/ChangeLog 2011-06-02 17:50:48 UTC (rev 87929)
+++ trunk/Source/WebKit2/ChangeLog 2011-06-02 18:01:23 UTC (rev 87930)
@@ -1,3 +1,26 @@
+2011-06-02 Carlos Garcia Campos <[email protected]>
+
+ Reviewed by Martin Robinson.
+
+ [GTK] Implement popup menus in Webkit2
+ https://bugs.webkit.org/show_bug.cgi?id=61854
+
+ * GNUmakefile.am: Add new files to compilation.
+ * UIProcess/API/gtk/PageClientImpl.cpp:
+ (WebKit::PageClientImpl::createPopupMenuProxy): Create a new
+ WebPopupMenuProxy.
+ * UIProcess/gtk/WebPopupMenuProxyGtk.cpp: Added.
+ (WebKit::WebPopupMenuProxyGtk::WebPopupMenuProxyGtk):
+ (WebKit::WebPopupMenuProxyGtk::~WebPopupMenuProxyGtk):
+ (WebKit::WebPopupMenuProxyGtk::showPopupMenu):
+ (WebKit::WebPopupMenuProxyGtk::hidePopupMenu):
+ (WebKit::WebPopupMenuProxyGtk::shutdownRunLoop):
+ (WebKit::WebPopupMenuProxyGtk::menuItemActivated):
+ (WebKit::WebPopupMenuProxyGtk::menuUnmapped):
+ * UIProcess/gtk/WebPopupMenuProxyGtk.h: Added.
+ (WebKit::WebPopupMenuProxyGtk::create):
+ (WebKit::WebPopupMenuProxyGtk::setActiveItem):
+
2011-06-01 Sam Weinig <[email protected]>
Reviewed by Anders Carlsson.
Modified: trunk/Source/WebKit2/GNUmakefile.am (87929 => 87930)
--- trunk/Source/WebKit2/GNUmakefile.am 2011-06-02 17:50:48 UTC (rev 87929)
+++ trunk/Source/WebKit2/GNUmakefile.am 2011-06-02 18:01:23 UTC (rev 87930)
@@ -394,6 +394,8 @@
Source/WebKit2/UIProcess/gtk/WebFullScreenManagerProxyGtk.cpp \
Source/WebKit2/UIProcess/gtk/WebInspectorGtk.cpp \
Source/WebKit2/UIProcess/gtk/WebPageProxyGtk.cpp \
+ Source/WebKit2/UIProcess/gtk/WebPopupMenuProxyGtk.cpp \
+ Source/WebKit2/UIProcess/gtk/WebPopupMenuProxyGtk.h \
Source/WebKit2/UIProcess/gtk/WebPreferencesGtk.cpp \
Source/WebKit2/UIProcess/Launcher/gtk/ProcessLauncherGtk.cpp \
Source/WebKit2/UIProcess/Launcher/gtk/ThreadLauncherGtk.cpp \
Modified: trunk/Source/WebKit2/UIProcess/API/gtk/PageClientImpl.cpp (87929 => 87930)
--- trunk/Source/WebKit2/UIProcess/API/gtk/PageClientImpl.cpp 2011-06-02 17:50:48 UTC (rev 87929)
+++ trunk/Source/WebKit2/UIProcess/API/gtk/PageClientImpl.cpp 2011-06-02 18:01:23 UTC (rev 87930)
@@ -37,6 +37,7 @@
#include "WebEventFactory.h"
#include "WebKitWebViewBasePrivate.h"
#include "WebPageProxy.h"
+#include "WebPopupMenuProxyGtk.h"
#include <WebCore/GtkUtilities.h>
#include <wtf/text/WTFString.h>
@@ -204,10 +205,9 @@
notImplemented();
}
-PassRefPtr<WebPopupMenuProxy> PageClientImpl::createPopupMenuProxy(WebPageProxy*)
+PassRefPtr<WebPopupMenuProxy> PageClientImpl::createPopupMenuProxy(WebPageProxy* page)
{
- notImplemented();
- return 0;
+ return WebPopupMenuProxyGtk::create(m_viewWidget, page);
}
PassRefPtr<WebContextMenuProxy> PageClientImpl::createContextMenuProxy(WebPageProxy*)
Added: trunk/Source/WebKit2/UIProcess/gtk/WebPopupMenuProxyGtk.cpp (0 => 87930)
--- trunk/Source/WebKit2/UIProcess/gtk/WebPopupMenuProxyGtk.cpp (rev 0)
+++ trunk/Source/WebKit2/UIProcess/gtk/WebPopupMenuProxyGtk.cpp 2011-06-02 18:01:23 UTC (rev 87930)
@@ -0,0 +1,133 @@
+/*
+ * Copyright (C) 2011 Igalia S.L.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "WebPopupMenuProxyGtk.h"
+
+#include "NativeWebMouseEvent.h"
+#include "WebPopupItem.h"
+#include <gtk/gtk.h>
+#include <wtf/gobject/GOwnPtr.h>
+#include <wtf/text/CString.h>
+
+using namespace WebCore;
+
+namespace WebKit {
+
+WebPopupMenuProxyGtk::WebPopupMenuProxyGtk(GtkWidget* webView, WebPopupMenuProxy::Client* client)
+ : WebPopupMenuProxy(client)
+ , m_webView(webView)
+ , m_activeItem(-1)
+ , m_runLoop(0)
+{
+}
+
+WebPopupMenuProxyGtk::~WebPopupMenuProxyGtk()
+{
+ if (m_popup) {
+ g_signal_handlers_disconnect_matched(m_popup->platformMenu(), G_SIGNAL_MATCH_DATA, 0, 0, 0, 0, this);
+ hidePopupMenu();
+ }
+}
+
+GtkAction* WebPopupMenuProxyGtk::createGtkActionForMenuItem(const WebPopupItem& item, int itemIndex)
+{
+ GOwnPtr<char> actionName(g_strdup_printf("popup-menu-action-%d", itemIndex));
+ GtkAction* action = "" item.m_text.utf8().data(), item.m_toolTip.utf8().data(), 0);
+ g_object_set_data(G_OBJECT(action), "popup-menu-action-index", GINT_TO_POINTER(itemIndex));
+ g_signal_connect(action, "activate", G_CALLBACK(menuItemActivated), this);
+ gtk_action_set_sensitive(action, item.m_isEnabled);
+
+ return action;
+}
+
+void WebPopupMenuProxyGtk::showPopupMenu(const IntRect& rect, TextDirection textDirection, double scaleFactor, const Vector<WebPopupItem>& items, const PlatformPopupMenuData& data, int32_t selectedIndex)
+{
+ if (m_popup)
+ m_popup->clear();
+ else
+ m_popup = GtkPopupMenu::create();
+
+ const int size = items.size();
+ for (int i = 0; i < size; i++) {
+ if (items[i].m_type == WebPopupItem::Separator)
+ m_popup->appendSeparator();
+ else {
+ GRefPtr<GtkAction> action = "" i));
+ m_popup->appendItem(action.get());
+ }
+ }
+
+ int x = 0;
+ int y = 0;
+ if (GdkWindow* window = gtk_widget_get_window(m_webView))
+ gdk_window_get_origin(window, &x, &y);
+ IntPoint menuPosition(rect.x() + x, rect.y() + y + rect.height());
+
+ gulong unmapHandler = g_signal_connect(m_popup->platformMenu(), "unmap", G_CALLBACK(menuUnmapped), this);
+ m_popup->popUp(rect.size(), menuPosition, size, selectedIndex, m_client->currentlyProcessedMouseDownEvent() ? m_client->currentlyProcessedMouseDownEvent()->nativeEvent() : 0);
+
+ // WebPageProxy expects the menu to run in a nested run loop, since it invalidates the
+ // menu right after calling WebPopupMenuProxy::showPopupMenu().
+ m_runLoop = g_main_loop_new(0, FALSE);
+
+ GDK_THREADS_LEAVE();
+ g_main_loop_run(m_runLoop);
+ GDK_THREADS_ENTER();
+
+ g_main_loop_unref(m_runLoop);
+ m_runLoop = 0;
+
+ g_signal_handler_disconnect(m_popup->platformMenu(), unmapHandler);
+
+ if (!m_client)
+ return;
+
+ m_client->valueChangedForPopupMenu(this, m_activeItem);
+}
+
+void WebPopupMenuProxyGtk::hidePopupMenu()
+{
+ m_popup->popDown();
+}
+
+void WebPopupMenuProxyGtk::shutdownRunLoop()
+{
+ if (g_main_loop_is_running(m_runLoop))
+ g_main_loop_quit(m_runLoop);
+}
+
+void WebPopupMenuProxyGtk::menuItemActivated(GtkAction* action, WebPopupMenuProxyGtk* popupMenu)
+{
+ popupMenu->setActiveItem(GPOINTER_TO_INT(g_object_get_data(G_OBJECT(action), "popup-menu-action-index")));
+ popupMenu->shutdownRunLoop();
+}
+
+void WebPopupMenuProxyGtk::menuUnmapped(GtkWidget*, WebPopupMenuProxyGtk* popupMenu)
+{
+ popupMenu->shutdownRunLoop();
+}
+
+} // namespace WebKit
Added: trunk/Source/WebKit2/UIProcess/gtk/WebPopupMenuProxyGtk.h (0 => 87930)
--- trunk/Source/WebKit2/UIProcess/gtk/WebPopupMenuProxyGtk.h (rev 0)
+++ trunk/Source/WebKit2/UIProcess/gtk/WebPopupMenuProxyGtk.h 2011-06-02 18:01:23 UTC (rev 87930)
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2011 Igalia S.L.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifndef WebPopupMenuProxyGtk_h
+#define WebPopupMenuProxyGtk_h
+
+#include "WebPopupMenuProxy.h"
+#include <WebCore/GtkPopupMenu.h>
+#include <WebCore/IntRect.h>
+
+typedef struct _GMainLoop GMainLoop;
+
+namespace WebKit {
+
+class WebPageProxy;
+
+class WebPopupMenuProxyGtk : public WebPopupMenuProxy {
+public:
+ static PassRefPtr<WebPopupMenuProxyGtk> create(GtkWidget* webView, WebPopupMenuProxy::Client* client)
+ {
+ return adoptRef(new WebPopupMenuProxyGtk(webView, client));
+ }
+ ~WebPopupMenuProxyGtk();
+
+ virtual void showPopupMenu(const WebCore::IntRect&, WebCore::TextDirection, double scaleFactor, const Vector<WebPopupItem>&, const PlatformPopupMenuData&, int32_t selectedIndex);
+ virtual void hidePopupMenu();
+
+private:
+ WebPopupMenuProxyGtk(GtkWidget*, WebPopupMenuProxy::Client*);
+ void shutdownRunLoop();
+ void setActiveItem(int activeItem) { m_activeItem = activeItem; }
+ GtkAction* createGtkActionForMenuItem(const WebPopupItem&, int itemIndex);
+
+ static void menuItemActivated(GtkAction*, WebPopupMenuProxyGtk*);
+ static void menuUnmapped(GtkWidget*, WebPopupMenuProxyGtk*);
+
+ GtkWidget* m_webView;
+ OwnPtr<WebCore::GtkPopupMenu> m_popup;
+ int m_activeItem;
+ GMainLoop* m_runLoop;
+};
+
+} // namespace WebKit
+
+
+#endif // WebPopupMenuProxyGtk_h