[gtk+/xi2] GtkMenu(Shell): Add gtk_menu_popup_for_device().



commit 052bb2c27c44e9787193b726dd7cd53d02900295
Author: Carlos Garnacho <carlos gnome org>
Date:   Thu Dec 24 02:26:54 2009 +0100

    GtkMenu(Shell): Add gtk_menu_popup_for_device().
    
    The internals have been modified to use device-aware API. The device
    pair that initiates the menu popup are the only ones able to deliver
    events to it.

 gtk/gtkmenu.c      |  176 ++++++++++++++++++++++++++++++++++++++++------------
 gtk/gtkmenu.h      |    9 +++
 gtk/gtkmenushell.c |   47 +++++++++++++-
 gtk/gtkmenushell.h |    8 +++
 4 files changed, 195 insertions(+), 45 deletions(-)
---
diff --git a/gtk/gtkmenu.c b/gtk/gtkmenu.c
index 6a37604..c69d7c4 100644
--- a/gtk/gtkmenu.c
+++ b/gtk/gtkmenu.c
@@ -1377,28 +1377,32 @@ gtk_menu_tearoff_bg_copy (GtkMenu *menu)
 
 static gboolean
 popup_grab_on_window (GdkWindow *window,
-		      guint32    activate_time,
-		      gboolean   grab_keyboard)
+                      GdkDevice *keyboard,
+                      GdkDevice *pointer,
+		      guint32    activate_time)
 {
-  if ((gdk_pointer_grab (window, TRUE,
-			 GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
-			 GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK |
-			 GDK_POINTER_MOTION_MASK,
-			 NULL, NULL, activate_time) == 0))
+  if (keyboard &&
+      gdk_device_grab (keyboard, window,
+                       GDK_OWNERSHIP_WINDOW, TRUE,
+                       GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK,
+                       NULL, activate_time) != GDK_GRAB_SUCCESS)
+    return FALSE;
+
+  if (pointer &&
+      gdk_device_grab (pointer, window,
+                       GDK_OWNERSHIP_WINDOW, TRUE,
+                       GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
+                       GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK |
+                       GDK_POINTER_MOTION_MASK,
+                       NULL, activate_time) != GDK_GRAB_SUCCESS)
     {
-      if (!grab_keyboard ||
-	  gdk_keyboard_grab (window, TRUE,
-			     activate_time) == 0)
-	return TRUE;
-      else
-	{
-	  gdk_display_pointer_ungrab (gdk_drawable_get_display (window),
-				      activate_time);
-	  return FALSE;
-	}
+      if (keyboard)
+        gdk_device_ungrab (keyboard, activate_time);
+
+      return FALSE;
     }
 
-  return FALSE;
+  return TRUE;
 }
 
 /**
@@ -1430,13 +1434,14 @@ popup_grab_on_window (GdkWindow *window,
  * be used instead.
  */
 void
-gtk_menu_popup (GtkMenu		    *menu,
-		GtkWidget	    *parent_menu_shell,
-		GtkWidget	    *parent_menu_item,
-		GtkMenuPositionFunc  func,
-		gpointer	     data,
-		guint		     button,
-		guint32		     activate_time)
+gtk_menu_popup_for_device (GtkMenu             *menu,
+                           GdkDevice           *device,
+                           GtkWidget           *parent_menu_shell,
+                           GtkWidget           *parent_menu_item,
+                           GtkMenuPositionFunc  func,
+                           gpointer             data,
+                           guint                button,
+                           guint32              activate_time)
 {
   GtkWidget *widget;
   GtkWidget *xgrab_shell;
@@ -1446,13 +1451,26 @@ gtk_menu_popup (GtkMenu		    *menu,
   gboolean grab_keyboard;
   GtkMenuPrivate *priv;
   GtkWidget *parent_toplevel;
+  GdkDevice *keyboard, *pointer;
 
   g_return_if_fail (GTK_IS_MENU (menu));
+  g_return_if_fail (GDK_IS_DEVICE (device));
 
   widget = GTK_WIDGET (menu);
   menu_shell = GTK_MENU_SHELL (menu);
   priv = gtk_menu_get_private (menu);
 
+  if (device->source == GDK_SOURCE_KEYBOARD)
+    {
+      keyboard = device;
+      pointer = gdk_device_get_associated_device (device);
+    }
+  else
+    {
+      pointer = device;
+      keyboard = gdk_device_get_associated_device (device);
+    }
+
   menu_shell->parent_menu_shell = parent_menu_shell;
 
   priv->seen_item_enter = FALSE;
@@ -1500,10 +1518,13 @@ gtk_menu_popup (GtkMenu		    *menu,
   grab_keyboard = gtk_menu_shell_get_take_focus (menu_shell);
   gtk_window_set_accept_focus (GTK_WINDOW (menu->toplevel), grab_keyboard);
 
+  if (!grab_keyboard)
+    keyboard = NULL;
+
   if (xgrab_shell && xgrab_shell != widget)
     {
-      if (popup_grab_on_window (xgrab_shell->window, activate_time, grab_keyboard))
-	GTK_MENU_SHELL (xgrab_shell)->have_xgrab = TRUE;
+      if (popup_grab_on_window (xgrab_shell->window, keyboard, pointer, activate_time))
+        GTK_MENU_SHELL (xgrab_shell)->have_xgrab = TRUE;
     }
   else
     {
@@ -1511,8 +1532,8 @@ gtk_menu_popup (GtkMenu		    *menu,
 
       xgrab_shell = widget;
       transfer_window = menu_grab_transfer_window_get (menu);
-      if (popup_grab_on_window (transfer_window, activate_time, grab_keyboard))
-	GTK_MENU_SHELL (xgrab_shell)->have_xgrab = TRUE;
+      if (popup_grab_on_window (transfer_window, keyboard, pointer, activate_time))
+        GTK_MENU_SHELL (xgrab_shell)->have_xgrab = TRUE;
     }
 
   if (!GTK_MENU_SHELL (xgrab_shell)->have_xgrab)
@@ -1526,6 +1547,7 @@ gtk_menu_popup (GtkMenu		    *menu,
       return;
     }
 
+  _gtk_menu_shell_set_grab_devices (GTK_MENU_SHELL (menu), keyboard, pointer);
   menu_shell->active = TRUE;
   menu_shell->button = button;
 
@@ -1621,10 +1643,76 @@ gtk_menu_popup (GtkMenu		    *menu,
   gtk_widget_show (menu->toplevel);
 
   if (xgrab_shell == widget)
-    popup_grab_on_window (widget->window, activate_time, grab_keyboard); /* Should always succeed */
+    popup_grab_on_window (widget->window, keyboard, pointer, activate_time); /* Should always succeed */
+
   gtk_grab_add (GTK_WIDGET (menu));
 }
 
+/**
+ * gtk_menu_popup:
+ * @menu: a #GtkMenu.
+ * @parent_menu_shell: the menu shell containing the triggering menu item, or %NULL
+ * @parent_menu_item: the menu item whose activation triggered the popup, or %NULL
+ * @func: a user supplied function used to position the menu, or %NULL
+ * @data: user supplied data to be passed to @func.
+ * @button: the mouse button which was pressed to initiate the event.
+ * @activate_time: the time at which the activation event occurred.
+ *
+ * Displays a menu and makes it available for selection.  Applications can use
+ * this function to display context-sensitive menus, and will typically supply
+ * %NULL for the @parent_menu_shell, @parent_menu_item, @func and @data 
+ * parameters. The default menu positioning function will position the menu
+ * at the current mouse cursor position.
+ *
+ * The @button parameter should be the mouse button pressed to initiate
+ * the menu popup. If the menu popup was initiated by something other than
+ * a mouse button press, such as a mouse button release or a keypress,
+ * @button should be 0.
+ *
+ * The @activate_time parameter is used to conflict-resolve initiation of
+ * concurrent requests for mouse/keyboard grab requests. To function
+ * properly, this needs to be the time stamp of the user event (such as
+ * a mouse click or key press) that caused the initiation of the popup.
+ * Only if no such event is available, gtk_get_current_event_time() can
+ * be used instead.
+ */
+void
+gtk_menu_popup (GtkMenu		    *menu,
+		GtkWidget	    *parent_menu_shell,
+		GtkWidget	    *parent_menu_item,
+		GtkMenuPositionFunc  func,
+		gpointer	     data,
+		guint		     button,
+		guint32		     activate_time)
+{
+  GdkDevice *device;
+
+  g_return_if_fail (GTK_IS_MENU (menu));
+
+  device = gtk_get_current_event_device ();
+
+  if (!device)
+    {
+      GdkDisplay *display;
+      GdkDeviceManager *device_manager;
+      GList *devices;
+
+      display = gtk_widget_get_display (GTK_WIDGET (menu));
+      device_manager = gdk_display_get_device_manager (display);
+      devices = gdk_device_manager_list_devices (device_manager, GDK_DEVICE_TYPE_MASTER);
+
+      device = devices->data;
+
+      g_list_free (devices);
+    }
+
+  gtk_menu_popup_for_device (menu, device,
+                             parent_menu_shell,
+                             parent_menu_item,
+                             func, data,
+                             button, activate_time);
+}
+
 void
 gtk_menu_popdown (GtkMenu *menu)
 {
@@ -1671,15 +1759,16 @@ gtk_menu_popdown (GtkMenu *menu)
 	} 
       else
 	{
-	  /* We popped up the menu from the tearoff, so we need to 
+          GdkDevice *keyboard, *pointer;
+
+          /* We popped up the menu from the tearoff, so we need to
 	   * release the grab - we aren't actually hiding the menu.
 	   */
-	  if (menu_shell->have_xgrab)
+	  if (menu_shell->have_xgrab &&
+              _gtk_menu_shell_get_grab_devices (menu_shell, &keyboard, &pointer))
 	    {
-	      GdkDisplay *display = gtk_widget_get_display (GTK_WIDGET (menu));
-	      
-	      gdk_display_pointer_ungrab (display, GDK_CURRENT_TIME);
-	      gdk_display_keyboard_ungrab (display, GDK_CURRENT_TIME);
+	      gdk_device_ungrab (keyboard, GDK_CURRENT_TIME);
+	      gdk_device_ungrab (pointer, GDK_CURRENT_TIME);
 	    }
 	}
 
@@ -1695,6 +1784,7 @@ gtk_menu_popdown (GtkMenu *menu)
     gtk_widget_hide (GTK_WIDGET (menu));
 
   menu_shell->have_xgrab = FALSE;
+  _gtk_menu_shell_set_grab_devices (menu_shell, NULL, NULL);
   gtk_grab_remove (GTK_WIDGET (menu));
 
   menu_grab_transfer_window_destroy (menu);
@@ -3963,7 +4053,9 @@ gtk_menu_stop_navigating_submenu_cb (gpointer user_data)
   
   if (GTK_WIDGET_REALIZED (menu))
     {
-      child_window = gdk_window_get_pointer (menu->bin_window, NULL, NULL, NULL);
+      child_window = gdk_window_get_device_position (menu->bin_window,
+                                                     popdown_data->device,
+                                                     NULL, NULL, NULL);
 
       if (child_window)
 	{
@@ -4199,15 +4291,17 @@ gtk_menu_position (GtkMenu *menu)
   GdkScreen *screen;
   GdkScreen *pointer_screen;
   GdkRectangle monitor;
-  
+  GdkDevice *pointer;
+
   g_return_if_fail (GTK_IS_MENU (menu));
 
   widget = GTK_WIDGET (menu);
 
   screen = gtk_widget_get_screen (widget);
-  gdk_display_get_pointer (gdk_screen_get_display (screen),
-			   &pointer_screen, &x, &y, NULL);
-  
+  _gtk_menu_shell_get_grab_devices (GTK_MENU_SHELL (menu), NULL, &pointer);
+  gdk_display_get_device_state (gdk_screen_get_display (screen),
+                                pointer, &pointer_screen, &x, &y, NULL);
+
   /* We need the requisition to figure out the right place to
    * popup the menu. In fact, we always need to ask here, since
    * if a size_request was queued while we weren't popped up,
diff --git a/gtk/gtkmenu.h b/gtk/gtkmenu.h
index 694457a..039083b 100644
--- a/gtk/gtkmenu.h
+++ b/gtk/gtkmenu.h
@@ -133,6 +133,15 @@ void	   gtk_menu_popup		  (GtkMenu	       *menu,
 					   gpointer		data,
 					   guint		button,
 					   guint32		activate_time);
+void       gtk_menu_popup_for_device      (GtkMenu             *menu,
+                                           GdkDevice           *device,
+                                           GtkWidget           *parent_menu_shell,
+                                           GtkWidget           *parent_menu_item,
+                                           GtkMenuPositionFunc  func,
+                                           gpointer             data,
+                                           guint                button,
+                                           guint32              activate_time);
+
 
 /* Position the menu according to its position function. Called
  * from gtkmenuitem.c when a menu-item changes its allocation
diff --git a/gtk/gtkmenushell.c b/gtk/gtkmenushell.c
index 910c760..21f040f 100644
--- a/gtk/gtkmenushell.c
+++ b/gtk/gtkmenushell.c
@@ -134,6 +134,9 @@ struct _GtkMenuShellPrivate
   GtkMnemonicHash *mnemonic_hash;
   GtkKeyHash *key_hash;
 
+  GdkDevice *grab_keyboard;
+  GdkDevice *grab_pointer;
+
   guint take_focus : 1;
   guint activated_submenu : 1;
 };
@@ -991,11 +994,14 @@ gtk_real_menu_shell_deactivate (GtkMenuShell *menu_shell)
 	}
       if (menu_shell->have_xgrab)
 	{
-	  GdkDisplay *display = gtk_widget_get_display (GTK_WIDGET (menu_shell));
-	  
+          GtkMenuShellPrivate *priv = GTK_MENU_SHELL_GET_PRIVATE (menu_shell);
+
+          gdk_device_ungrab (priv->grab_pointer, GDK_CURRENT_TIME);
+          gdk_device_ungrab (priv->grab_keyboard, GDK_CURRENT_TIME);
+
 	  menu_shell->have_xgrab = FALSE;
-	  gdk_display_pointer_ungrab (display, GDK_CURRENT_TIME);
-	  gdk_display_keyboard_ungrab (display, GDK_CURRENT_TIME);
+          priv->grab_pointer = NULL;
+          priv->grab_keyboard = NULL;
 	}
     }
 }
@@ -1621,6 +1627,39 @@ _gtk_menu_shell_remove_mnemonic (GtkMenuShell *menu_shell,
   gtk_menu_shell_reset_key_hash (menu_shell);
 }
 
+void
+_gtk_menu_shell_set_grab_devices (GtkMenuShell *menu_shell,
+                                  GdkDevice    *keyboard,
+                                  GdkDevice    *pointer)
+{
+  GtkMenuShellPrivate *priv = GTK_MENU_SHELL_GET_PRIVATE (menu_shell);
+
+  g_return_if_fail (GTK_IS_MENU_SHELL (menu_shell));
+  g_return_if_fail (!keyboard || GDK_IS_DEVICE (keyboard));
+  g_return_if_fail (!pointer || GDK_IS_DEVICE (pointer));
+
+  priv->grab_keyboard = keyboard;
+  priv->grab_pointer = pointer;
+}
+
+gboolean
+_gtk_menu_shell_get_grab_devices (GtkMenuShell  *menu_shell,
+                                  GdkDevice    **keyboard,
+                                  GdkDevice    **pointer)
+{
+  GtkMenuShellPrivate *priv = GTK_MENU_SHELL_GET_PRIVATE (menu_shell);
+
+  g_return_val_if_fail (GTK_IS_MENU_SHELL (menu_shell), FALSE);
+
+  if (keyboard)
+    *keyboard = priv->grab_keyboard;
+
+  if (pointer)
+    *pointer = priv->grab_pointer;
+
+  return TRUE;
+}
+
 /**
  * gtk_menu_shell_get_take_focus:
  * @menu_shell: a #GtkMenuShell
diff --git a/gtk/gtkmenushell.h b/gtk/gtkmenushell.h
index e8281bc..70e4db7 100644
--- a/gtk/gtkmenushell.h
+++ b/gtk/gtkmenushell.h
@@ -117,6 +117,14 @@ void _gtk_menu_shell_select_last       (GtkMenuShell *menu_shell,
 					gboolean      search_sensitive);
 void  _gtk_menu_shell_activate         (GtkMenuShell *menu_shell);
 gint  _gtk_menu_shell_get_popup_delay  (GtkMenuShell *menu_shell);
+
+void     _gtk_menu_shell_set_grab_devices (GtkMenuShell *menu_shell,
+                                           GdkDevice    *keyboard,
+                                           GdkDevice    *pointer);
+gboolean _gtk_menu_shell_get_grab_devices (GtkMenuShell  *menu_shell,
+                                           GdkDevice    **keyboard,
+                                           GdkDevice    **pointer);
+
 void  gtk_menu_shell_cancel            (GtkMenuShell *menu_shell);
 
 void  _gtk_menu_shell_add_mnemonic     (GtkMenuShell *menu_shell,



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]