diff options
Diffstat (limited to 'libowl/owlwindowmenu.c')
-rw-r--r-- | libowl/owlwindowmenu.c | 132 |
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); +} |