[gtk+] gtkwindow: Use window-manager-side window menus



commit 0ea1a526f93411f8a2aef60dcb5a429a7694ca99
Author: Jasper St. Pierre <jstpierre mecheye net>
Date:   Thu Mar 13 17:28:01 2014 -0400

    gtkwindow: Use window-manager-side window menus
    
    This avoids a bunch of policy problems with deciding how to lay
    out the window menu under different WMs.
    
    For now, we use the special event _GTK_SHOW_WINDOW_MENU, but we
    hope to have this standardized in wm-spec quite soon, as KDE wants
    it as well.

 gdk/gdkwindow.c         |   33 ++++++++
 gdk/gdkwindow.h         |    3 +
 gdk/gdkwindowimpl.h     |    2 +
 gdk/x11/gdkwindow-x11.c |   42 ++++++++++
 gtk/gtkwindow.c         |  200 +++--------------------------------------------
 5 files changed, 91 insertions(+), 189 deletions(-)
---
diff --git a/gdk/gdkwindow.c b/gdk/gdkwindow.c
index e50601a..7f72dca 100644
--- a/gdk/gdkwindow.c
+++ b/gdk/gdkwindow.c
@@ -10921,3 +10921,36 @@ gdk_window_set_shadow_width (GdkWindow *window,
   if (impl_class->set_shadow_width)
     impl_class->set_shadow_width (window, left, right, top, bottom);
 }
+
+/**
+ * gdk_window_show_window_menu:
+ * @window: a #GdkWindow
+ * @event: a #GdkEvent to show the menu for
+ *
+ * Asks the window menu to show the window menu. The window menu is
+ * the menu shown when right-clicking the titlebar on traditional
+ * windows managed by the window manager. This is useful for windows
+ * using client-side decorations, activating it with a right-click
+ * on the window decorations.
+ *
+ * Returns: %TRUE if the window menu was shown by the window
+ * manager and %FALSE otherwise.
+ *
+ * Since: 3.14
+ */
+gboolean
+gdk_window_show_window_menu (GdkWindow *window,
+                             GdkEvent  *event)
+{
+  GdkWindowImplClass *impl_class;
+
+  g_return_val_if_fail (GDK_IS_WINDOW (window), FALSE);
+  g_return_val_if_fail (!GDK_WINDOW_DESTROYED (window), FALSE);
+
+  impl_class = GDK_WINDOW_IMPL_GET_CLASS (window->impl);
+
+  if (impl_class->show_window_menu)
+    return impl_class->show_window_menu (window, event);
+  else
+    return FALSE;
+}
diff --git a/gdk/gdkwindow.h b/gdk/gdkwindow.h
index d6c7205..09a4af3 100644
--- a/gdk/gdkwindow.h
+++ b/gdk/gdkwindow.h
@@ -1100,6 +1100,9 @@ void       gdk_window_set_shadow_width         (GdkWindow      *window,
                                                 gint            right,
                                                 gint            top,
                                                 gint            bottom);
+GDK_AVAILABLE_IN_3_14
+gboolean  gdk_window_show_window_menu          (GdkWindow      *window,
+                                                GdkEvent       *event);
 
 G_END_DECLS
 
diff --git a/gdk/gdkwindowimpl.h b/gdk/gdkwindowimpl.h
index bd1714d..b1e9290 100644
--- a/gdk/gdkwindowimpl.h
+++ b/gdk/gdkwindowimpl.h
@@ -301,6 +301,8 @@ struct _GdkWindowImplClass
                                            gint            right,
                                            gint            top,
                                            gint            bottom);
+  gboolean     (* show_window_menu)       (GdkWindow      *window,
+                                           GdkEvent       *event);
 };
 
 /* Interface Functions */
diff --git a/gdk/x11/gdkwindow-x11.c b/gdk/x11/gdkwindow-x11.c
index e5e0b23..d3120a6 100644
--- a/gdk/x11/gdkwindow-x11.c
+++ b/gdk/x11/gdkwindow-x11.c
@@ -5702,6 +5702,47 @@ gdk_x11_window_set_opaque_region (GdkWindow      *window,
     g_free (data);
 }
 
+static gboolean
+gdk_x11_window_show_window_menu (GdkWindow *window,
+                                 GdkEvent  *event)
+{
+  GdkDisplay *display = GDK_WINDOW_DISPLAY (window);
+  GdkDevice *device;
+  int device_id;
+  XClientMessageEvent xclient = { 0 };
+
+  switch (event->type)
+    {
+    case GDK_BUTTON_PRESS:
+    case GDK_BUTTON_RELEASE:
+      break;
+    default:
+      return FALSE;
+    }
+
+  if (!gdk_x11_screen_supports_net_wm_hint (GDK_WINDOW_SCREEN (window),
+                                            gdk_atom_intern_static_string ("_GTK_SHOW_WINDOW_MENU")))
+    return FALSE;
+
+  device = gdk_event_get_device (event);
+
+  g_object_get (G_OBJECT (device),
+                "device-id", &device_id,
+                NULL);
+
+  xclient.type = ClientMessage;
+  xclient.window = GDK_WINDOW_XID (window);
+  xclient.message_type = gdk_x11_get_xatom_by_name_for_display (display, "_GTK_SHOW_WINDOW_MENU");
+  xclient.data.l[0] = device_id;
+  xclient.format = 32;
+
+  XSendEvent (GDK_DISPLAY_XDISPLAY (display), GDK_WINDOW_XROOTWIN (window), False,
+              SubstructureRedirectMask | SubstructureNotifyMask,
+              (XEvent *)&xclient);
+
+  return TRUE;
+}
+
 static void
 gdk_window_impl_x11_class_init (GdkWindowImplX11Class *klass)
 {
@@ -5791,4 +5832,5 @@ gdk_window_impl_x11_class_init (GdkWindowImplX11Class *klass)
   impl_class->get_scale_factor = gdk_x11_window_get_scale_factor;
   impl_class->set_opaque_region = gdk_x11_window_set_opaque_region;
   impl_class->set_shadow_width = gdk_x11_window_set_shadow_width;
+  impl_class->show_window_menu = gdk_x11_window_show_window_menu;
 }
diff --git a/gtk/gtkwindow.c b/gtk/gtkwindow.c
index 20f7251..537cdf1 100644
--- a/gtk/gtkwindow.c
+++ b/gtk/gtkwindow.c
@@ -8633,48 +8633,6 @@ popup_position_func (GtkMenu   *menu,
 }
 
 static void
-ontop_window_clicked (GtkMenuItem *menuitem,
-                      gpointer     user_data)
-{
-  GtkWindow *window = (GtkWindow *)user_data;
-
-  gtk_window_set_keep_above (window, !window->priv->above_initially);
-}
-
-#ifdef GDK_WINDOWING_X11
-static void
-stick_window_clicked (GtkMenuItem *menuitem,
-                      gpointer     user_data)
-{
-  GtkWindow *window = (GtkWindow *)user_data;
-
-  gtk_window_stick (window);
-}
-
-static void
-unstick_window_clicked (GtkMenuItem *menuitem,
-                        gpointer     user_data)
-{
-  GtkWindow *window = (GtkWindow *)user_data;
-
-  gtk_window_unstick (window);
-}
-
-static void
-workspace_change_clicked (GtkMenuItem *menuitem,
-                          gpointer     user_data)
-{
-  GtkWindow *window = (GtkWindow *)user_data;
-  GdkWindow *gdk_window;
-  guint32 desktop;
-
-  gdk_window = gtk_widget_get_window (GTK_WIDGET (window));
-  desktop = GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (menuitem), "workspace"));
-  gdk_x11_window_move_to_desktop (gdk_window, desktop);
-}
-#endif
-
-static void
 close_window_clicked (GtkMenuItem *menuitem,
                       gpointer     user_data)
 {
@@ -8685,30 +8643,8 @@ close_window_clicked (GtkMenuItem *menuitem,
 }
 
 static void
-move_window_clicked (GtkMenuItem *menuitem,
-                     gpointer     user_data)
-{
-  GtkWidget *widget = (GtkWidget *)user_data;
-
-  gdk_window_begin_move_drag (gtk_widget_get_window (widget),
-                              0, 0, 0,
-                              gtk_get_current_event_time ());
-}
-
-static void
-resize_window_clicked (GtkMenuItem *menuitem,
-                       gpointer     user_data)
-{
-  GtkWidget *widget = (GtkWidget *)user_data;
-
-  gdk_window_begin_resize_drag (gtk_widget_get_window (widget),
-                                0, 0, 0, 0,
-                                gtk_get_current_event_time ());
-}
-
-static void
-gtk_window_do_popup (GtkWindow      *window,
-                     GdkEventButton *event)
+gtk_window_do_popup_fallback (GtkWindow      *window,
+                              GdkEventButton *event)
 {
   GtkWindowPrivate *priv = window->priv;
   GtkWidget *menuitem;
@@ -8724,129 +8660,6 @@ gtk_window_do_popup (GtkWindow      *window,
                              GTK_WIDGET (window),
                              popup_menu_detach);
 
-  menuitem = gtk_menu_item_new_with_label (_("Minimize"));
-  gtk_widget_show (menuitem);
-  if (priv->gdk_type_hint != GDK_WINDOW_TYPE_HINT_NORMAL)
-    gtk_widget_set_sensitive (menuitem, FALSE);
-  g_signal_connect_swapped (G_OBJECT (menuitem), "activate",
-                            G_CALLBACK (gtk_window_iconify), window);
-  gtk_menu_shell_append (GTK_MENU_SHELL (priv->popup_menu), menuitem);
-
-  menuitem = gtk_menu_item_new_with_label (priv->maximized ? _("Unmaximize") : _("Maximize"));
-  gtk_widget_show (menuitem);
-  if (!priv->resizable ||
-      priv->gdk_type_hint != GDK_WINDOW_TYPE_HINT_NORMAL)
-    gtk_widget_set_sensitive (menuitem, FALSE);
-  g_signal_connect_swapped (G_OBJECT (menuitem), "activate",
-                            G_CALLBACK (_gtk_window_toggle_maximized), window);
-  gtk_menu_shell_append (GTK_MENU_SHELL (priv->popup_menu), menuitem);
-
-  menuitem = gtk_menu_item_new_with_label (_("Move"));
-  gtk_widget_show (menuitem);
-  g_signal_connect (G_OBJECT (menuitem), "activate",
-                    G_CALLBACK (move_window_clicked), window);
-  gtk_menu_shell_append (GTK_MENU_SHELL (priv->popup_menu), menuitem);
-
-  menuitem = gtk_menu_item_new_with_label (_("Resize"));
-  gtk_widget_show (menuitem);
-  if (!priv->resizable || priv->maximized)
-    gtk_widget_set_sensitive (menuitem, FALSE);
-  g_signal_connect (G_OBJECT (menuitem), "activate",
-                    G_CALLBACK (resize_window_clicked), window);
-  gtk_menu_shell_append (GTK_MENU_SHELL (priv->popup_menu), menuitem);
-
-  menuitem = gtk_separator_menu_item_new ();
-  gtk_widget_show (menuitem);
-  gtk_menu_shell_append (GTK_MENU_SHELL (priv->popup_menu), menuitem);
-
-  menuitem = gtk_check_menu_item_new_with_label (_("Always on Top"));
-  gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (menuitem), priv->above_initially);
-  if (priv->maximized)
-    gtk_widget_set_sensitive (menuitem, FALSE);
-  gtk_widget_show (menuitem);
-  g_signal_connect (G_OBJECT (menuitem), "activate",
-                    G_CALLBACK (ontop_window_clicked), window);
-  gtk_menu_shell_append (GTK_MENU_SHELL (priv->popup_menu), menuitem);
-
-#ifdef GDK_WINDOWING_X11
-  if (GDK_IS_X11_DISPLAY (gtk_widget_get_display (GTK_WIDGET (window))))
-    {
-      menuitem = gtk_check_menu_item_new_with_label (_("Always on Visible Workspace"));
-      gtk_check_menu_item_set_draw_as_radio (GTK_CHECK_MENU_ITEM (menuitem), TRUE);
-      gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (menuitem), priv->stick_initially);
-      gtk_widget_show (menuitem);
-      g_signal_connect (G_OBJECT (menuitem), "activate",
-                        G_CALLBACK (stick_window_clicked), window);
-      gtk_menu_shell_append (GTK_MENU_SHELL (priv->popup_menu), menuitem);
-
-      menuitem = gtk_check_menu_item_new_with_label (_("Only on This Workspace"));
-      gtk_check_menu_item_set_draw_as_radio (GTK_CHECK_MENU_ITEM (menuitem), TRUE);
-      gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (menuitem), !priv->stick_initially);
-      gtk_widget_show (menuitem);
-      g_signal_connect (G_OBJECT (menuitem), "activate",
-                        G_CALLBACK (unstick_window_clicked), window);
-      gtk_menu_shell_append (GTK_MENU_SHELL (priv->popup_menu), menuitem);
-
-      if (!priv->stick_initially)
-        {
-          guint32 n_desktops, desktop;
-
-          n_desktops = gdk_x11_screen_get_number_of_desktops (gtk_widget_get_screen (GTK_WIDGET (window)));
-          desktop = gdk_x11_window_get_desktop (gtk_widget_get_window (GTK_WIDGET (window)));
-
-          if (desktop > 0)
-            {
-              menuitem = gtk_menu_item_new_with_label (_("Move to Workspace Up"));
-              g_object_set_data (G_OBJECT (menuitem), "workspace", GUINT_TO_POINTER (desktop - 1));
-              gtk_widget_show (menuitem);
-              g_signal_connect (G_OBJECT (menuitem), "activate",
-                                G_CALLBACK (workspace_change_clicked), window);
-              gtk_menu_shell_append (GTK_MENU_SHELL (priv->popup_menu), menuitem);
-            }
-          if (desktop + 1 < n_desktops)
-            {
-              menuitem = gtk_menu_item_new_with_label (_("Move to Workspace Down"));
-              g_object_set_data (G_OBJECT (menuitem), "workspace", GUINT_TO_POINTER (desktop + 1));
-              gtk_widget_show (menuitem);
-              g_signal_connect (G_OBJECT (menuitem), "activate",
-                                G_CALLBACK (workspace_change_clicked), window);
-              gtk_menu_shell_append (GTK_MENU_SHELL (priv->popup_menu), menuitem);
-            }
-          if (n_desktops > 2)
-            {
-              GtkWidget *submenu;
-              gint d;
-              guint32 current;
-
-              current = gdk_x11_screen_get_current_desktop (gtk_widget_get_screen (GTK_WIDGET (window)));
-              menuitem = gtk_menu_item_new_with_label (_("Move to Another Workspace"));
-              gtk_widget_show (menuitem);
-              gtk_menu_shell_append (GTK_MENU_SHELL (priv->popup_menu), menuitem);
-              submenu = gtk_menu_new ();
-              gtk_menu_item_set_submenu (GTK_MENU_ITEM (menuitem), submenu);
-              for (d = 0; d < n_desktops; d++)
-                {
-                  gchar *label;
-                  label = g_strdup_printf (_("Workspace %d"), d + 1);
-                  menuitem = gtk_menu_item_new_with_label (label);
-                  g_free (label);
-                  g_object_set_data (G_OBJECT (menuitem), "workspace", GUINT_TO_POINTER (d));
-                  if (d == current)
-                    gtk_widget_set_sensitive (menuitem, FALSE);
-                  gtk_widget_show (menuitem);
-                  g_signal_connect (G_OBJECT (menuitem), "activate",
-                                    G_CALLBACK (workspace_change_clicked), window);
-                  gtk_menu_shell_append (GTK_MENU_SHELL (submenu), menuitem);
-                }
-            }
-        }
-    }
-#endif
-
-  menuitem = gtk_separator_menu_item_new ();
-  gtk_widget_show (menuitem);
-  gtk_menu_shell_append (GTK_MENU_SHELL (priv->popup_menu), menuitem);
-
   menuitem = gtk_menu_item_new_with_label (_("Close"));
   gtk_widget_show (menuitem);
   if (!priv->deletable)
@@ -8867,6 +8680,15 @@ gtk_window_do_popup (GtkWindow      *window,
                     0, gtk_get_current_event_time ());
 }
 
+static void
+gtk_window_do_popup (GtkWindow      *window,
+                     GdkEventButton *event)
+{
+  if (!gdk_window_show_window_menu (gtk_widget_get_window (GTK_WIDGET (window)),
+                                    (GdkEvent *) event))
+    gtk_window_do_popup_fallback (window, event);
+}
+
 /*********************************
  * Functions related to resizing *
  *********************************/


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