aboutsummaryrefslogtreecommitdiffstats
path: root/libowl/owlwindowmenu.c
diff options
context:
space:
mode:
Diffstat (limited to 'libowl/owlwindowmenu.c')
-rw-r--r--libowl/owlwindowmenu.c132
1 files changed, 132 insertions, 0 deletions
diff --git a/libowl/owlwindowmenu.c b/libowl/owlwindowmenu.c
new file mode 100644
index 0000000..7be5f0f
--- /dev/null
+++ b/libowl/owlwindowmenu.c
@@ -0,0 +1,132 @@
+/*
+ * Copyright (C) 2007 Ross Burton <ross@openedhand.com>
+ *
+ * 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; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include <config.h>
+
+#include <string.h>
+#include <gtk/gtk.h>
+#include <gdk/gdkx.h>
+#include "owlwindowmenu.h"
+
+#define MENU_PROP "owl::windowmenu"
+
+static Atom atom_custom, atom_protocol;
+
+static void
+ensure_atoms (GdkDrawable *drawable)
+{
+ static gboolean done = FALSE;
+ if (G_UNLIKELY (!done)) {
+ GdkDisplay *display = gdk_drawable_get_display (drawable);
+
+ atom_custom = gdk_x11_get_xatom_by_name_for_display
+ (display, "_NET_WM_CONTEXT_CUSTOM");
+ atom_protocol = gdk_x11_get_xatom_by_name_for_display
+ (display, "WM_PROTOCOLS");
+
+ g_assert (atom_custom);
+ g_assert (atom_protocol);
+ done = TRUE;
+ }
+}
+
+static void
+position_func (GtkMenu *menu,
+ gint *x,
+ gint *y,
+ gboolean *push_in,
+ gpointer user_data)
+{
+ GtkWidget *window = user_data;
+
+ /* Set the position of the menu to the origin of the window. This means that
+ the menu is in the top left of the application window. */
+
+ gdk_window_get_origin (window->window, x, y);
+}
+
+static GdkFilterReturn
+filter_func (GdkXEvent *xevent, GdkEvent *event, gpointer data)
+{
+ if (((XEvent*)xevent)->type == ClientMessage) {
+ XClientMessageEvent *xev = xevent;
+ if (xev->message_type == atom_protocol) {
+ Atom protocol = xev->data.l[0];
+ if (protocol == atom_custom) {
+ GtkMenu *menu;
+
+ g_return_val_if_fail (GTK_IS_WINDOW (data), GDK_FILTER_CONTINUE);
+
+ menu = g_object_get_data (G_OBJECT (data), MENU_PROP);
+ /* I wonder if this offset from the window should be specified by the theme */
+ gtk_menu_popup (menu, NULL, NULL,
+ position_func, data,
+ 0, gtk_get_current_event_time());
+
+ return GDK_FILTER_REMOVE;
+ }
+ }
+ }
+ return GDK_FILTER_CONTINUE;
+}
+
+void
+owl_set_window_menu (GtkWindow *window, GtkMenu *menu)
+{
+ gboolean done_setup;
+ GdkWindow *w;
+ Status status;
+ Atom *old_atoms = NULL, *atoms;
+ int count = 0;
+
+ g_return_if_fail (GTK_IS_WINDOW (window));
+ g_return_if_fail (GTK_IS_MENU (menu));
+
+ w = GTK_WIDGET (window)->window;
+
+ ensure_atoms (w);
+
+ /* We only need to setup if the property isn't already set */
+ done_setup = g_object_get_data (G_OBJECT (window), MENU_PROP) != NULL;
+
+ g_object_set_data_full (G_OBJECT (window), MENU_PROP, g_object_ref (menu), g_object_unref);
+
+ /* It's possible that we are replacing the menu with another menu, so the
+ filters and so on are already registered. */
+ if (done_setup)
+ return;
+
+ status = XGetWMProtocols(GDK_WINDOW_XDISPLAY (w), GDK_WINDOW_XID (w),
+ &old_atoms, &count);
+ /* TODO: check error */
+
+ /* TODO: check if old_atoms already contains custom */
+
+ atoms = g_new0 (Atom, count+1);
+ memcpy (atoms, old_atoms, count * sizeof (Atom));
+ atoms[count] = atom_custom;
+
+ XSetWMProtocols (GDK_WINDOW_XDISPLAY (w), GDK_WINDOW_XID (w), atoms, count+1);
+ /* TODO: check error */
+
+ XFree (old_atoms);
+ g_free (atoms);
+
+ gdk_window_add_filter (w, filter_func, window);
+}