2008-01-02 Luca Bruno <lethalman88@gmail.com>
authoralp@webkit.org <alp@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 2 Jan 2008 10:25:57 +0000 (10:25 +0000)
committeralp@webkit.org <alp@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 2 Jan 2008 10:25:57 +0000 (10:25 +0000)
        Reviewed by Alp Toker.

        http://bugs.webkit.org/show_bug.cgi?id=16115
        [GTK] ContextMenu and ContextMenuItem lacks an implementation

        Add context menu support.

        Based on a patch by Holger Freyther.

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@29080 268f45cc-cd09-0410-ab3c-d52691b4dbfc

WebCore/ChangeLog
WebCore/platform/gtk/ContextMenuGtk.cpp
WebCore/platform/gtk/ContextMenuItemGtk.cpp
WebKit/gtk/ChangeLog
WebKit/gtk/WebCoreSupport/ContextMenuClientGtk.cpp
WebKit/gtk/WebView/webkitprivate.h
WebKit/gtk/WebView/webkitwebview.cpp

index 32d1db237f8b8511a6e0bb73eabd0e6d812bf1d6..aa9c31155bf7bb986911ad31db38383b12396e36 100644 (file)
@@ -1,3 +1,33 @@
+2008-01-02  Luca Bruno  <lethalman88@gmail.com>
+
+        Reviewed by Alp Toker.
+
+        http://bugs.webkit.org/show_bug.cgi?id=16115
+        [GTK] ContextMenu and ContextMenuItem lacks an implementation
+
+        Add context menu support.
+
+        Based on a patch by Holger Freyther.
+
+        * platform/gtk/ContextMenuGtk.cpp:
+        (WebCore::menuItemActivated):
+        (WebCore::ContextMenu::ContextMenu):
+        (WebCore::ContextMenu::~ContextMenu):
+        (WebCore::ContextMenu::appendItem):
+        (WebCore::ContextMenu::setPlatformDescription):
+        (WebCore::ContextMenu::releasePlatformDescription):
+        * platform/gtk/ContextMenuItemGtk.cpp:
+        (WebCore::ContextMenuItem::ContextMenuItem):
+        (WebCore::ContextMenuItem::~ContextMenuItem):
+        (WebCore::ContextMenuItem::releasePlatformDescription):
+        (WebCore::ContextMenuItem::type):
+        (WebCore::ContextMenuItem::action):
+        (WebCore::ContextMenuItem::setAction):
+        (WebCore::ContextMenuItem::platformSubMenu):
+        (WebCore::ContextMenuItem::setSubMenu):
+        (WebCore::ContextMenuItem::setChecked):
+        (WebCore::ContextMenuItem::setEnabled):
+
 2008-01-02  Alp Toker  <alp@atoker.com>
 
         GTK+ autotools build fix. Track changes in r29073.
index 2cb4ce61d15e75995826873b1a382080d6b5bde4..f2eabe724c43d26691781170654839024215d562 100644 (file)
@@ -1,4 +1,6 @@
 /*
+ *  Copyright (C) 2007 Holger Hans Peter Freyther
+ *
  *  This library is free software; you can redistribute it and/or
  *  modify it under the terms of the GNU Lesser General Public
  *  License as published by the Free Software Foundation; either
 #include "config.h"
 #include "ContextMenu.h"
 
-#include "NotImplemented.h"
+#include "ContextMenuController.h"
+
+#include <gtk/gtk.h>
 
 namespace WebCore {
 
+// TODO: On-the-fly menu item creation and ref-counting correctness checking.
+// See http://bugs.webkit.org/show_bug.cgi?id=16115
+
+static void menuItemActivated(GtkMenuItem* item, ContextMenu* menu)
+{
+    ContextMenuItem contextItem(item);
+    menu->controller()->contextMenuItemSelected(&contextItem);
+}
+
 ContextMenu::ContextMenu(const HitTestResult& result)
     : m_hitTestResult(result)
-    , m_platformDescription(0)
 {
-    notImplemented();
+    m_platformDescription = GTK_MENU(gtk_menu_new());
+
+#if GLIB_CHECK_VERSION(2,10,0)
+    g_object_ref_sink(G_OBJECT(m_platformDescription));
+#else
+    g_object_ref(G_OBJECT(m_platformDescription));
+    gtk_object_sink(GTK_OBJECT(m_platformDescription));
+#endif
 }
 
 ContextMenu::~ContextMenu()
 {
-    notImplemented();
+  if (m_platformDescription)
+      g_object_unref(m_platformDescription);
 }
 
-void ContextMenu::appendItem(ContextMenuItem&)
+void ContextMenu::appendItem(ContextMenuItem& item)
 {
-    notImplemented();
+    ASSERT(m_platformDescription);
+    checkOrEnableIfNeeded(item);
+
+    GtkMenuItem* platformItem = item.releasePlatformDescription();
+    ASSERT(platformItem);
+
+    g_signal_connect(platformItem, "activate", G_CALLBACK(menuItemActivated), this);
+    gtk_menu_shell_append(GTK_MENU_SHELL(m_platformDescription), GTK_WIDGET(platformItem));
+    gtk_widget_show(GTK_WIDGET(platformItem));
+    g_object_unref(platformItem);
 }
 
 void ContextMenu::setPlatformDescription(PlatformMenuDescription menu)
 {
+    ASSERT(menu);
+    if (m_platformDescription)
+        g_object_unref(m_platformDescription);
+
     m_platformDescription = menu;
+    g_object_ref(m_platformDescription);
 }
 
 PlatformMenuDescription ContextMenu::platformDescription() const
@@ -50,8 +84,10 @@ PlatformMenuDescription ContextMenu::platformDescription() const
 
 PlatformMenuDescription ContextMenu::releasePlatformDescription()
 {
-    notImplemented(); 
-    return 0;
+    PlatformMenuDescription description = m_platformDescription;
+    m_platformDescription = 0;
+
+    return description;
 }
 
 }
index a8e6461bcb6c5dfd801b29f19b6e085b8c5dce71..6c494d698a312f298c19be2a6f436ac3f9297152 100644 (file)
@@ -1,4 +1,6 @@
 /*
+ *  Copyright (C) 2007 Holger Hans Peter Freyther
+ *
  *  This library is free software; you can redistribute it and/or
  *  modify it under the terms of the GNU Lesser General Public
  *  License as published by the Free Software Foundation; either
  */
 
 #include "config.h"
+#include "ContextMenu.h"
 #include "ContextMenuItem.h"
-
+#include "CString.h"
 #include "NotImplemented.h"
 
+#include <gtk/gtk.h>
+
+#define WEBKIT_CONTEXT_MENU_ACTION "webkit-context-menu"
+
 namespace WebCore {
 
-ContextMenuItem::ContextMenuItem(PlatformMenuItemDescription)
+ContextMenuItem::ContextMenuItem(PlatformMenuItemDescription item)
+    : m_platformDescription(item)
 {
-    notImplemented();
+    g_object_ref(m_platformDescription);
 }
 
 ContextMenuItem::ContextMenuItem(ContextMenu*)
@@ -31,26 +39,48 @@ ContextMenuItem::ContextMenuItem(ContextMenu*)
     notImplemented();
 }
 
-ContextMenuItem::ContextMenuItem(ContextMenuItemType, ContextMenuAction, const String&, ContextMenu*)
+ContextMenuItem::ContextMenuItem(ContextMenuItemType type, ContextMenuAction action, const String& title, ContextMenu* subMenu)
+    : m_platformDescription(0)
 {
-    notImplemented();
+    if (type == SeparatorType) {
+        m_platformDescription = GTK_MENU_ITEM(gtk_separator_menu_item_new());
+    } else {
+        m_platformDescription = GTK_MENU_ITEM(gtk_menu_item_new_with_label(title.utf8().data()));
+        setAction(action);
+
+        if (subMenu)
+            setSubMenu(subMenu);
+    }
+
+#if GLIB_CHECK_VERSION(2,10,0)
+    g_object_ref_sink(G_OBJECT(m_platformDescription));
+#else
+    g_object_ref(G_OBJECT(m_platformDescription));
+    gtk_object_sink(GTK_OBJECT(m_platformDescription));
+#endif
 }
 
 ContextMenuItem::~ContextMenuItem()
 {
-    notImplemented();
+    if (m_platformDescription)
+        g_object_unref(m_platformDescription);
 }
 
 PlatformMenuItemDescription ContextMenuItem::releasePlatformDescription()
 {
-    notImplemented();
-    return m_platformDescription;
+    PlatformMenuItemDescription menuItem = m_platformDescription;
+    m_platformDescription = 0;
+    return menuItem;
 }
 
 ContextMenuItemType ContextMenuItem::type() const
 {
-    notImplemented();
-    return ActionType; 
+    if (GTK_IS_SEPARATOR_MENU_ITEM(m_platformDescription))
+        return SeparatorType;
+    if (gtk_menu_item_get_submenu(m_platformDescription))
+        return SubmenuType;
+
+    return ActionType;
 }
 
 void ContextMenuItem::setType(ContextMenuItemType)
@@ -60,13 +90,19 @@ void ContextMenuItem::setType(ContextMenuItemType)
 
 ContextMenuAction ContextMenuItem::action() const
 {
-    notImplemented();
-    return ContextMenuItemTagNoAction;
+    ASSERT(m_platformDescription);
+    
+    return *static_cast<ContextMenuAction*>(g_object_get_data(G_OBJECT(m_platformDescription), WEBKIT_CONTEXT_MENU_ACTION));
 }
 
-void ContextMenuItem::setAction(ContextMenuAction)
+void ContextMenuItem::setAction(ContextMenuAction action)
 {
-    notImplemented();
+    ASSERT(m_platformDescription);
+
+    // FIXME: Check the memory management of menuAction.
+    ContextMenuAction* menuAction = static_cast<ContextMenuAction*>(malloc(sizeof(ContextMenuAction*)));
+    *menuAction = action;
+    g_object_set_data(G_OBJECT(m_platformDescription), WEBKIT_CONTEXT_MENU_ACTION, menuAction);
 }
 
 String ContextMenuItem::title() const
@@ -82,23 +118,29 @@ void ContextMenuItem::setTitle(const String&)
 
 PlatformMenuDescription ContextMenuItem::platformSubMenu() const
 {
-    notImplemented();
-    return 0;
+    return GTK_MENU(gtk_menu_item_get_submenu(m_platformDescription));
 }
 
-void ContextMenuItem::setSubMenu(ContextMenu*)
+void ContextMenuItem::setSubMenu(ContextMenu* menu)
 {
-    notImplemented();
+    gtk_menu_item_set_submenu(m_platformDescription, GTK_WIDGET(menu->releasePlatformDescription()));
 }
 
-void ContextMenuItem::setChecked(bool)
+void ContextMenuItem::setChecked(bool shouldCheck)
 {
-    notImplemented();
+    ASSERT(type() == ActionType);
+
+    if (GTK_IS_CHECK_MENU_ITEM(m_platformDescription)) {
+        gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(m_platformDescription), shouldCheck);
+    } else {
+        // TODO: Need to morph this into a GtkCheckMenuItem.
+        notImplemented();
+    }
 }
 
-void ContextMenuItem::setEnabled(bool)
+void ContextMenuItem::setEnabled(bool shouldEnable)
 {
-    notImplemented();
+    gtk_widget_set_sensitive(GTK_WIDGET(m_platformDescription), shouldEnable);
 }
 
 }
index abb2e6e1408da7fee2f4750e92303b11cd31437a..661ba4246be0839e4bd359825ae4c5f237088f04 100644 (file)
@@ -1,3 +1,19 @@
+2008-01-02  Luca Bruno  <lethalman88@gmail.com>
+
+        Reviewed by Alp Toker.
+
+        http://bugs.webkit.org/show_bug.cgi?id=16115
+        [GTK] ContextMenu and ContextMenuItem lacks an implementation
+
+        Add context menu support.
+
+        Based on a patch by Holger Freyther.
+
+        * WebCoreSupport/ContextMenuClientGtk.cpp:
+        (WebKit::ContextMenuClient::getCustomMenuFromDefaultItems):
+        * WebView/webkitprivate.h:
+        * WebView/webkitwebview.cpp:
+
 2007-12-29  Jan Michael Alonzo  <jmalonzo@unpluggable.com>
 
         Reviewed by Alp Toker.
index 9f8cf7dbbf2bc1cbe7d34c9a7b7161877fd7c1f3..6a2feb28ebacb3f42f2437dc4cca04bf40b9fa80 100644 (file)
@@ -15,6 +15,7 @@
  */
 
 #include "config.h"
+#include "ContextMenu.h"
 #include "ContextMenuClientGtk.h"
 
 #include "HitTestResult.h"
@@ -30,10 +31,9 @@ void ContextMenuClient::contextMenuDestroyed()
     delete this;
 }
 
-PlatformMenuDescription ContextMenuClient::getCustomMenuFromDefaultItems(ContextMenu*)
+PlatformMenuDescription ContextMenuClient::getCustomMenuFromDefaultItems(ContextMenu* menu)
 {
-    notImplemented();
-    return PlatformMenuDescription();
+    return menu->releasePlatformDescription();
 }
 
 void ContextMenuClient::contextMenuItemSelected(ContextMenuItem*, const ContextMenu*)
index e0a08f33358270d2bf226bffb9f39e5661b86763..14328bc24de1c581640ca3ebbf7a64e16beb6854 100644 (file)
@@ -61,6 +61,9 @@ extern "C" {
         WebCore::String applicationNameForUserAgent;
         WebCore::String* userAgent;
 
+        gint lastPopupXPosition;
+        gint lastPopupYPosition;
+
         HashSet<GtkWidget*> children;
         bool editable;
         GtkIMContext* imContext;
index 77bfbc447d4b74cb5deb75d8b00b32cc41ae1f98..5e26e3c829c7a5f419136be691aebdfa7ea180fa 100644 (file)
 #include "NotImplemented.h"
 #include "CString.h"
 #include "ChromeClientGtk.h"
+#include "ContextMenu.h"
 #include "ContextMenuClientGtk.h"
+#include "ContextMenuController.h"
+#include "Cursor.h"
 #include "DragClientGtk.h"
 #include "Editor.h"
 #include "EditorClientGtk.h"
@@ -88,6 +91,92 @@ static guint webkit_web_view_signals[LAST_SIGNAL] = { 0, };
 
 G_DEFINE_TYPE(WebKitWebView, webkit_web_view, GTK_TYPE_CONTAINER)
 
+static void webkit_web_view_context_menu_position_func(GtkMenu*, gint* x, gint* y, gboolean* pushIn, WebKitWebViewPrivate* data)
+{
+    *pushIn = FALSE;
+    *x = data->lastPopupXPosition;
+    *y = data->lastPopupYPosition;
+}
+
+static gboolean webkit_web_view_forward_context_menu_event(WebKitWebView* webView, const PlatformMouseEvent& event)
+{
+    Page* page = core(webView);
+    page->contextMenuController()->clearContextMenu();
+    Frame* focusedFrame = page->focusController()->focusedOrMainFrame();
+    focusedFrame->view()->setCursor(pointerCursor());
+    bool handledEvent = focusedFrame->eventHandler()->sendContextMenuEvent(event);
+    if (!handledEvent)
+        return FALSE;
+
+    ContextMenu* coreMenu = page->contextMenuController()->contextMenu();
+    if (!coreMenu)
+        return FALSE;
+
+    if (!coreMenu->platformDescription())
+        return FALSE;
+
+
+    WebKitWebViewPrivate* pageData = WEBKIT_WEB_VIEW_GET_PRIVATE(webView);
+    pageData->lastPopupXPosition = event.globalX();
+    pageData->lastPopupYPosition = event.globalY();
+    gtk_menu_popup(GTK_MENU(coreMenu->platformDescription()), NULL, NULL,
+                   reinterpret_cast<GtkMenuPositionFunc>(webkit_web_view_context_menu_position_func), pageData,
+                   event.button() + 1, gtk_get_current_event_time());
+    return TRUE;
+}
+
+static gboolean webkit_web_view_popup_menu_handler(GtkWidget* widget)
+{
+    static const int contextMenuMargin = 1;
+
+    // The context menu event was generated from the keyboard, so show the context menu by the current selection.
+    Page* page = core(WEBKIT_WEB_VIEW(widget));
+    FrameView* view = page->mainFrame()->view();
+    Position start = page->mainFrame()->selectionController()->selection().start();
+    Position end = page->mainFrame()->selectionController()->selection().end();
+
+    int rightAligned = FALSE;
+    IntPoint location;
+
+    if (!start.node() || !end.node())
+        location = IntPoint(rightAligned ? view->contentsWidth() - contextMenuMargin : contextMenuMargin, contextMenuMargin);
+    else {
+        RenderObject* renderer = start.node()->renderer();
+        if (!renderer)
+            return FALSE;
+
+        // Calculate the rect of the first line of the selection (cribbed from -[WebCoreFrameBridge firstRectForDOMRange:]).
+        int extraWidthToEndOfLine = 0;
+        IntRect startCaretRect = renderer->caretRect(start.offset(), DOWNSTREAM, &extraWidthToEndOfLine);
+        IntRect endCaretRect = renderer->caretRect(end.offset(), UPSTREAM);
+
+        IntRect firstRect;
+        if (startCaretRect.y() == endCaretRect.y())
+            firstRect = IntRect(MIN(startCaretRect.x(), endCaretRect.x()),
+                                startCaretRect.y(),
+                                abs(endCaretRect.x() - startCaretRect.x()),
+                                MAX(startCaretRect.height(), endCaretRect.height()));
+        else
+            firstRect = IntRect(startCaretRect.x(),
+                                startCaretRect.y(),
+                                startCaretRect.width() + extraWidthToEndOfLine,
+                                startCaretRect.height());
+
+        location = IntPoint(rightAligned ? firstRect.right() : firstRect.x(), firstRect.bottom());
+    }
+
+    int x, y;
+    gdk_window_get_origin(GTK_WIDGET(view->containingWindow())->window, &x, &y);
+
+    // FIXME: The IntSize(0, -1) is a hack to get the hit-testing to result in the selected element.
+    // Ideally we'd have the position of a context menu event be separate from its target node.
+    location = view->contentsToWindow(location) + IntSize(0, -1);
+    IntPoint global = location + IntSize(x, y);
+    PlatformMouseEvent event(location, global, NoButton, MouseEventPressed, 0, false, false, false, false, gtk_get_current_event_time());
+
+    return webkit_web_view_forward_context_menu_event(WEBKIT_WEB_VIEW(widget), event);
+}
+
 static void webkit_web_view_get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) {
     WebKitWebView* webView = WEBKIT_WEB_VIEW(object);
 
@@ -180,7 +269,11 @@ static gboolean webkit_web_view_button_press_event(GtkWidget* widget, GdkEventBu
 
     // FIXME: need to keep track of subframe focus for key events
     gtk_widget_grab_focus(GTK_WIDGET(widget));
-    return frame->eventHandler()->handleMousePressEvent(PlatformMouseEvent(event));
+
+    if (event->button == 3)
+        return webkit_web_view_forward_context_menu_event(WEBKIT_WEB_VIEW(widget), PlatformMouseEvent(event));
+    else
+        return frame->eventHandler()->handleMousePressEvent(PlatformMouseEvent(event));
 }
 
 static gboolean webkit_web_view_button_release_event(GtkWidget* widget, GdkEventButton* event)
@@ -803,6 +896,7 @@ static void webkit_web_view_class_init(WebKitWebViewClass* webViewClass)
     widgetClass->motion_notify_event = webkit_web_view_motion_event;
     widgetClass->scroll_event = webkit_web_view_scroll_event;
     widgetClass->size_allocate = webkit_web_view_size_allocate;
+    widgetClass->popup_menu = webkit_web_view_popup_menu_handler;
 
     GtkContainerClass* containerClass = GTK_CONTAINER_CLASS(webViewClass);
     containerClass->add = webkit_web_view_container_add;
@@ -886,6 +980,7 @@ static void webkit_web_view_init(WebKitWebView* webView)
 
     GTK_WIDGET_SET_FLAGS(webView, GTK_CAN_FOCUS);
     webViewData->mainFrame = WEBKIT_WEB_FRAME(webkit_web_frame_new(webView));
+    webViewData->lastPopupXPosition = webViewData->lastPopupYPosition = -1;
     webViewData->editable = false;
 
 #if GTK_CHECK_VERSION(2,10,0)