1 /* gtkwindowpeer.c -- Native implementation of GtkWindowPeer
2 Copyright (C) 1998, 1999, 2002 Free Software Foundation, Inc.
4 This file is part of GNU Classpath.
6 GNU Classpath is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2, or (at your option)
11 GNU Classpath is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with GNU Classpath; see the file COPYING. If not, write to the
18 Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
21 Linking this library statically or dynamically with other modules is
22 making a combined work based on this library. Thus, the terms and
23 conditions of the GNU General Public License cover the whole
26 As a special exception, the copyright holders of this library give you
27 permission to link this library with independent modules to produce an
28 executable, regardless of the license terms of these independent
29 modules, and to copy and distribute the resulting executable under
30 terms of your choice, provided that you also meet, for each linked
31 independent module, the terms and conditions of the license of that
32 module. An independent module is a module which is not derived from
33 or based on this library. If you modify this library, you may extend
34 this exception to your version of the library, but you are not
35 obligated to do so. If you do not wish to do so, delete this
36 exception statement from your version. */
40 #include "gnu_java_awt_peer_gtk_GtkComponentPeer.h"
41 #include "gnu_java_awt_peer_gtk_GtkWindowPeer.h"
42 #include "gnu_java_awt_peer_gtk_GtkFramePeer.h"
43 #include <gdk/gdkprivate.h>
45 #include <X11/Xatom.h>
47 /* FIXME: we're currently seeing the double-activation that occurs
48 with metacity and GTK. See
49 http://bugzilla.gnome.org/show_bug.cgi?id=140977 for details. */
51 static void window_get_frame_extents (GtkWidget *window,
53 int *bottom, int *right);
55 static void request_frame_extents (GtkWidget *window);
57 static Bool property_notify_predicate (Display *display,
61 static GtkLayout *find_layout (GtkWindow *window);
63 static void window_delete_cb (GtkWidget *widget, GdkEvent *event,
65 static void window_destroy_cb (GtkWidget *widget, GdkEvent *event,
67 static void window_show_cb (GtkWidget *widget, jobject peer);
68 static void window_active_state_change_cb (GtkWidget *widget,
71 static void window_focus_state_change_cb (GtkWidget *widget,
74 static gboolean window_focus_in_cb (GtkWidget * widget,
77 static gboolean window_focus_out_cb (GtkWidget * widget,
80 static gboolean window_window_state_cb (GtkWidget *widget,
83 static jint window_get_new_state (GtkWidget *widget);
84 static gboolean window_property_changed_cb (GtkWidget *widget,
85 GdkEventProperty *event,
88 JNIEXPORT void JNICALL
89 Java_gnu_java_awt_peer_gtk_GtkWindowPeer_create
90 (JNIEnv *env, jobject obj, jint type, jboolean decorated,
91 jint width, jint height, jobject parent, jintArray jinsets)
93 GtkWidget *window_widget;
104 insets = (*env)->GetIntArrayElements (env, jinsets, 0);
105 insets[0] = insets[1] = insets[2] = insets[3] = 0;
107 /* Create global reference and save it for future use */
108 NSA_SET_GLOBAL_REF (env, obj);
110 gdk_threads_enter ();
112 window_widget = gtk_window_new (GTK_WINDOW_TOPLEVEL);
113 window = GTK_WINDOW (window_widget);
115 /* Keep this window in front of its parent, if it has one. */
118 window_parent = NSA_GET_PTR (env, parent);
119 gtk_window_set_transient_for (window, GTK_WINDOW(window_parent));
122 gtk_window_set_decorated (window, decorated);
124 gtk_window_set_type_hint (window, type);
126 gtk_window_group_add_window (global_gtk_window_group, window);
128 vbox = gtk_vbox_new (0, 0);
129 layout = gtk_layout_new (NULL, NULL);
130 gtk_box_pack_end (GTK_BOX (vbox), layout, 1, 1, 0);
131 gtk_container_add (GTK_CONTAINER (window_widget), vbox);
133 gtk_widget_show (layout);
134 gtk_widget_show (vbox);
135 gtk_widget_realize (window_widget);
138 window_get_frame_extents (window_widget, &top, &left, &bottom, &right);
140 gtk_window_set_default_size (window,
141 MAX (1, width - left - right),
142 MAX (1, height - top - bottom));
144 /* We must set this window's size requisition. Otherwise when a
145 resize is queued (when gtk_widget_queue_resize is called) the
146 window will snap to its default requisition of 0x0. If we omit
147 this call, Frames and Dialogs shrink to degenerate 1x1 windows
148 when their resizable property changes. */
149 gtk_widget_set_size_request (window_widget,
150 MAX (1, width - left - right),
151 MAX (1, height - top - bottom));
158 gdk_threads_leave ();
160 (*env)->ReleaseIntArrayElements (env, jinsets, insets, 0);
162 NSA_SET_PTR (env, obj, window_widget);
165 JNIEXPORT void JNICALL
166 Java_gnu_java_awt_peer_gtk_GtkWindowPeer_nativeSetVisible
167 (JNIEnv *env, jobject obj, jboolean visible)
171 ptr = NSA_GET_PTR (env, obj);
173 gdk_threads_enter ();
176 gtk_widget_show (GTK_WIDGET (ptr));
178 gtk_widget_hide (GTK_WIDGET (ptr));
180 XFlush (GDK_DISPLAY ());
182 gdk_threads_leave ();
185 JNIEXPORT void JNICALL
186 Java_gnu_java_awt_peer_gtk_GtkWindowPeer_connectJObject
187 (JNIEnv *env, jobject obj)
192 ptr = NSA_GET_PTR (env, obj);
194 gdk_threads_enter ();
196 layout = find_layout (GTK_WINDOW (ptr));
198 gtk_widget_realize (GTK_WIDGET (layout));
200 connect_awt_hook (env, obj, 1, layout->bin_window);
202 gtk_widget_realize (ptr);
204 connect_awt_hook (env, obj, 1, GTK_WIDGET (ptr)->window);
206 gdk_threads_leave ();
209 JNIEXPORT void JNICALL
210 Java_gnu_java_awt_peer_gtk_GtkWindowPeer_connectSignals
211 (JNIEnv *env, jobject obj)
217 ptr = NSA_GET_PTR (env, obj);
219 gref = NSA_GET_GLOBAL_REF (env, obj);
221 gdk_threads_enter ();
223 gtk_widget_realize (ptr);
225 /* Receive events from the GtkLayout too */
226 layout = find_layout (GTK_WINDOW (ptr));
228 g_signal_connect (G_OBJECT (layout), "event",
229 G_CALLBACK (pre_event_handler), *gref);
231 /* Connect signals for window event support. */
232 g_signal_connect (G_OBJECT (ptr), "delete-event",
233 G_CALLBACK (window_delete_cb), *gref);
235 g_signal_connect (G_OBJECT (ptr), "destroy-event",
236 G_CALLBACK (window_destroy_cb), *gref);
238 g_signal_connect (G_OBJECT (ptr), "show",
239 G_CALLBACK (window_show_cb), *gref);
241 g_signal_connect (G_OBJECT (ptr), "notify::is-active",
242 G_CALLBACK (window_active_state_change_cb), *gref);
244 g_signal_connect (G_OBJECT (ptr), "notify::has-toplevel-focus",
245 G_CALLBACK (window_focus_state_change_cb), *gref);
247 g_signal_connect (G_OBJECT (ptr), "focus-in-event",
248 G_CALLBACK (window_focus_in_cb), *gref);
250 g_signal_connect (G_OBJECT (ptr), "focus-out-event",
251 G_CALLBACK (window_focus_out_cb), *gref);
253 g_signal_connect (G_OBJECT (ptr), "window-state-event",
254 G_CALLBACK (window_window_state_cb), *gref);
256 g_signal_connect (G_OBJECT (ptr), "property-notify-event",
257 G_CALLBACK (window_property_changed_cb), *gref);
259 gdk_threads_leave ();
261 /* Connect the superclass signals. */
262 Java_gnu_java_awt_peer_gtk_GtkComponentPeer_connectSignals (env, obj);
266 * Lower the z-level of a window.
269 JNIEXPORT void JNICALL
270 Java_gnu_java_awt_peer_gtk_GtkWindowPeer_toBack (JNIEnv *env,
274 ptr = NSA_GET_PTR (env, obj);
276 gdk_threads_enter ();
277 gdk_window_lower (GTK_WIDGET (ptr)->window);
279 XFlush (GDK_DISPLAY ());
280 gdk_threads_leave ();
284 * Raise the z-level of a window.
287 JNIEXPORT void JNICALL
288 Java_gnu_java_awt_peer_gtk_GtkWindowPeer_toFront (JNIEnv *env,
292 ptr = NSA_GET_PTR (env, obj);
294 gdk_threads_enter ();
295 gdk_window_raise (GTK_WIDGET (ptr)->window);
297 XFlush (GDK_DISPLAY ());
298 gdk_threads_leave ();
301 JNIEXPORT void JNICALL
302 Java_gnu_java_awt_peer_gtk_GtkWindowPeer_setBoundsCallback
303 (JNIEnv *env __attribute__((unused)), jobject obj __attribute__((unused)),
304 jobject window, jint x, jint y, jint width, jint height)
306 /* Circumvent package-private access to call Window's
307 setBoundsCallback method. */
308 (*gdk_env)->CallVoidMethod (gdk_env, window, setBoundsCallbackID,
309 x, y, width, height);
312 JNIEXPORT void JNICALL
313 Java_gnu_java_awt_peer_gtk_GtkWindowPeer_setSize
314 (JNIEnv *env, jobject obj, jint width, jint height)
316 void *ptr = NSA_GET_PTR (env, obj);
318 /* Avoid GTK runtime assertion failures. */
319 width = (width < 1) ? 1 : width;
320 height = (height < 1) ? 1 : height;
322 gdk_threads_enter ();
323 gtk_widget_set_size_request (GTK_WIDGET(ptr), width, height);
324 gdk_threads_leave ();
327 JNIEXPORT void JNICALL
328 Java_gnu_java_awt_peer_gtk_GtkWindowPeer_nativeSetBounds
329 (JNIEnv *env, jobject obj, jint x, jint y, jint width, jint height)
331 void *ptr = NSA_GET_PTR (env, obj);
333 /* Avoid GTK runtime assertion failures. */
334 width = (width < 1) ? 1 : width;
335 height = (height < 1) ? 1 : height;
337 gdk_threads_enter ();
338 gtk_window_move (GTK_WINDOW(ptr), x, y);
339 /* The call to gdk_window_move is needed in addition to the call to
340 gtk_window_move. If gdk_window_move isn't called, then the
341 following set of operations doesn't give the expected results:
344 2. manually move it to another position on the screen
346 4. reposition the window with Component.setLocation
349 Instead of being at the position set by setLocation, the window
350 is reshown at the position to which it was moved manually. */
351 gdk_window_move (GTK_WIDGET (ptr)->window, x, y);
353 /* Need to change the widget's request size. */
354 gtk_widget_set_size_request (GTK_WIDGET(ptr), width, height);
355 /* Also need to call gtk_window_resize. If the resize is requested
356 by the program and the window's "resizable" property is true then
357 the size request will not be honoured. */
358 gtk_window_resize (GTK_WINDOW (ptr), width, height);
359 gdk_threads_leave ();
362 JNIEXPORT void JNICALL
363 Java_gnu_java_awt_peer_gtk_GtkFramePeer_removeMenuBarPeer
364 (JNIEnv *env, jobject obj)
371 wptr = NSA_GET_PTR (env, obj);
373 gdk_threads_enter ();
375 box = GTK_BIN (wptr)->child;
377 children = gtk_container_get_children (GTK_CONTAINER (box));
379 while (children != NULL && !GTK_IS_MENU_SHELL (children->data))
381 children = children->next;
384 /* If there isn't a MenuBar in this Frame's list of children
385 then we can just return. */
386 if (!GTK_IS_MENU_SHELL (children->data))
389 mptr = children->data;
391 /* This will actually destroy the MenuBar. By removing it from
392 its parent, the reference count for the MenuBar widget will
393 decrement to 0. The widget will be automatically destroyed
395 gtk_container_remove (GTK_CONTAINER (box), GTK_WIDGET (mptr));
400 JNIEXPORT void JNICALL
401 Java_gnu_java_awt_peer_gtk_GtkFramePeer_setMenuBarPeer
402 (JNIEnv *env, jobject obj, jobject menubar)
408 wptr = NSA_GET_PTR (env, obj);
409 mptr = NSA_GET_PTR (env, menubar);
411 gdk_threads_enter ();
413 box = GTK_BIN (wptr)->child;
414 gtk_box_pack_start (GTK_BOX (box), mptr, 0, 0, 0);
416 gtk_widget_show (mptr);
419 gdk_threads_leave ();
422 JNIEXPORT jint JNICALL
423 Java_gnu_java_awt_peer_gtk_GtkFramePeer_getMenuBarHeight
424 (JNIEnv *env, jobject obj __attribute__((unused)), jobject menubar)
428 GtkRequisition gtkreq;
430 ptr = NSA_GET_PTR (env, menubar);
432 gdk_threads_enter ();
433 gtk_widget_size_request (ptr, >kreq);
435 height = gtkreq.height;
436 gdk_threads_leave ();
440 JNIEXPORT void JNICALL
441 Java_gnu_java_awt_peer_gtk_GtkFramePeer_moveLayout
442 (JNIEnv *env, jobject obj, jint offset)
449 ptr = NSA_GET_PTR (env, obj);
451 gdk_threads_enter ();
453 layout = find_layout (GTK_WINDOW (ptr));
455 children = gtk_container_get_children (GTK_CONTAINER (layout));
457 while (children != NULL)
459 widget = children->data;
460 gtk_layout_move (layout, widget, widget->allocation.x,
461 widget->allocation.y+offset);
462 children = children->next;
465 gdk_threads_leave ();
468 JNIEXPORT void JNICALL
469 Java_gnu_java_awt_peer_gtk_GtkFramePeer_gtkLayoutSetVisible
470 (JNIEnv *env, jobject obj, jboolean visible)
475 ptr = NSA_GET_PTR (env, obj);
477 gdk_threads_enter ();
479 layout = find_layout (GTK_WINDOW (ptr));
482 gtk_widget_show (GTK_WIDGET (layout));
484 gtk_widget_hide (GTK_WIDGET (layout));
486 gdk_threads_leave ();
490 window_get_frame_extents (GtkWidget *window,
491 int *top, int *left, int *bottom, int *right)
493 unsigned long *extents = NULL;
495 /* Guess frame extents in case _NET_FRAME_EXTENTS is not
502 /* Request that the window manager set window's
503 _NET_FRAME_EXTENTS property. */
504 request_frame_extents (window);
506 /* Attempt to retrieve window's frame extents. */
507 if (gdk_property_get (window->window,
508 gdk_atom_intern ("_NET_FRAME_EXTENTS", FALSE),
509 gdk_atom_intern ("CARDINAL", FALSE),
511 sizeof (unsigned long) * 4,
516 (guchar **)&extents))
519 *right = extents [1];
521 *bottom = extents [3];
525 static Atom extents_atom = 0;
527 /* Requests that the window manager set window's
528 _NET_FRAME_EXTENTS property. */
530 request_frame_extents (GtkWidget *window)
532 const char *request_str = "_NET_REQUEST_FRAME_EXTENTS";
533 GdkAtom request_extents = gdk_atom_intern (request_str, FALSE);
535 /* Check if the current window manager supports
536 _NET_REQUEST_FRAME_EXTENTS. */
537 if (gdk_net_wm_supports (request_extents))
539 GdkDisplay *display = gtk_widget_get_display (window);
540 Display *xdisplay = GDK_DISPLAY_XDISPLAY (display);
542 GdkWindow *root_window = gdk_get_default_root_window ();
543 Window xroot_window = GDK_WINDOW_XID (root_window);
545 Atom extents_request_atom =
546 gdk_x11_get_xatom_by_name_for_display (display, request_str);
549 XEvent notify_xevent;
551 unsigned long window_id = GDK_WINDOW_XID (GDK_DRAWABLE(window->window));
555 const char *extents_str = "_NET_FRAME_EXTENTS";
557 gdk_x11_get_xatom_by_name_for_display (display, extents_str);
560 xevent.xclient.type = ClientMessage;
561 xevent.xclient.message_type = extents_request_atom;
562 xevent.xclient.display = xdisplay;
563 xevent.xclient.window = window_id;
564 xevent.xclient.format = 32;
565 xevent.xclient.data.l[0] = 0;
566 xevent.xclient.data.l[1] = 0;
567 xevent.xclient.data.l[2] = 0;
568 xevent.xclient.data.l[3] = 0;
569 xevent.xclient.data.l[4] = 0;
571 XSendEvent (xdisplay, xroot_window, False,
572 (SubstructureRedirectMask | SubstructureNotifyMask),
575 XIfEvent(xdisplay, ¬ify_xevent,
576 property_notify_predicate, (XPointer) &window_id);
581 property_notify_predicate (Display *xdisplay __attribute__((unused)),
585 unsigned long *window = (unsigned long *) window_id;
587 if (event->xany.type == PropertyNotify
588 && event->xany.window == *window
589 && event->xproperty.atom == extents_atom)
596 window_delete_cb (GtkWidget *widget __attribute__((unused)),
597 GdkEvent *event __attribute__((unused)),
600 (*gdk_env)->CallVoidMethod (gdk_env, peer,
602 (jint) AWT_WINDOW_CLOSING,
603 (jobject) NULL, (jint) 0);
607 window_destroy_cb (GtkWidget *widget __attribute__((unused)),
608 GdkEvent *event __attribute__((unused)),
611 (*gdk_env)->CallVoidMethod (gdk_env, peer,
613 (jint) AWT_WINDOW_CLOSED,
614 (jobject) NULL, (jint) 0);
618 window_show_cb (GtkWidget *widget __attribute__((unused)),
621 (*gdk_env)->CallVoidMethod (gdk_env, peer,
623 (jint) AWT_WINDOW_OPENED,
624 (jobject) NULL, (jint) 0);
628 window_active_state_change_cb (GtkWidget *widget,
632 /* FIXME: not sure if this is needed or not. */
634 if (GTK_WINDOW (widget)->is_active)
635 (*gdk_env)->CallVoidMethod (gdk_env, peer,
637 (jint) AWT_WINDOW_GAINED_FOCUS,
638 (jobject) NULL, (jint) 0);
640 (*gdk_env)->CallVoidMethod (gdk_env, peer,
642 (jint) AWT_WINDOW_DEACTIVATED,
643 (jobject) NULL, (jint) 0);
648 window_focus_state_change_cb (GtkWidget *widget,
652 if (GTK_WINDOW (widget)->has_toplevel_focus)
653 (*gdk_env)->CallVoidMethod (gdk_env, peer,
655 (jint) AWT_WINDOW_ACTIVATED,
656 (jobject) NULL, (jint) 0);
658 (*gdk_env)->CallVoidMethod (gdk_env, peer,
660 (jint) AWT_WINDOW_DEACTIVATED,
661 (jobject) NULL, (jint) 0);
665 window_focus_in_cb (GtkWidget * widget,
666 GdkEventFocus *event,
669 (*gdk_env)->CallVoidMethod (gdk_env, peer,
671 (jint) AWT_WINDOW_GAINED_FOCUS,
672 (jobject) NULL, (jint) 0);
677 window_focus_out_cb (GtkWidget * widget,
678 GdkEventFocus *event,
681 (*gdk_env)->CallVoidMethod (gdk_env, peer,
683 (jint) AWT_WINDOW_LOST_FOCUS,
684 (jobject) NULL, (jint) 0);
689 window_window_state_cb (GtkWidget *widget,
695 /* Handle WINDOW_ICONIFIED and WINDOW_DEICONIFIED events. */
696 if (event->window_state.changed_mask & GDK_WINDOW_STATE_ICONIFIED)
698 /* We've either been iconified or deiconified. */
699 if (event->window_state.new_window_state & GDK_WINDOW_STATE_ICONIFIED)
701 /* We've been iconified. */
702 (*gdk_env)->CallVoidMethod (gdk_env, peer,
704 (jint) AWT_WINDOW_ICONIFIED,
705 (jobject) NULL, (jint) 0);
709 /* We've been deiconified. */
710 (*gdk_env)->CallVoidMethod (gdk_env, peer,
712 (jint) AWT_WINDOW_DEICONIFIED,
713 (jobject) NULL, (jint) 0);
717 /* Post a WINDOW_STATE_CHANGED event, passing the new frame state to
719 new_state = AWT_FRAME_STATE_NORMAL;
721 if (event->window_state.new_window_state & GDK_WINDOW_STATE_ICONIFIED)
722 new_state |= AWT_FRAME_STATE_ICONIFIED;
724 new_state |= window_get_new_state (widget);
726 (*gdk_env)->CallVoidMethod (gdk_env, peer,
728 (jint) AWT_WINDOW_STATE_CHANGED,
729 (jobject) NULL, new_state);
734 window_get_new_state (GtkWidget *widget)
736 GdkDisplay *display = gtk_widget_get_display(widget);
737 jint new_state = AWT_FRAME_STATE_NORMAL;
742 Atom *atom_list = NULL;
745 XGetWindowProperty (GDK_DISPLAY_XDISPLAY (display), GDK_WINDOW_XID (widget->window),
746 gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_STATE"),
747 0, G_MAXLONG, False, XA_ATOM, &type, &format, &atom_count,
748 &bytes_after, (guchar **)&atom_list);
752 Atom maxvert = gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_STATE_MAXIMIZED_VERT");
753 Atom maxhorz = gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_STATE_MAXIMIZED_HORZ");
756 while (i < atom_count)
758 if (atom_list[i] == maxhorz)
759 new_state |= AWT_FRAME_STATE_MAXIMIZED_HORIZ;
760 else if (atom_list[i] == maxvert)
761 new_state |= AWT_FRAME_STATE_MAXIMIZED_VERT;
772 window_property_changed_cb (GtkWidget *widget __attribute__((unused)),
773 GdkEventProperty *event,
776 unsigned long *extents;
778 static int id_set = 0;
779 static jmethodID postInsetsChangedEventID;
783 jclass gtkwindowpeer = (*gdk_env)->FindClass (gdk_env,
784 "gnu/java/awt/peer/gtk/GtkWindowPeer");
785 postInsetsChangedEventID = (*gdk_env)->GetMethodID (gdk_env,
787 "postInsetsChangedEvent",
792 if (gdk_atom_intern ("_NET_FRAME_EXTENTS", FALSE) == event->atom
793 && gdk_property_get (event->window,
794 gdk_atom_intern ("_NET_FRAME_EXTENTS", FALSE),
795 gdk_atom_intern ("CARDINAL", FALSE),
797 sizeof (unsigned long) * 4,
802 (guchar **)&extents))
803 (*gdk_env)->CallVoidMethod (gdk_env, peer,
804 postInsetsChangedEventID,
805 (jint) extents[2], /* top */
806 (jint) extents[0], /* left */
807 (jint) extents[3], /* bottom */
808 (jint) extents[1]); /* right */
814 find_layout (GtkWindow *window)
820 children = gtk_container_get_children (GTK_CONTAINER (window));
821 vbox = children->data;
822 g_assert (GTK_IS_VBOX (vbox));
824 children = gtk_container_get_children (GTK_CONTAINER (vbox));
827 layout = children->data;
828 children = children->next;
830 while (!GTK_IS_LAYOUT (layout) && children != NULL);
831 g_assert (GTK_IS_LAYOUT (layout));