X-Git-Url: http://pileus.org/git/?a=blobdiff_plain;f=gtk%2Fgtkwindow.c;h=821f9d855eb3d02c7ee78499fa929064bdc69be8;hb=2cb2d33b2086617fcb8d968f08bde3e60e09a9f7;hp=b839c580ce5c55c91447c87bbdf895d88664986e;hpb=e648c2d68fac18e27b06783f3c2aaea3f52b3c82;p=~andy%2Fgtk diff --git a/gtk/gtkwindow.c b/gtk/gtkwindow.c index b839c580c..821f9d855 100644 --- a/gtk/gtkwindow.c +++ b/gtk/gtkwindow.c @@ -2,30 +2,57 @@ * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald * * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public + * modify it under the terms of the GNU Lesser 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. + * Lesser 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * You should have received a copy of the GNU Lesser 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. */ + +/* + * Modified by the GTK+ Team and others 1997-2000. See the AUTHORS + * file for a list of people on the GTK+ Team. See the ChangeLog + * files for a list of changes. These files are distributed with + * GTK+ at ftp://ftp.gtk.org/pub/gtk/. + */ + #include #include #include "gdk/gdk.h" #include "gdk/gdkkeysyms.h" -#include "gdk/gdkx.h" + +#if defined (GDK_WINDOWING_X11) +#include "x11/gdkx.h" +#elif defined (GDK_WINDOWING_WIN32) +#include "win32/gdkwin32.h" +#elif defined (GDK_WINDOWING_NANOX) +#include "nanox/gdkprivate-nanox.h" +#elif defined (GDK_WINDOWING_FB) +#include "linux-fb/gdkfb.h" +#endif + #include "gtkprivate.h" +#include "gtkrc.h" #include "gtksignal.h" #include "gtkwindow.h" +#include "gtkbindings.h" +#include "gtkmain.h" +#include "gtkiconfactory.h" + +/* TODO: remove this define and assorted code in 1.3 and fix up the + * real culprits. + */ +#define FIXME_ZVT_ME_HARDER enum { - MOVE_RESIZE, SET_FOCUS, LAST_SIGNAL }; @@ -36,37 +63,43 @@ enum { ARG_AUTO_SHRINK, ARG_ALLOW_SHRINK, ARG_ALLOW_GROW, - ARG_WIN_POS + ARG_MODAL, + ARG_WIN_POS, + ARG_DEFAULT_WIDTH, + ARG_DEFAULT_HEIGHT, + ARG_DESTROY_WITH_PARENT }; -typedef gint (*GtkWindowSignal1) (GtkObject *object, - gpointer arg1, - gpointer arg2, - gint arg3, - gint arg4, - gpointer data); -typedef void (*GtkWindowSignal2) (GtkObject *object, - gpointer arg1, - gpointer data); - -static void gtk_window_marshal_signal_1 (GtkObject *object, - GtkSignalFunc func, - gpointer func_data, - GtkArg *args); -static void gtk_window_marshal_signal_2 (GtkObject *object, - GtkSignalFunc func, - gpointer func_data, - GtkArg *args); +typedef struct { + GdkGeometry geometry; /* Last set of geometry hints we set */ + GdkWindowHints flags; + gint width; + gint height; +} GtkWindowLastGeometryInfo; + +typedef struct { + /* Properties that the app has set on the window + */ + GdkGeometry geometry; /* Geometry hints */ + GdkWindowHints mask; + GtkWidget *widget; /* subwidget to which hints apply */ + gint width; /* Default size */ + gint height; + + GtkWindowLastGeometryInfo last; +} GtkWindowGeometryInfo; + static void gtk_window_class_init (GtkWindowClass *klass); static void gtk_window_init (GtkWindow *window); -static void gtk_window_set_arg (GtkWindow *window, +static void gtk_window_set_arg (GtkObject *object, GtkArg *arg, guint arg_id); -static void gtk_window_get_arg (GtkWindow *window, +static void gtk_window_get_arg (GtkObject *object, GtkArg *arg, guint arg_id); +static void gtk_window_shutdown (GObject *object); static void gtk_window_destroy (GtkObject *object); -static void gtk_window_finalize (GtkObject *object); +static void gtk_window_finalize (GObject *object); static void gtk_window_show (GtkWidget *widget); static void gtk_window_hide (GtkWidget *widget); static void gtk_window_map (GtkWidget *widget); @@ -76,8 +109,6 @@ static void gtk_window_size_request (GtkWidget *widget, GtkRequisition *requisition); static void gtk_window_size_allocate (GtkWidget *widget, GtkAllocation *allocation); -static gint gtk_window_expose_event (GtkWidget *widget, - GdkEventExpose *event); static gint gtk_window_configure_event (GtkWidget *widget, GdkEventConfigure *event); static gint gtk_window_key_press_event (GtkWidget *widget, @@ -94,42 +125,74 @@ static gint gtk_window_focus_out_event (GtkWidget *widget, GdkEventFocus *event); static gint gtk_window_client_event (GtkWidget *widget, GdkEventClient *event); -static gint gtk_window_need_resize (GtkContainer *container); -static gint gtk_real_window_move_resize (GtkWindow *window, - gint *x, - gint *y, - gint width, - gint height); -static void gtk_real_window_set_focus (GtkWindow *window, +static void gtk_window_check_resize (GtkContainer *container); +static void gtk_window_real_set_focus (GtkWindow *window, GtkWidget *focus); -static gint gtk_window_move_resize (GtkWidget *widget); -static void gtk_window_set_hints (GtkWidget *widget, - GtkRequisition *requisition); -static gint gtk_window_check_accelerator (GtkWindow *window, - gint key, - guint mods); +static void gtk_window_move_resize (GtkWindow *window); +static gboolean gtk_window_compare_hints (GdkGeometry *geometry_a, + guint flags_a, + GdkGeometry *geometry_b, + guint flags_b); +static void gtk_window_compute_default_size (GtkWindow *window, + guint *width, + guint *height); +static void gtk_window_constrain_size (GtkWindow *window, + GdkGeometry *geometry, + guint flags, + gint width, + gint height, + gint *new_width, + gint *new_height); +static void gtk_window_compute_hints (GtkWindow *window, + GdkGeometry *new_geometry, + guint *new_flags); +static void gtk_window_compute_reposition (GtkWindow *window, + gint new_width, + gint new_height, + gint *x, + gint *y); +static void gtk_window_read_rcfiles (GtkWidget *widget, + GdkEventClient *event); +static void gtk_window_draw (GtkWidget *widget, + GdkRectangle *area); +static void gtk_window_paint (GtkWidget *widget, + GdkRectangle *area); +static gint gtk_window_expose (GtkWidget *widget, + GdkEventExpose *event); +static void gtk_window_unset_transient_for (GtkWindow *window); +static void gtk_window_transient_parent_realized (GtkWidget *parent, + GtkWidget *window); +static void gtk_window_transient_parent_unrealized (GtkWidget *parent, + GtkWidget *window); + +static GtkWindowGeometryInfo* gtk_window_get_geometry_info (GtkWindow *window, + gboolean create); +static void gtk_window_geometry_destroy (GtkWindowGeometryInfo *info); + +static GSList *toplevel_list = NULL; static GtkBinClass *parent_class = NULL; -static guint window_signals[LAST_SIGNAL] = { 0 }; +static guint window_signals[LAST_SIGNAL] = { 0 }; -guint -gtk_window_get_type () +GtkType +gtk_window_get_type (void) { - static guint window_type = 0; + static GtkType window_type = 0; if (!window_type) { - GtkTypeInfo window_info = + static const GtkTypeInfo window_info = { "GtkWindow", sizeof (GtkWindow), sizeof (GtkWindowClass), (GtkClassInitFunc) gtk_window_class_init, (GtkObjectInitFunc) gtk_window_init, - (GtkArgSetFunc) gtk_window_set_arg, - (GtkArgGetFunc) gtk_window_get_arg, + /* reserved_1 */ NULL, + /* reserved_2 */ NULL, + (GtkClassInitFunc) NULL, }; window_type = gtk_type_unique (gtk_bin_get_type (), &window_info); @@ -141,6 +204,7 @@ gtk_window_get_type () static void gtk_window_class_init (GtkWindowClass *klass) { + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); GtkObjectClass *object_class; GtkWidgetClass *widget_class; GtkContainerClass *container_class; @@ -151,36 +215,34 @@ gtk_window_class_init (GtkWindowClass *klass) parent_class = gtk_type_class (gtk_bin_get_type ()); + gobject_class->shutdown = gtk_window_shutdown; + gobject_class->finalize = gtk_window_finalize; + gtk_object_add_arg_type ("GtkWindow::type", GTK_TYPE_WINDOW_TYPE, GTK_ARG_READWRITE, ARG_TYPE); gtk_object_add_arg_type ("GtkWindow::title", GTK_TYPE_STRING, GTK_ARG_READWRITE, ARG_TITLE); gtk_object_add_arg_type ("GtkWindow::auto_shrink", GTK_TYPE_BOOL, GTK_ARG_READWRITE, ARG_AUTO_SHRINK); gtk_object_add_arg_type ("GtkWindow::allow_shrink", GTK_TYPE_BOOL, GTK_ARG_READWRITE, ARG_ALLOW_SHRINK); gtk_object_add_arg_type ("GtkWindow::allow_grow", GTK_TYPE_BOOL, GTK_ARG_READWRITE, ARG_ALLOW_GROW); - gtk_object_add_arg_type ("GtkWindow::window_position", GTK_TYPE_ENUM, GTK_ARG_READWRITE, ARG_WIN_POS); - - window_signals[MOVE_RESIZE] = - gtk_signal_new ("move_resize", - GTK_RUN_LAST, - object_class->type, - GTK_SIGNAL_OFFSET (GtkWindowClass, move_resize), - gtk_window_marshal_signal_1, - GTK_TYPE_BOOL, 4, - GTK_TYPE_POINTER, GTK_TYPE_POINTER, - GTK_TYPE_INT, GTK_TYPE_INT); - + gtk_object_add_arg_type ("GtkWindow::modal", GTK_TYPE_BOOL, GTK_ARG_READWRITE, ARG_MODAL); + gtk_object_add_arg_type ("GtkWindow::window_position", GTK_TYPE_WINDOW_POSITION, GTK_ARG_READWRITE, ARG_WIN_POS); + gtk_object_add_arg_type ("GtkWindow::default_width", GTK_TYPE_INT, GTK_ARG_READWRITE, ARG_DEFAULT_WIDTH); + gtk_object_add_arg_type ("GtkWindow::default_height", GTK_TYPE_INT, GTK_ARG_READWRITE, ARG_DEFAULT_HEIGHT); + gtk_object_add_arg_type ("GtkWindow::destroy_with_parent", GTK_TYPE_BOOL, GTK_ARG_READWRITE, ARG_DESTROY_WITH_PARENT); + window_signals[SET_FOCUS] = gtk_signal_new ("set_focus", GTK_RUN_LAST, - object_class->type, + GTK_CLASS_TYPE (object_class), GTK_SIGNAL_OFFSET (GtkWindowClass, set_focus), - gtk_window_marshal_signal_2, + gtk_marshal_VOID__POINTER, GTK_TYPE_NONE, 1, - GTK_TYPE_POINTER); + GTK_TYPE_WIDGET); gtk_object_class_add_signals (object_class, window_signals, LAST_SIGNAL); + object_class->set_arg = gtk_window_set_arg; + object_class->get_arg = gtk_window_get_arg; object_class->destroy = gtk_window_destroy; - object_class->finalize = gtk_window_finalize; widget_class->show = gtk_window_show; widget_class->hide = gtk_window_hide; @@ -189,7 +251,6 @@ gtk_window_class_init (GtkWindowClass *klass) widget_class->realize = gtk_window_realize; widget_class->size_request = gtk_window_size_request; widget_class->size_allocate = gtk_window_size_allocate; - widget_class->expose_event = gtk_window_expose_event; widget_class->configure_event = gtk_window_configure_event; widget_class->key_press_event = gtk_window_key_press_event; widget_class->key_release_event = gtk_window_key_release_event; @@ -199,10 +260,12 @@ gtk_window_class_init (GtkWindowClass *klass) widget_class->focus_out_event = gtk_window_focus_out_event; widget_class->client_event = gtk_window_client_event; - container_class->need_resize = gtk_window_need_resize; + widget_class->draw = gtk_window_draw; + widget_class->expose_event = gtk_window_expose; + + container_class->check_resize = gtk_window_check_resize; - klass->move_resize = gtk_real_window_move_resize; - klass->set_focus = gtk_real_window_set_focus; + klass->set_focus = gtk_window_real_set_focus; } static void @@ -211,30 +274,38 @@ gtk_window_init (GtkWindow *window) GTK_WIDGET_UNSET_FLAGS (window, GTK_NO_WINDOW); GTK_WIDGET_SET_FLAGS (window, GTK_TOPLEVEL); + gtk_container_set_resize_mode (GTK_CONTAINER (window), GTK_RESIZE_QUEUE); + window->title = NULL; - window->wmclass_name = g_strdup (gdk_progname); + window->wmclass_name = g_strdup (g_get_prgname ()); window->wmclass_class = g_strdup (gdk_progclass); window->type = GTK_WINDOW_TOPLEVEL; - window->accelerator_tables = NULL; window->focus_widget = NULL; window->default_widget = NULL; window->resize_count = 0; - window->need_resize = FALSE; window->allow_shrink = FALSE; window->allow_grow = TRUE; window->auto_shrink = FALSE; window->handling_resize = FALSE; window->position = GTK_WIN_POS_NONE; window->use_uposition = TRUE; - - gtk_container_register_toplevel (GTK_CONTAINER (window)); + window->modal = FALSE; + + gtk_widget_ref (GTK_WIDGET (window)); + gtk_object_sink (GTK_OBJECT (window)); + window->has_user_ref_count = TRUE; + toplevel_list = g_slist_prepend (toplevel_list, window); } static void -gtk_window_set_arg (GtkWindow *window, +gtk_window_set_arg (GtkObject *object, GtkArg *arg, guint arg_id) { + GtkWindow *window; + + window = GTK_WINDOW (object); + switch (arg_id) { case ARG_TYPE: @@ -245,32 +316,48 @@ gtk_window_set_arg (GtkWindow *window, break; case ARG_AUTO_SHRINK: window->auto_shrink = (GTK_VALUE_BOOL (*arg) != FALSE); - gtk_window_set_hints (GTK_WIDGET (window), >K_WIDGET (window)->requisition); + gtk_widget_queue_resize (GTK_WIDGET (window)); break; case ARG_ALLOW_SHRINK: window->allow_shrink = (GTK_VALUE_BOOL (*arg) != FALSE); - gtk_window_set_hints (GTK_WIDGET (window), >K_WIDGET (window)->requisition); + gtk_widget_queue_resize (GTK_WIDGET (window)); break; case ARG_ALLOW_GROW: window->allow_grow = (GTK_VALUE_BOOL (*arg) != FALSE); - gtk_window_set_hints (GTK_WIDGET (window), >K_WIDGET (window)->requisition); + gtk_widget_queue_resize (GTK_WIDGET (window)); + break; + case ARG_MODAL: + gtk_window_set_modal (window, GTK_VALUE_BOOL (*arg)); break; case ARG_WIN_POS: - gtk_window_position (window, GTK_VALUE_ENUM (*arg)); + gtk_window_set_position (window, GTK_VALUE_ENUM (*arg)); + break; + case ARG_DEFAULT_WIDTH: + gtk_window_set_default_size (window, GTK_VALUE_INT (*arg), -2); + break; + case ARG_DEFAULT_HEIGHT: + gtk_window_set_default_size (window, -2, GTK_VALUE_INT (*arg)); + break; + case ARG_DESTROY_WITH_PARENT: + gtk_window_set_destroy_with_parent (window, GTK_VALUE_BOOL (*arg)); break; default: - arg->type = GTK_TYPE_INVALID; break; } } static void -gtk_window_get_arg (GtkWindow *window, +gtk_window_get_arg (GtkObject *object, GtkArg *arg, guint arg_id) { + GtkWindow *window; + + window = GTK_WINDOW (object); + switch (arg_id) { + GtkWindowGeometryInfo *info; case ARG_TYPE: GTK_VALUE_ENUM (*arg) = window->type; break; @@ -286,9 +373,29 @@ gtk_window_get_arg (GtkWindow *window, case ARG_ALLOW_GROW: GTK_VALUE_BOOL (*arg) = window->allow_grow; break; + case ARG_MODAL: + GTK_VALUE_BOOL (*arg) = window->modal; + break; case ARG_WIN_POS: GTK_VALUE_ENUM (*arg) = window->position; break; + case ARG_DEFAULT_WIDTH: + info = gtk_window_get_geometry_info (window, FALSE); + if (!info) + GTK_VALUE_INT (*arg) = -1; + else + GTK_VALUE_INT (*arg) = info->width; + break; + case ARG_DEFAULT_HEIGHT: + info = gtk_window_get_geometry_info (window, FALSE); + if (!info) + GTK_VALUE_INT (*arg) = -1; + else + GTK_VALUE_INT (*arg) = info->height; + break; + case ARG_DESTROY_WITH_PARENT: + GTK_VALUE_BOOL (*arg) = window->destroy_with_parent; + break; default: arg->type = GTK_TYPE_INVALID; break; @@ -300,7 +407,9 @@ gtk_window_new (GtkWindowType type) { GtkWindow *window; - window = gtk_type_new (gtk_window_get_type ()); + g_return_val_if_fail (type >= GTK_WINDOW_TOPLEVEL && type <= GTK_WINDOW_POPUP, NULL); + + window = gtk_type_new (GTK_TYPE_WINDOW); window->type = type; @@ -324,8 +433,8 @@ gtk_window_set_title (GtkWindow *window, void gtk_window_set_wmclass (GtkWindow *window, - gchar *wmclass_name, - gchar *wmclass_class) + const gchar *wmclass_name, + const gchar *wmclass_class) { g_return_if_fail (window != NULL); g_return_if_fail (GTK_IS_WINDOW (window)); @@ -344,30 +453,46 @@ void gtk_window_set_focus (GtkWindow *window, GtkWidget *focus) { - gtk_signal_emit (GTK_OBJECT (window), window_signals[SET_FOCUS], focus); + g_return_if_fail (window != NULL); + g_return_if_fail (GTK_IS_WINDOW (window)); + if (focus) + { + g_return_if_fail (GTK_IS_WIDGET (focus)); + g_return_if_fail (GTK_WIDGET_CAN_FOCUS (focus)); + } + + if ((window->focus_widget != focus) || + (focus && !GTK_WIDGET_HAS_FOCUS (focus))) + gtk_signal_emit (GTK_OBJECT (window), window_signals[SET_FOCUS], focus); } void gtk_window_set_default (GtkWindow *window, - GtkWidget *defaultw) + GtkWidget *default_widget) { g_return_if_fail (window != NULL); g_return_if_fail (GTK_IS_WINDOW (window)); - g_return_if_fail (GTK_WIDGET_CAN_DEFAULT (defaultw)); - if (window->default_widget != defaultw) + if (default_widget) + g_return_if_fail (GTK_WIDGET_CAN_DEFAULT (default_widget)); + + if (window->default_widget != default_widget) { if (window->default_widget) { - GTK_WIDGET_UNSET_FLAGS (window->default_widget, GTK_HAS_DEFAULT); + if (window->focus_widget != window->default_widget || + !GTK_WIDGET_RECEIVES_DEFAULT (window->default_widget)) + GTK_WIDGET_UNSET_FLAGS (window->default_widget, GTK_HAS_DEFAULT); gtk_widget_draw_default (window->default_widget); } - window->default_widget = defaultw; + window->default_widget = default_widget; if (window->default_widget) { - GTK_WIDGET_SET_FLAGS (window->default_widget, GTK_HAS_DEFAULT); + if (window->focus_widget == NULL || + !GTK_WIDGET_RECEIVES_DEFAULT (window->focus_widget)) + GTK_WIDGET_SET_FLAGS (window->default_widget, GTK_HAS_DEFAULT); gtk_widget_draw_default (window->default_widget); } } @@ -386,45 +511,57 @@ gtk_window_set_policy (GtkWindow *window, window->allow_grow = (allow_grow != FALSE); window->auto_shrink = (auto_shrink != FALSE); - gtk_window_set_hints (GTK_WIDGET (window), >K_WIDGET (window)->requisition); + gtk_widget_queue_resize (GTK_WIDGET (window)); } void -gtk_window_add_accelerator_table (GtkWindow *window, - GtkAcceleratorTable *table) +gtk_window_add_accel_group (GtkWindow *window, + GtkAccelGroup *accel_group) { g_return_if_fail (window != NULL); g_return_if_fail (GTK_IS_WINDOW (window)); + g_return_if_fail (accel_group != NULL); - gtk_accelerator_table_ref (table); - window->accelerator_tables = g_list_prepend (window->accelerator_tables, - table); + gtk_accel_group_attach (accel_group, GTK_OBJECT (window)); } void -gtk_window_remove_accelerator_table (GtkWindow *window, - GtkAcceleratorTable *table) +gtk_window_remove_accel_group (GtkWindow *window, + GtkAccelGroup *accel_group) { - GList *list; - g_return_if_fail (window != NULL); g_return_if_fail (GTK_IS_WINDOW (window)); + g_return_if_fail (accel_group != NULL); + + gtk_accel_group_detach (accel_group, GTK_OBJECT (window)); +} - for (list = window->accelerator_tables; list; list = list->next) +GtkAccelGroup* +gtk_window_get_default_accel_group (GtkWindow *window) +{ + GtkAccelGroup *group; + + g_return_val_if_fail (GTK_IS_WINDOW (window), NULL); + + group = gtk_object_get_data (GTK_OBJECT (window), + "gtk-accel-group"); + + if (group == NULL) { - if (list->data == table) - { - gtk_accelerator_table_unref (table); - window->accelerator_tables = g_list_remove_link (window->accelerator_tables, list); - g_list_free_1 (list); - break; - } + group = gtk_accel_group_new (); + gtk_window_add_accel_group (window, group); + gtk_object_set_data (GTK_OBJECT (window), + "gtk-accel-group", + group); + gtk_accel_group_unref (group); } + + return group; } void -gtk_window_position (GtkWindow *window, - GtkWindowPosition position) +gtk_window_set_position (GtkWindow *window, + GtkWindowPosition position) { g_return_if_fail (window != NULL); g_return_if_fail (GTK_IS_WINDOW (window)); @@ -440,7 +577,8 @@ gtk_window_activate_focus (GtkWindow *window) if (window->focus_widget) { - gtk_widget_activate (window->focus_widget); + if (GTK_WIDGET_IS_SENSITIVE (window->focus_widget)) + gtk_widget_activate (window->focus_widget); return TRUE; } @@ -453,7 +591,7 @@ gtk_window_activate_default (GtkWindow *window) g_return_val_if_fail (window != NULL, FALSE); g_return_val_if_fail (GTK_IS_WINDOW (window), FALSE); - if (window->default_widget) + if (window->default_widget && GTK_WIDGET_IS_SENSITIVE (window->default_widget)) { gtk_widget_activate (window->default_widget); return TRUE; @@ -462,93 +600,463 @@ gtk_window_activate_default (GtkWindow *window) return FALSE; } +void +gtk_window_set_modal (GtkWindow *window, + gboolean modal) +{ + g_return_if_fail (window != NULL); + g_return_if_fail (GTK_IS_WINDOW (window)); + + window->modal = modal != FALSE; + + /* adjust desired modality state */ + if (GTK_WIDGET_VISIBLE (window) && window->modal) + gtk_grab_add (GTK_WIDGET (window)); + else + gtk_grab_remove (GTK_WIDGET (window)); +} + +GList* +gtk_window_list_toplevels (void) +{ + GList *list = NULL; + GSList *slist; + + for (slist = toplevel_list; slist; slist = slist->next) + list = g_list_prepend (list, gtk_widget_ref (slist->data)); + + return list; +} + +void +gtk_window_add_embedded_xid (GtkWindow *window, guint xid) +{ + GList *embedded_windows; + + g_return_if_fail (window != NULL); + g_return_if_fail (GTK_IS_WINDOW (window)); + + embedded_windows = gtk_object_get_data (GTK_OBJECT (window), "gtk-embedded"); + if (embedded_windows) + gtk_object_remove_no_notify_by_id (GTK_OBJECT (window), + g_quark_from_static_string ("gtk-embedded")); + embedded_windows = g_list_prepend (embedded_windows, + GUINT_TO_POINTER (xid)); + + gtk_object_set_data_full (GTK_OBJECT (window), "gtk-embedded", + embedded_windows, + embedded_windows ? + (GtkDestroyNotify) g_list_free : NULL); +} + +void +gtk_window_remove_embedded_xid (GtkWindow *window, guint xid) +{ + GList *embedded_windows; + GList *node; + + g_return_if_fail (window != NULL); + g_return_if_fail (GTK_IS_WINDOW (window)); + + embedded_windows = gtk_object_get_data (GTK_OBJECT (window), "gtk-embedded"); + if (embedded_windows) + gtk_object_remove_no_notify_by_id (GTK_OBJECT (window), + g_quark_from_static_string ("gtk-embedded")); + + node = g_list_find (embedded_windows, GUINT_TO_POINTER (xid)); + if (node) + { + embedded_windows = g_list_remove_link (embedded_windows, node); + g_list_free_1 (node); + } + + gtk_object_set_data_full (GTK_OBJECT (window), + "gtk-embedded", embedded_windows, + embedded_windows ? + (GtkDestroyNotify) g_list_free : NULL); +} + +void +gtk_window_reposition (GtkWindow *window, + gint x, + gint y) +{ + GtkWindowGeometryInfo *info; + + g_return_if_fail (window != NULL); + g_return_if_fail (GTK_IS_WINDOW (window)); + + /* keep this in sync with gtk_window_compute_reposition() + */ + if (GTK_WIDGET_REALIZED (window)) + { + info = gtk_window_get_geometry_info (window, TRUE); + + if (!(info->last.flags & GDK_HINT_POS)) + { + info->last.flags |= GDK_HINT_POS; + gdk_window_set_geometry_hints (GTK_WIDGET (window)->window, + &info->last.geometry, + info->last.flags); + } + + gdk_window_move (GTK_WIDGET (window)->window, x, y); + } +} + static void -gtk_window_marshal_signal_1 (GtkObject *object, - GtkSignalFunc func, - gpointer func_data, - GtkArg *args) +gtk_window_shutdown (GObject *object) { - GtkWindowSignal1 rfunc; - gint *return_val; + GtkWindow *window; + + g_return_if_fail (GTK_IS_WINDOW (object)); + + window = GTK_WINDOW (object); - rfunc = (GtkWindowSignal1) func; - return_val = GTK_RETLOC_BOOL (args[4]); + gtk_window_set_focus (window, NULL); + gtk_window_set_default (window, NULL); - *return_val = (* rfunc) (object, - GTK_VALUE_POINTER (args[0]), - GTK_VALUE_POINTER (args[1]), - GTK_VALUE_INT (args[2]), - GTK_VALUE_INT (args[3]), - func_data); + G_OBJECT_CLASS (parent_class)->shutdown (object); } static void -gtk_window_marshal_signal_2 (GtkObject *object, - GtkSignalFunc func, - gpointer func_data, - GtkArg *args) +parent_destroyed_callback (GtkWindow *parent, GtkWindow *child) { - GtkWindowSignal2 rfunc; + gtk_widget_destroy (GTK_WIDGET (child)); +} - rfunc = (GtkWindowSignal2) func; +static void +connect_parent_destroyed (GtkWindow *window) +{ + if (window->transient_parent) + { + gtk_signal_connect (GTK_OBJECT (window->transient_parent), + "destroy", + GTK_SIGNAL_FUNC (parent_destroyed_callback), + window); + } +} - (* rfunc) (object, GTK_VALUE_POINTER (args[0]), func_data); +static void +disconnect_parent_destroyed (GtkWindow *window) +{ + if (window->transient_parent) + { + gtk_signal_disconnect_by_func (GTK_OBJECT (window->transient_parent), + GTK_SIGNAL_FUNC (parent_destroyed_callback), + window); + } } static void -gtk_window_destroy (GtkObject *object) +gtk_window_transient_parent_realized (GtkWidget *parent, + GtkWidget *window) { - GList *list; + if (GTK_WIDGET_REALIZED (window)) + gdk_window_set_transient_for (window->window, parent->window); +} - g_return_if_fail (object != NULL); - g_return_if_fail (GTK_IS_WINDOW (object)); +static void +gtk_window_transient_parent_unrealized (GtkWidget *parent, + GtkWidget *window) +{ + if (GTK_WIDGET_REALIZED (window)) + gdk_property_delete (window->window, + gdk_atom_intern ("WM_TRANSIENT_FOR", FALSE)); +} - gtk_container_unregister_toplevel (GTK_CONTAINER (object)); +static void +gtk_window_unset_transient_for (GtkWindow *window) +{ + if (window->transient_parent) + { + gtk_signal_disconnect_by_func (GTK_OBJECT (window->transient_parent), + GTK_SIGNAL_FUNC (gtk_window_transient_parent_realized), + window); + gtk_signal_disconnect_by_func (GTK_OBJECT (window->transient_parent), + GTK_SIGNAL_FUNC (gtk_window_transient_parent_unrealized), + window); + gtk_signal_disconnect_by_func (GTK_OBJECT (window->transient_parent), + GTK_SIGNAL_FUNC (gtk_widget_destroyed), + &window->transient_parent); + + if (window->destroy_with_parent) + disconnect_parent_destroyed (window); + + window->transient_parent = NULL; + } +} + +void +gtk_window_set_transient_for (GtkWindow *window, + GtkWindow *parent) +{ + g_return_if_fail (GTK_IS_WINDOW (window)); + g_return_if_fail (parent == NULL || GTK_IS_WINDOW (parent)); + g_return_if_fail (window != parent); + + + if (window->transient_parent) + { + if (GTK_WIDGET_REALIZED (window) && + GTK_WIDGET_REALIZED (window->transient_parent) && + (!parent || !GTK_WIDGET_REALIZED (parent))) + gtk_window_transient_parent_unrealized (GTK_WIDGET (window->transient_parent), + GTK_WIDGET (window)); + + gtk_window_unset_transient_for (window); + } + + window->transient_parent = parent; + + if (parent) + { + gtk_signal_connect (GTK_OBJECT (parent), "destroy", + GTK_SIGNAL_FUNC (gtk_widget_destroyed), + &window->transient_parent); + gtk_signal_connect (GTK_OBJECT (parent), "realize", + GTK_SIGNAL_FUNC (gtk_window_transient_parent_realized), + window); + gtk_signal_connect (GTK_OBJECT (parent), "unrealize", + GTK_SIGNAL_FUNC (gtk_window_transient_parent_unrealized), + window); + + if (window->destroy_with_parent) + connect_parent_destroyed (window); + + if (GTK_WIDGET_REALIZED (window) && + GTK_WIDGET_REALIZED (parent)) + gtk_window_transient_parent_realized (GTK_WIDGET (parent), + GTK_WIDGET (window)); + } +} + +/** + * gtk_window_set_destroy_with_parent: + * @window: a #GtkWindow + * @setting: whether to destroy @window with its transient parent + * + * If @setting is TRUE, then destroying the transient parent of @window + * will also destroy @window itself. This is useful for dialogs that + * shouldn't persist beyond the lifetime of the main window they're + * associated with, for example. + **/ +void +gtk_window_set_destroy_with_parent (GtkWindow *window, + gboolean setting) +{ + g_return_if_fail (GTK_IS_WINDOW (window)); + + if (window->destroy_with_parent == (setting != FALSE)) + return; + + if (window->destroy_with_parent) + { + disconnect_parent_destroyed (window); + } + else + { + connect_parent_destroyed (window); + } + + window->destroy_with_parent = setting; +} - for (list = GTK_WINDOW (object)->accelerator_tables; list; list = list->next) - gtk_accelerator_table_unref (list->data); - g_list_free (GTK_WINDOW (object)->accelerator_tables); - GTK_WINDOW (object)->accelerator_tables = NULL; +static void +gtk_window_geometry_destroy (GtkWindowGeometryInfo *info) +{ + if (info->widget) + gtk_signal_disconnect_by_func (GTK_OBJECT (info->widget), + GTK_SIGNAL_FUNC (gtk_widget_destroyed), + &info->widget); + g_free (info); +} + +static GtkWindowGeometryInfo* +gtk_window_get_geometry_info (GtkWindow *window, + gboolean create) +{ + GtkWindowGeometryInfo *info; + + info = gtk_object_get_data (GTK_OBJECT (window), "gtk-window-geometry"); + + if (!info && create) + { + info = g_new0 (GtkWindowGeometryInfo, 1); + + info->width = 0; + info->height = 0; + info->last.width = -1; + info->last.height = -1; + info->widget = NULL; + info->mask = 0; + + gtk_object_set_data_full (GTK_OBJECT (window), + "gtk-window-geometry", + info, + (GtkDestroyNotify) gtk_window_geometry_destroy); + } + + return info; +} + +void +gtk_window_set_geometry_hints (GtkWindow *window, + GtkWidget *geometry_widget, + GdkGeometry *geometry, + GdkWindowHints geom_mask) +{ + GtkWindowGeometryInfo *info; + + g_return_if_fail (window != NULL); + + info = gtk_window_get_geometry_info (window, TRUE); + + if (info->widget) + gtk_signal_disconnect_by_func (GTK_OBJECT (info->widget), + GTK_SIGNAL_FUNC (gtk_widget_destroyed), + &info->widget); + + info->widget = geometry_widget; + if (info->widget) + gtk_signal_connect (GTK_OBJECT (geometry_widget), "destroy", + GTK_SIGNAL_FUNC (gtk_widget_destroyed), + &info->widget); + + if (geometry) + info->geometry = *geometry; + + info->mask = geom_mask; + + gtk_widget_queue_resize (GTK_WIDGET (window)); +} + +void +gtk_window_set_default_size (GtkWindow *window, + gint width, + gint height) +{ + GtkWindowGeometryInfo *info; + + g_return_if_fail (GTK_IS_WINDOW (window)); + + info = gtk_window_get_geometry_info (window, TRUE); + + if (width >= 0) + info->width = width; + if (height >= 0) + info->height = height; + + gtk_widget_queue_resize (GTK_WIDGET (window)); +} + +static void +gtk_window_destroy (GtkObject *object) +{ + GtkWindow *window; - if (GTK_OBJECT_CLASS (parent_class)->destroy) - (* GTK_OBJECT_CLASS (parent_class)->destroy) (object); + g_return_if_fail (GTK_IS_WINDOW (object)); + + window = GTK_WINDOW (object); + + if (window->transient_parent) + gtk_window_set_transient_for (window, NULL); + + if (window->has_user_ref_count) + { + window->has_user_ref_count = FALSE; + gtk_widget_unref (GTK_WIDGET (window)); + } + + GTK_OBJECT_CLASS (parent_class)->destroy (object); } static void -gtk_window_finalize (GtkObject *object) +gtk_window_finalize (GObject *object) { GtkWindow *window; - g_return_if_fail (object != NULL); g_return_if_fail (GTK_IS_WINDOW (object)); window = GTK_WINDOW (object); + + toplevel_list = g_slist_remove (toplevel_list, window); + g_free (window->title); g_free (window->wmclass_name); g_free (window->wmclass_class); - GTK_OBJECT_CLASS(parent_class)->finalize (object); + G_OBJECT_CLASS(parent_class)->finalize (object); } static void gtk_window_show (GtkWidget *widget) { - g_return_if_fail (widget != NULL); - g_return_if_fail (GTK_IS_WINDOW (widget)); + GtkWindow *window = GTK_WINDOW (widget); + GtkContainer *container = GTK_CONTAINER (window); + gboolean need_resize; GTK_WIDGET_SET_FLAGS (widget, GTK_VISIBLE); - gtk_container_need_resize (GTK_CONTAINER (widget)); + + need_resize = container->need_resize || !GTK_WIDGET_REALIZED (widget); + container->need_resize = FALSE; + + if (need_resize) + { + GtkWindowGeometryInfo *info = gtk_window_get_geometry_info (window, TRUE); + GtkAllocation allocation = { 0, 0 }; + GdkGeometry new_geometry; + guint width, height, new_flags; + + /* determine default size to initially show the window with */ + gtk_widget_size_request (widget, NULL); + gtk_window_compute_default_size (window, &width, &height); + + /* save away the last default size for later comparisions */ + info->last.width = width; + info->last.height = height; + + /* constrain size to geometry */ + gtk_window_compute_hints (window, &new_geometry, &new_flags); + gtk_window_constrain_size (window, + &new_geometry, new_flags, + width, height, + &width, &height); + + /* and allocate the window */ + allocation.width = width; + allocation.height = height; + gtk_widget_size_allocate (widget, &allocation); + + if (GTK_WIDGET_REALIZED (widget)) + gdk_window_resize (widget->window, width, height); + else + gtk_widget_realize (widget); + } + + gtk_container_check_resize (container); + gtk_widget_map (widget); + + if (window->modal) + gtk_grab_add (widget); } static void gtk_window_hide (GtkWidget *widget) { + GtkWindow *window; + g_return_if_fail (widget != NULL); g_return_if_fail (GTK_IS_WINDOW (widget)); + window = GTK_WINDOW (widget); + GTK_WIDGET_UNSET_FLAGS (widget, GTK_VISIBLE); gtk_widget_unmap (widget); + + if (window->modal) + gtk_grab_remove (widget); } static void @@ -561,7 +1069,6 @@ gtk_window_map (GtkWidget *widget) GTK_WIDGET_SET_FLAGS (widget, GTK_MAPPED); - gtk_window_move_resize (widget); window = GTK_WINDOW (widget); if (window->bin.child && @@ -569,7 +1076,6 @@ gtk_window_map (GtkWidget *widget) !GTK_WIDGET_MAPPED (window->bin.child)) gtk_widget_map (window->bin.child); - gtk_window_set_hints (widget, &widget->requisition); gdk_window_show (widget->window); } @@ -582,10 +1088,12 @@ gtk_window_unmap (GtkWidget *widget) g_return_if_fail (GTK_IS_WINDOW (widget)); GTK_WIDGET_UNSET_FLAGS (widget, GTK_MAPPED); - gdk_window_hide (widget->window); + gdk_window_withdraw (widget->window); window = GTK_WINDOW (widget); window->use_uposition = TRUE; + window->resize_count = 0; + window->handling_resize = FALSE; } static void @@ -594,13 +1102,36 @@ gtk_window_realize (GtkWidget *widget) GtkWindow *window; GdkWindowAttr attributes; gint attributes_mask; - - g_return_if_fail (widget != NULL); + g_return_if_fail (GTK_IS_WINDOW (widget)); - GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED); window = GTK_WINDOW (widget); + /* ensure widget tree is properly size allocated */ + if (widget->allocation.x == -1 && + widget->allocation.y == -1 && + widget->allocation.width == 1 && + widget->allocation.height == 1) + { + GtkRequisition requisition; + GtkAllocation allocation = { 0, 0, 200, 200 }; + + gtk_widget_size_request (widget, &requisition); + if (requisition.width || requisition.height) + { + /* non-empty window */ + allocation.width = requisition.width; + allocation.height = requisition.height; + } + gtk_widget_size_allocate (widget, &allocation); + + gtk_container_queue_resize (GTK_CONTAINER (widget)); + + g_return_if_fail (!GTK_WIDGET_REALIZED (widget)); + } + + GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED); + switch (window->type) { case GTK_WINDOW_TOPLEVEL: @@ -613,7 +1144,7 @@ gtk_window_realize (GtkWidget *widget) attributes.window_type = GDK_WINDOW_TEMP; break; } - + attributes.title = window->title; attributes.wmclass_name = window->wmclass_name; attributes.wmclass_class = window->wmclass_class; @@ -629,16 +1160,22 @@ gtk_window_realize (GtkWidget *widget) GDK_LEAVE_NOTIFY_MASK | GDK_FOCUS_CHANGE_MASK | GDK_STRUCTURE_MASK); - + attributes_mask = GDK_WA_VISUAL | GDK_WA_COLORMAP; attributes_mask |= (window->title ? GDK_WA_TITLE : 0); attributes_mask |= (window->wmclass_name ? GDK_WA_WMCLASS : 0); - + widget->window = gdk_window_new (NULL, &attributes, attributes_mask); gdk_window_set_user_data (widget->window, window); widget->style = gtk_style_attach (widget->style, widget->window); gtk_style_set_background (widget->style, widget->window, GTK_STATE_NORMAL); + gtk_window_paint (widget, NULL); + + if (window->transient_parent && + GTK_WIDGET_REALIZED (window->transient_parent)) + gdk_window_set_transient_for (widget->window, + GTK_WIDGET (window->transient_parent)->window); } static void @@ -646,27 +1183,26 @@ gtk_window_size_request (GtkWidget *widget, GtkRequisition *requisition) { GtkWindow *window; + GtkBin *bin; g_return_if_fail (widget != NULL); g_return_if_fail (GTK_IS_WINDOW (widget)); g_return_if_fail (requisition != NULL); window = GTK_WINDOW (widget); + bin = GTK_BIN (window); + + requisition->width = GTK_CONTAINER (window)->border_width * 2; + requisition->height = GTK_CONTAINER (window)->border_width * 2; - if (window->bin.child) + if (bin->child && GTK_WIDGET_VISIBLE (bin->child)) { - requisition->width = GTK_CONTAINER (window)->border_width * 2; - requisition->height = GTK_CONTAINER (window)->border_width * 2; - - gtk_widget_size_request (window->bin.child, &window->bin.child->requisition); + GtkRequisition child_requisition; + + gtk_widget_size_request (bin->child, &child_requisition); - requisition->width += window->bin.child->requisition.width; - requisition->height += window->bin.child->requisition.height; - } - else - { - if (!GTK_WIDGET_VISIBLE (window)) - window->need_resize = TRUE; + requisition->width += child_requisition.width; + requisition->height += child_requisition.height; } } @@ -688,34 +1224,20 @@ gtk_window_size_allocate (GtkWidget *widget, { child_allocation.x = GTK_CONTAINER (window)->border_width; child_allocation.y = GTK_CONTAINER (window)->border_width; - child_allocation.width = allocation->width - child_allocation.x * 2; - child_allocation.height = allocation->height - child_allocation.y * 2; + child_allocation.width = + MAX (1, (gint)allocation->width - child_allocation.x * 2); + child_allocation.height = + MAX (1, (gint)allocation->height - child_allocation.y * 2); gtk_widget_size_allocate (window->bin.child, &child_allocation); } } -static gint -gtk_window_expose_event (GtkWidget *widget, - GdkEventExpose *event) -{ - g_return_val_if_fail (widget != NULL, FALSE); - g_return_val_if_fail (GTK_IS_WINDOW (widget), FALSE); - g_return_val_if_fail (event != NULL, FALSE); - - if (GTK_WIDGET_DRAWABLE (widget)) - if (GTK_WIDGET_CLASS (parent_class)->expose_event) - return (* GTK_WIDGET_CLASS (parent_class)->expose_event) (widget, event); - - return FALSE; -} - static gint gtk_window_configure_event (GtkWidget *widget, GdkEventConfigure *event) { GtkWindow *window; - GtkAllocation allocation; g_return_val_if_fail (widget != NULL, FALSE); g_return_val_if_fail (GTK_IS_WINDOW (widget), FALSE); @@ -723,32 +1245,43 @@ gtk_window_configure_event (GtkWidget *widget, window = GTK_WINDOW (widget); - /* If the window was merely moved, do nothing */ - if ((widget->allocation.width == event->width) && - (widget->allocation.height == event->height) && - (window->resize_count == 0)) - return FALSE; - - window->handling_resize = TRUE; - - allocation.x = 0; - allocation.y = 0; - allocation.width = event->width; - allocation.height = event->height; - - gtk_widget_size_allocate (widget, &allocation); - - if (window->bin.child && - GTK_WIDGET_VISIBLE (window->bin.child) && - !GTK_WIDGET_MAPPED (window->bin.child)) - gtk_widget_map (window->bin.child); - - if (window->resize_count > 0) - window->resize_count -= 1; - - window->handling_resize = FALSE; - - return FALSE; + /* we got a configure event specifying the new window size and position, + * in principle we have to distinguish 4 cases here: + * 1) the size didn't change and resize_count == 0 + * -> the window was merely moved (sometimes not even that) + * 2) the size didn't change and resize_count > 0 + * -> we requested a new size, but didn't get it + * 3) the size changed and resize_count > 0 + * -> we asked for a new size and we got one + * 4) the size changed and resize_count == 0 + * -> we got resized from outside the toolkit, and have to + * accept that size since we don't want to fight neither the + * window manager nor the user + * in the three latter cases we have to reallocate the widget tree, + * which happens in gtk_window_move_resize(), so we set a flag for + * that function and assign the new size. if resize_count > 1, + * we simply do nothing and wait for more configure events. + */ + + if (window->resize_count > 0 || + widget->allocation.width != event->width || + widget->allocation.height != event->height) + { + if (window->resize_count > 0) + window->resize_count -= 1; + + if (window->resize_count == 0) + { + window->handling_resize = TRUE; + + widget->allocation.width = event->width; + widget->allocation.height = event->height; + + gtk_widget_queue_resize (widget); + } + } + + return TRUE; } static gint @@ -757,7 +1290,7 @@ gtk_window_key_press_event (GtkWidget *widget, { GtkWindow *window; GtkDirectionType direction = 0; - gint return_val; + gboolean handled; g_return_val_if_fail (widget != NULL, FALSE); g_return_val_if_fail (GTK_IS_WINDOW (widget), FALSE); @@ -765,55 +1298,71 @@ gtk_window_key_press_event (GtkWidget *widget, window = GTK_WINDOW (widget); - return_val = FALSE; - if (window->focus_widget) - return_val = gtk_widget_event (window->focus_widget, (GdkEvent*) event); - - if (!return_val && gtk_window_check_accelerator (window, event->keyval, event->state)) - return_val = TRUE; + handled = FALSE; + + if (window->focus_widget && + window->focus_widget != widget && + GTK_WIDGET_IS_SENSITIVE (window->focus_widget)) + { + handled = gtk_widget_event (window->focus_widget, (GdkEvent*) event); + } + + if (!handled) + handled = gtk_accel_groups_activate (GTK_OBJECT (window), event->keyval, event->state); - if (!return_val) + if (!handled) { switch (event->keyval) { case GDK_space: if (window->focus_widget) { - gtk_widget_activate (window->focus_widget); - return_val = TRUE; + if (GTK_WIDGET_IS_SENSITIVE (window->focus_widget)) + gtk_widget_activate (window->focus_widget); + handled = TRUE; } break; case GDK_Return: case GDK_KP_Enter: - if (window->default_widget) + if (window->default_widget && GTK_WIDGET_IS_SENSITIVE (window->default_widget) && + (!window->focus_widget || !GTK_WIDGET_RECEIVES_DEFAULT (window->focus_widget))) { gtk_widget_activate (window->default_widget); - return_val = TRUE; + handled = TRUE; } else if (window->focus_widget) { - gtk_widget_activate (window->focus_widget); - return_val = TRUE; + if (GTK_WIDGET_IS_SENSITIVE (window->focus_widget)) + gtk_widget_activate (window->focus_widget); + handled = TRUE; } break; case GDK_Up: case GDK_Down: case GDK_Left: case GDK_Right: + case GDK_KP_Up: + case GDK_KP_Down: + case GDK_KP_Left: + case GDK_KP_Right: case GDK_Tab: case GDK_ISO_Left_Tab: switch (event->keyval) { case GDK_Up: + case GDK_KP_Up: direction = GTK_DIR_UP; break; case GDK_Down: + case GDK_KP_Down: direction = GTK_DIR_DOWN; break; case GDK_Left: + case GDK_KP_Left: direction = GTK_DIR_LEFT; break; case GDK_Right: + case GDK_KP_Right: direction = GTK_DIR_RIGHT; break; case GDK_Tab: @@ -832,12 +1381,15 @@ gtk_window_key_press_event (GtkWidget *widget, if (!GTK_CONTAINER (window)->focus_child) gtk_window_set_focus (GTK_WINDOW (widget), NULL); else - return_val = TRUE; + handled = TRUE; break; } } - return return_val; + if (!handled && GTK_WIDGET_CLASS (parent_class)->key_press_event) + handled = GTK_WIDGET_CLASS (parent_class)->key_press_event (widget, event); + + return handled; } static gint @@ -845,18 +1397,25 @@ gtk_window_key_release_event (GtkWidget *widget, GdkEventKey *event) { GtkWindow *window; - gint return_val; - + gint handled; + g_return_val_if_fail (widget != NULL, FALSE); g_return_val_if_fail (GTK_IS_WINDOW (widget), FALSE); g_return_val_if_fail (event != NULL, FALSE); - + window = GTK_WINDOW (widget); - return_val = FALSE; - if (window->focus_widget) - return_val = gtk_widget_event (window->focus_widget, (GdkEvent*) event); + handled = FALSE; + if (window->focus_widget && + window->focus_widget != widget && + GTK_WIDGET_SENSITIVE (window->focus_widget)) + { + handled = gtk_widget_event (window->focus_widget, (GdkEvent*) event); + } - return return_val; + if (!handled && GTK_WIDGET_CLASS (parent_class)->key_release_event) + handled = GTK_WIDGET_CLASS (parent_class)->key_release_event (widget, event); + + return handled; } static gint @@ -900,7 +1459,9 @@ gtk_window_focus_in_event (GtkWidget *widget, if (GTK_WIDGET_VISIBLE (widget)) { window = GTK_WINDOW (widget); - if (window->focus_widget && !GTK_WIDGET_HAS_FOCUS (window->focus_widget)) + if (window->focus_widget && + window->focus_widget != widget && + !GTK_WIDGET_HAS_FOCUS (window->focus_widget)) { fevent.type = GDK_FOCUS_CHANGE; fevent.window = window->focus_widget->window; @@ -925,7 +1486,9 @@ gtk_window_focus_out_event (GtkWidget *widget, g_return_val_if_fail (event != NULL, FALSE); window = GTK_WINDOW (widget); - if (window->focus_widget && GTK_WIDGET_HAS_FOCUS (window->focus_widget)) + if (window->focus_widget && + window->focus_widget != widget && + GTK_WIDGET_HAS_FOCUS (window->focus_widget)) { fevent.type = GDK_FOCUS_CHANGE; fevent.window = window->focus_widget->window; @@ -937,400 +1500,810 @@ gtk_window_focus_out_event (GtkWidget *widget, return FALSE; } +static GdkAtom atom_rcfiles = GDK_NONE; + static void -gtk_window_style_set_event (GtkWidget *widget, - GdkEventClient *event) -{ - GdkAtom atom_default_colors; - GtkStyle *style_newdefault; - GdkAtom realtype; - gint retfmt, retlen; - GdkColor *data, *stylecolors; - int i = 0; - GdkColormap *widget_cmap; - - atom_default_colors = gdk_atom_intern("_GTK_DEFAULT_COLORS", FALSE); - - if(gdk_property_get (GDK_ROOT_PARENT(), - atom_default_colors, - gdk_atom_intern("STRING", FALSE), - 0, - sizeof(GdkColor) * GTK_STYLE_NUM_STYLECOLORS(), - FALSE, - &realtype, - &retfmt, - &retlen, - (guchar **)&data) != TRUE) { - g_warning("gdk_property_get() failed in _GTK_STYLE_CHANGED handler\n"); - return; - } - if(retfmt != sizeof(gushort)*8) { - g_warning("retfmt (%d) != sizeof(gushort)*8 (%d)\n", retfmt, - sizeof(gushort)*8); - return; - } - /* We have the color data, now let's interpret it */ - style_newdefault = gtk_widget_get_default_style(); - gtk_style_ref(style_newdefault); - stylecolors = (GdkColor *) style_newdefault; +gtk_window_read_rcfiles (GtkWidget *widget, + GdkEventClient *event) +{ + GList *embedded_windows; - widget_cmap = gtk_widget_get_colormap(widget); - for(i = 0; i < GTK_STYLE_NUM_STYLECOLORS(); i++) { - stylecolors[i] = data[i]; - gdk_color_alloc(widget_cmap, &stylecolors[i]); - } + embedded_windows = gtk_object_get_data (GTK_OBJECT (widget), "gtk-embedded"); + if (embedded_windows) + { + GdkEventClient sev; + int i; + + for(i = 0; i < 5; i++) + sev.data.l[i] = 0; + sev.data_format = 32; + sev.message_type = atom_rcfiles; + + while (embedded_windows) + { + guint xid = GPOINTER_TO_UINT (embedded_windows->data); + gdk_event_send_client_message ((GdkEvent *) &sev, xid); + embedded_windows = embedded_windows->next; + } + } - gtk_widget_set_default_style(style_newdefault); - gtk_style_unref(style_newdefault); + if (gtk_rc_reparse_all ()) + { + /* If the above returned true, some of our RC files are out + * of date, so we need to reset all our widgets. Our other + * toplevel windows will also get the message, but by + * then, the RC file will up to date, so we have to tell + * them now. Also, we have to invalidate cached icons in + * icon sets so they get re-rendered. + */ + GList *list, *toplevels; - /* Now we need to redraw everything */ - gtk_widget_draw(widget, NULL); - gtk_widget_draw_children(widget); + _gtk_icon_set_invalidate_caches (); + + toplevels = gtk_window_list_toplevels (); + + for (list = toplevels; list; list = list->next) + { + gtk_widget_reset_rc_styles (list->data); + gtk_widget_unref (list->data); + } + g_list_free (toplevels); + } } static gint gtk_window_client_event (GtkWidget *widget, GdkEventClient *event) { - GdkAtom atom_styleset; g_return_val_if_fail (widget != NULL, FALSE); g_return_val_if_fail (GTK_IS_WINDOW (widget), FALSE); g_return_val_if_fail (event != NULL, FALSE); - atom_styleset = gdk_atom_intern("_GTK_STYLE_CHANGED", FALSE); + if (!atom_rcfiles) + atom_rcfiles = gdk_atom_intern("_GTK_READ_RCFILES", FALSE); + + if(event->message_type == atom_rcfiles) + gtk_window_read_rcfiles (widget, event); - if(event->message_type == atom_styleset) { - gtk_window_style_set_event(widget, event); - } return FALSE; } -static gint -gtk_window_need_resize (GtkContainer *container) +static void +gtk_window_check_resize (GtkContainer *container) { GtkWindow *window; - gint return_val; - - g_return_val_if_fail (container != NULL, FALSE); - g_return_val_if_fail (GTK_IS_WINDOW (container), FALSE); - return_val = FALSE; + g_return_if_fail (container != NULL); + g_return_if_fail (GTK_IS_WINDOW (container)); window = GTK_WINDOW (container); - if (window->handling_resize) - return return_val; if (GTK_WIDGET_VISIBLE (container)) + gtk_window_move_resize (window); +} + +static void +gtk_window_real_set_focus (GtkWindow *window, + GtkWidget *focus) +{ + GdkEventFocus event; + gboolean def_flags = 0; + + g_return_if_fail (window != NULL); + g_return_if_fail (GTK_IS_WINDOW (window)); + + if (window->default_widget) + def_flags = GTK_WIDGET_HAS_DEFAULT (window->default_widget); + + if (window->focus_widget) + { + event.type = GDK_FOCUS_CHANGE; + event.window = window->focus_widget->window; + event.in = FALSE; + + if (GTK_WIDGET_RECEIVES_DEFAULT (window->focus_widget) && + (window->focus_widget != window->default_widget)) + { + GTK_WIDGET_UNSET_FLAGS (window->focus_widget, GTK_HAS_DEFAULT); + /* if any widget had the default set there should be + a default_widget, but might not so this is a sanity + check */ + if (window->default_widget) + GTK_WIDGET_SET_FLAGS (window->default_widget, GTK_HAS_DEFAULT); + } + + gtk_widget_event (window->focus_widget, (GdkEvent*) &event); + } + + window->focus_widget = focus; + + if (window->focus_widget) + { + event.type = GDK_FOCUS_CHANGE; + event.window = window->focus_widget->window; + event.in = TRUE; + + if (window->default_widget) + { + if (GTK_WIDGET_RECEIVES_DEFAULT (window->focus_widget) && + (window->focus_widget != window->default_widget)) + { + if (GTK_WIDGET_CAN_DEFAULT (window->focus_widget)) + GTK_WIDGET_SET_FLAGS (window->focus_widget, GTK_HAS_DEFAULT); + GTK_WIDGET_UNSET_FLAGS (window->default_widget, GTK_HAS_DEFAULT); + } + else + { + GTK_WIDGET_SET_FLAGS (window->default_widget, GTK_HAS_DEFAULT); + } + } + + gtk_widget_event (window->focus_widget, (GdkEvent*) &event); + } + else if (window->default_widget) { - window->need_resize = TRUE; - return_val = gtk_window_move_resize (GTK_WIDGET (window)); - window->need_resize = FALSE; + GTK_WIDGET_SET_FLAGS (window->default_widget, GTK_HAS_DEFAULT); } - return return_val; + if (window->default_widget && + (def_flags != GTK_WIDGET_FLAGS (window->default_widget))) + gtk_widget_queue_draw (window->default_widget); } -static gint -gtk_real_window_move_resize (GtkWindow *window, - gint *x, - gint *y, - gint width, - gint height) +/********************************* + * Functions related to resizing * + *********************************/ + +static void +gtk_window_move_resize (GtkWindow *window) { GtkWidget *widget; - - g_return_val_if_fail (window != NULL, FALSE); - g_return_val_if_fail (GTK_IS_WINDOW (window), FALSE); - g_return_val_if_fail ((x != NULL) || (y != NULL), FALSE); - + GtkContainer *container; + GtkWindowGeometryInfo *info; + GtkWindowLastGeometryInfo saved_last_info; + GdkGeometry new_geometry; + guint new_flags; + gint x, y; + gint width, height; + gint new_width, new_height; + gboolean default_size_changed = FALSE; + gboolean hints_changed = FALSE; + + g_return_if_fail (GTK_IS_WINDOW (window)); + g_return_if_fail (GTK_WIDGET_REALIZED (window)); + widget = GTK_WIDGET (window); + container = GTK_CONTAINER (widget); + info = gtk_window_get_geometry_info (window, TRUE); + saved_last_info = info->last; + + gtk_widget_size_request (widget, NULL); + gtk_window_compute_default_size (window, &new_width, &new_height); - if ((*x != -1) && (*y != -1)) - gdk_window_move (widget->window, *x, *y); - - if ((widget->requisition.width == 0) || - (widget->requisition.height == 0)) + if (info->last.width < 0 || + info->last.width != new_width || + info->last.height != new_height) { - widget->requisition.width = 200; - widget->requisition.height = 200; + default_size_changed = TRUE; + info->last.width = new_width; + info->last.height = new_height; + + /* We need to force a reposition in this case + */ + if (window->position == GTK_WIN_POS_CENTER_ALWAYS) + window->use_uposition = TRUE; } - gdk_window_get_geometry (widget->window, NULL, NULL, &width, &height, NULL); - - if ((window->auto_shrink && - ((width != widget->requisition.width) || - (height != widget->requisition.height))) || - (width < widget->requisition.width) || - (height < widget->requisition.height)) + /* Compute new set of hints for the window + */ + gtk_window_compute_hints (window, &new_geometry, &new_flags); + if (!gtk_window_compare_hints (&info->last.geometry, info->last.flags, + &new_geometry, new_flags)) { - window->resize_count += 1; - gdk_window_resize (widget->window, - widget->requisition.width, - widget->requisition.height); + hints_changed = TRUE; + info->last.geometry = new_geometry; + info->last.flags = new_flags; } - else + + /* From the default size and the allocation, figure out the size + * the window should be. + */ + if (!default_size_changed || + (!window->auto_shrink && + new_width <= widget->allocation.width && + new_height <= widget->allocation.height)) { - /* The window hasn't changed size but one of its children - * queued a resize request. Which means that the allocation - * is not sufficient for the requisition of some child. - * We've already performed a size request at this point, - * so we simply need to run through the list of resize - * widgets and reallocate their sizes appropriately. We - * make the optimization of not performing reallocation - * for a widget who also has a parent in the resize widgets - * list. GTK_RESIZE_NEEDED is used for flagging those - * parents inside this function. - */ - GSList *resize_widgets; - GSList *resize_containers; - GSList *node; + new_width = widget->allocation.width; + new_height = widget->allocation.height; + } + + /* constrain the window size to the specified geometry */ + gtk_window_constrain_size (window, + &new_geometry, new_flags, + new_width, new_height, + &new_width, &new_height); + + /* compute new window position if a move is required + */ + gtk_window_compute_reposition (window, new_width, new_height, &x, &y); + if (x != 1 && y != -1 && !(new_flags & GDK_HINT_POS)) + { + new_flags |= GDK_HINT_POS; + hints_changed = TRUE; + } + + + /* handle actual resizing: + * - handle reallocations due to configure events + * - figure whether we need to request a new window size + * - handle simple resizes within our widget tree + * - reposition window if neccessary + */ + width = widget->allocation.width; + height = widget->allocation.height; + + if (window->handling_resize) + { + GtkAllocation allocation; - resize_widgets = GTK_CONTAINER (window)->resize_widgets; - GTK_CONTAINER (window)->resize_widgets = NULL; + /* if we are just responding to a configure event, which + * might be due to a resize by the window manager, the + * user, or a response to a resizing request we made + * earlier, we go ahead, allocate the new size and we're done + * (see gtk_window_configure_event() for more details). + */ - for (node = resize_widgets; node; node = node->next) - { - widget = node->data; - - GTK_PRIVATE_UNSET_FLAG (widget, GTK_RESIZE_NEEDED); - - while (widget && widget->parent && - ((widget->allocation.width < widget->requisition.width) || - (widget->allocation.height < widget->requisition.height))) - widget = widget->parent; - - GTK_PRIVATE_SET_FLAG (widget, GTK_RESIZE_NEEDED); - node->data = widget; - } + window->handling_resize = FALSE; - resize_containers = NULL; + allocation = widget->allocation; - for (node = resize_widgets; node; node = node->next) + gtk_widget_size_allocate (widget, &allocation); + gtk_widget_queue_draw (widget); + +#ifdef FIXME_ZVT_ME_HARDER + if ((default_size_changed || hints_changed) && (width != new_width || height != new_height)) { - GtkWidget *resize_container; - - widget = node->data; + /* We could be here for two reasons + * 1) We coincidentally got a resize while handling + * another resize. + * 2) Our computation of default_size_changed was completely + * screwed up, probably because one of our children + * is broken (i.e. changes requisition during + * size allocation). It's probably a zvt widget. + * + * For 1), we could just go ahead and ask for the + * new size right now, but doing that for 2) + * might well be fighting the user (and can even + * trigger a loop). Since we really don't want to + * do that, we requeue a resize in hopes that + * by the time it gets handled, the child has seen + * the light and is willing to go along with the + * new size. (this happens for the zvt widget, since + * the size_allocate() above will have stored the + * requisition corresponding to the new size in the + * zvt widget) + * + * This doesn't buy us anything for 1), but it shouldn't + * hurt us too badly, since it is what would have + * happened if we had gotten the configure event before + * the new size had been set. + */ - if (!GTK_WIDGET_RESIZE_NEEDED (widget)) - continue; - - resize_container = widget->parent; - - if (resize_container) - { - GTK_PRIVATE_UNSET_FLAG (widget, GTK_RESIZE_NEEDED); - widget = resize_container->parent; - - while (widget) - { - if (GTK_WIDGET_RESIZE_NEEDED (widget)) - { - GTK_PRIVATE_UNSET_FLAG (resize_container, GTK_RESIZE_NEEDED); - resize_container = widget; - } - widget = widget->parent; - } - } - else - resize_container = widget; + if (x != -1 && y != -1) + gdk_window_move (widget->window, x, y); + + /* we have to preserve the values and flags that are used + * for computation of default_size_changed and hints_changed + */ + + info->last = saved_last_info; - if (!g_slist_find (resize_containers, resize_container)) - resize_containers = g_slist_prepend (resize_containers, - resize_container); + gtk_widget_queue_resize (widget); + + return; } - g_slist_free (resize_widgets); +#endif /* FIXME_ZVT_ME_HARDER */ + } + + /* Now set hints if necessary + */ + if (hints_changed) + gdk_window_set_geometry_hints (widget->window, + &new_geometry, + new_flags); + + if ((default_size_changed || hints_changed) && + (width != new_width || height != new_height)) + { + /* given that (width != new_width || height != new_height), we are in one + * of the following situations: + * + * default_size_changed + * our requisition has changed and we need a different window size, + * so we request it from the window manager. + * + * !default_size_changed + * the window manager wouldn't assign us the size we requested, in this + * case we don't try to request a new size with every resize. + * + * !default_size_changed && hints_changed + * the window manager rejects our size, but we have just changed the + * window manager hints, so there's a certain chance our request will + * be honoured this time, so we try again. + */ - for (node = resize_containers; node; node = node->next) - { - widget = node->data; - - GTK_PRIVATE_UNSET_FLAG (widget, GTK_RESIZE_NEEDED); - gtk_widget_size_allocate (widget, &widget->allocation); - gtk_widget_queue_draw (widget); - } - g_slist_free (resize_containers); + /* request a new window size */ + if (x != -1 && y != -1) + gdk_window_move_resize (GTK_WIDGET (window)->window, x, y, new_width, new_height); + else + gdk_window_resize (GTK_WIDGET (window)->window, new_width, new_height); + window->resize_count += 1; + + /* we are now awaiting the new configure event in response to our + * resizing request. the configure event will cause a new resize + * with ->handling_resize=TRUE. + * until then, we want to + * - discard expose events + * - coalesce resizes for our children + * - defer any window resizes until the configure event arrived + * to achive this, we queue a resize for the window, but remove its + * resizing handler, so resizing will not be handled from the next + * idle handler but when the configure event arrives. + * + * FIXME: we should also dequeue the pending redraws here, since + * we handle those ourselves in ->handling_resize==TRUE. + */ + gtk_widget_queue_resize (GTK_WIDGET (container)); + if (container->resize_mode == GTK_RESIZE_QUEUE) + gtk_container_dequeue_resize_handler (container); + } + else + { + if (x != -1 && y != -1) + gdk_window_move (widget->window, x, y); + + if (container->resize_widgets) + gtk_container_resize_children (GTK_CONTAINER (window)); } - - return FALSE; } -static gint -gtk_window_move_resize (GtkWidget *widget) +/* Compare two sets of Geometry hints for equality. + */ +static gboolean +gtk_window_compare_hints (GdkGeometry *geometry_a, + guint flags_a, + GdkGeometry *geometry_b, + guint flags_b) { - GtkWindow *window; - gint x, y; - gint width, height; - gint screen_width; - gint screen_height; - gint return_val; + if (flags_a != flags_b) + return FALSE; + + if ((flags_a & GDK_HINT_MIN_SIZE) && + (geometry_a->min_width != geometry_b->min_width || + geometry_a->min_height != geometry_b->min_height)) + return FALSE; - g_return_val_if_fail (widget != NULL, FALSE); - g_return_val_if_fail (GTK_IS_WINDOW (widget), FALSE); + if ((flags_a & GDK_HINT_MAX_SIZE) && + (geometry_a->max_width != geometry_b->max_width || + geometry_a->max_height != geometry_b->max_height)) + return FALSE; - return_val = FALSE; + if ((flags_a & GDK_HINT_BASE_SIZE) && + (geometry_a->base_width != geometry_b->base_width || + geometry_a->base_height != geometry_b->base_height)) + return FALSE; - if (GTK_WIDGET_REALIZED (widget)) - { - window = GTK_WINDOW (widget); + if ((flags_a & GDK_HINT_ASPECT) && + (geometry_a->min_aspect != geometry_b->min_aspect || + geometry_a->max_aspect != geometry_b->max_aspect)) + return FALSE; - /* Remember old size, to know if we have to reset hints */ - width = widget->requisition.width; - height = widget->requisition.height; - gtk_widget_size_request (widget, &widget->requisition); + if ((flags_a & GDK_HINT_RESIZE_INC) && + (geometry_a->width_inc != geometry_b->width_inc || + geometry_a->height_inc != geometry_b->height_inc)) + return FALSE; - if (GTK_WIDGET_MAPPED (widget) && - (width != widget->requisition.width || - height != widget->requisition.height)) - gtk_window_set_hints (widget, &widget->requisition); + return TRUE; +} - x = -1; - y = -1; - width = widget->requisition.width; - height = widget->requisition.height; +/* Compute the default_size for a window. The result will + * be stored in *width and *height. The default size is + * the size the window should have when initially mapped. + * This routine does not attempt to constrain the size + * to obey the geometry hints - that must be done elsewhere. + */ +static void +gtk_window_compute_default_size (GtkWindow *window, + guint *width, + guint *height) +{ + GtkRequisition requisition; + GtkWindowGeometryInfo *info; + + gtk_widget_get_child_requisition (GTK_WIDGET (window), &requisition); + *width = requisition.width; + *height = requisition.height; - if (window->use_uposition) - switch (window->position) - { - case GTK_WIN_POS_CENTER: - x = (gdk_screen_width () - width) / 2; - y = (gdk_screen_height () - height) / 2; - gtk_widget_set_uposition (widget, x, y); - break; - case GTK_WIN_POS_MOUSE: - gdk_window_get_pointer (NULL, &x, &y, NULL); - - x -= width / 2; - y -= height / 2; - - screen_width = gdk_screen_width (); - screen_height = gdk_screen_height (); - - if (x < 0) - x = 0; - else if (x > (screen_width - width)) - x = screen_width - width; - - if (y < 0) - y = 0; - else if (y > (screen_height - height)) - y = screen_height - height; - - gtk_widget_set_uposition (widget, x, y); - break; - } - - gtk_signal_emit (GTK_OBJECT (widget), window_signals[MOVE_RESIZE], - &x, &y, width, height, &return_val); + info = gtk_window_get_geometry_info (window, FALSE); + + if (*width == 0 && *height == 0) + { + /* empty window */ + *width = 200; + *height = 200; + } + + if (info) + { + *width = info->width > 0 ? info->width : *width; + *height = info->height > 0 ? info->height : *height; } - - return return_val; } -static void -gtk_real_window_set_focus (GtkWindow *window, - GtkWidget *focus) +/* Constrain a window size to obey the hints passed in geometry + * and flags. The result will be stored in *new_width and *new_height + * + * This routine is partially borrowed from fvwm. + * + * Copyright 1993, Robert Nation + * You may use this code for any purpose, as long as the original + * copyright remains in the source code and all documentation + * + * which in turn borrows parts of the algorithm from uwm + */ +static void +gtk_window_constrain_size (GtkWindow *window, + GdkGeometry *geometry, + guint flags, + gint width, + gint height, + gint *new_width, + gint *new_height) { - GdkEventFocus event; + gint min_width = 0; + gint min_height = 0; + gint base_width = 0; + gint base_height = 0; + gint xinc = 1; + gint yinc = 1; + gint max_width = G_MAXINT; + gint max_height = G_MAXINT; + +#define FLOOR(value, base) ( ((gint) ((value) / (base))) * (base) ) - g_return_if_fail (window != NULL); - g_return_if_fail (GTK_IS_WINDOW (window)); + if ((flags & GDK_HINT_BASE_SIZE) && (flags & GDK_HINT_MIN_SIZE)) + { + base_width = geometry->base_width; + base_height = geometry->base_height; + min_width = geometry->min_width; + min_height = geometry->min_height; + } + else if (flags & GDK_HINT_BASE_SIZE) + { + base_width = geometry->base_width; + base_height = geometry->base_height; + min_width = geometry->base_width; + min_height = geometry->base_height; + } + else if (flags & GDK_HINT_MIN_SIZE) + { + base_width = geometry->min_width; + base_height = geometry->min_height; + min_width = geometry->min_width; + min_height = geometry->min_height; + } - if (focus && !GTK_WIDGET_CAN_FOCUS (focus)) - return; + if (flags & GDK_HINT_MAX_SIZE) + { + max_width = geometry->max_width ; + max_height = geometry->max_height; + } - if (window->focus_widget != focus) + if (flags & GDK_HINT_RESIZE_INC) { - if (window->focus_widget) - { - event.type = GDK_FOCUS_CHANGE; - event.window = window->focus_widget->window; - event.in = FALSE; + xinc = MAX (xinc, geometry->width_inc); + yinc = MAX (yinc, geometry->height_inc); + } + + /* clamp width and height to min and max values + */ + width = CLAMP (width, min_width, max_width); + height = CLAMP (height, min_height, max_height); + + /* shrink to base + N * inc + */ + width = base_width + FLOOR (width - base_width, xinc); + height = base_height + FLOOR (height - base_height, yinc); + + /* constrain aspect ratio, according to: + * + * width + * min_aspect <= -------- <= max_aspect + * height + */ + + if (flags & GDK_HINT_ASPECT && + geometry->min_aspect > 0 && + geometry->max_aspect > 0) + { + gint delta; - gtk_widget_event (window->focus_widget, (GdkEvent*) &event); + if (geometry->min_aspect * height > width) + { + delta = FLOOR (height - width * geometry->min_aspect, yinc); + if (height - delta >= min_height) + height -= delta; + else + { + delta = FLOOR (height * geometry->min_aspect - width, xinc); + if (width + delta <= max_width) + width += delta; + } } - - window->focus_widget = focus; - - if (window->focus_widget) + + if (geometry->max_aspect * height < width) { - event.type = GDK_FOCUS_CHANGE; - event.window = window->focus_widget->window; - event.in = TRUE; - - gtk_widget_event (window->focus_widget, (GdkEvent*) &event); + delta = FLOOR (width - height * geometry->max_aspect, xinc); + if (width - delta >= min_width) + width -= delta; + else + { + delta = FLOOR (width / geometry->max_aspect - height, yinc); + if (height + delta <= max_height) + height += delta; + } } } + +#undef FLOOR + + *new_width = width; + *new_height = height; } +/* Compute the set of geometry hints and flags for a window + * based on the application set geometry, and requisiition + * of the window. gtk_widget_size_request() must have been + * called first. + */ static void -gtk_window_set_hints (GtkWidget *widget, - GtkRequisition *requisition) +gtk_window_compute_hints (GtkWindow *window, + GdkGeometry *new_geometry, + guint *new_flags) { - GtkWindow *window; + GtkWidget *widget; GtkWidgetAuxInfo *aux_info; - gint flags; gint ux, uy; + gint extra_width = 0; + gint extra_height = 0; + GtkWindowGeometryInfo *geometry_info; + GtkRequisition requisition; - g_return_if_fail (widget != NULL); - g_return_if_fail (GTK_IS_WINDOW (widget)); - g_return_if_fail (requisition != NULL); + g_return_if_fail (GTK_IS_WINDOW (window)); + + widget = GTK_WIDGET (window); + + gtk_widget_get_child_requisition (widget, &requisition); + geometry_info = gtk_window_get_geometry_info (GTK_WINDOW (widget), FALSE); - if (GTK_WIDGET_REALIZED (widget)) + g_return_if_fail (geometry_info != NULL); + + *new_flags = geometry_info->mask; + *new_geometry = geometry_info->geometry; + + if (geometry_info->widget) { - window = GTK_WINDOW (widget); + extra_width = widget->requisition.width - geometry_info->widget->requisition.width; + extra_height = widget->requisition.height - geometry_info->widget->requisition.height; + } + + ux = 0; + uy = 0; + + aux_info = gtk_object_get_data (GTK_OBJECT (widget), "gtk-aux-info"); + if (aux_info && (aux_info->x != -1) && (aux_info->y != -1)) + { + ux = aux_info->x; + uy = aux_info->y; + *new_flags |= GDK_HINT_POS; + } + + if (*new_flags & GDK_HINT_BASE_SIZE) + { + new_geometry->base_width += extra_width; + new_geometry->base_height += extra_height; + } + else if (!(*new_flags & GDK_HINT_MIN_SIZE) && + (*new_flags & GDK_HINT_RESIZE_INC) && + ((extra_width != 0) || (extra_height != 0))) + { + *new_flags |= GDK_HINT_BASE_SIZE; + + new_geometry->base_width = extra_width; + new_geometry->base_height = extra_height; + } + + if (*new_flags & GDK_HINT_MIN_SIZE) + { + if (new_geometry->min_width < 0) + new_geometry->min_width = requisition.width; + else + new_geometry->min_width += extra_width; + + if (new_geometry->min_height < 0) + new_geometry->min_width = requisition.height; + else + new_geometry->min_height += extra_height; + } + else if (!window->allow_shrink) + { + *new_flags |= GDK_HINT_MIN_SIZE; + + new_geometry->min_width = requisition.width; + new_geometry->min_height = requisition.height; + } + + if (*new_flags & GDK_HINT_MAX_SIZE) + { + if (new_geometry->max_width < 0) + new_geometry->max_width = requisition.width; + else + new_geometry->max_width += extra_width; + + if (new_geometry->max_height < 0) + new_geometry->max_width = requisition.height; + else + new_geometry->max_height += extra_height; + } + else if (!window->allow_grow) + { + *new_flags |= GDK_HINT_MAX_SIZE; + + new_geometry->max_width = requisition.width; + new_geometry->max_height = requisition.height; + } +} + +/* Compute a new position for the window based on a new + * size. *x and *y will be set to the new coordinates, or to -1 if the + * window does not need to be moved + */ +static void +gtk_window_compute_reposition (GtkWindow *window, + gint new_width, + gint new_height, + gint *x, + gint *y) +{ + GtkWidget *widget; + GtkWindowPosition pos; + GtkWidget *parent_widget; + + widget = GTK_WIDGET (window); - flags = 0; - ux = 0; - uy = 0; + *x = -1; + *y = -1; - aux_info = gtk_object_get_data (GTK_OBJECT (widget), "gtk-aux-info"); - if (aux_info && (aux_info->x != -1) && (aux_info->y != -1)) + parent_widget = (GtkWidget*) window->transient_parent; + + pos = window->position; + if (pos == GTK_WIN_POS_CENTER_ON_PARENT && + (parent_widget == NULL || + !GTK_WIDGET_MAPPED (parent_widget))) + pos = GTK_WIN_POS_NONE; + + switch (pos) + { + case GTK_WIN_POS_CENTER: + case GTK_WIN_POS_CENTER_ALWAYS: + if (window->use_uposition) { - ux = aux_info->x; - uy = aux_info->y; - flags |= GDK_HINT_POS; + gint screen_width = gdk_screen_width (); + gint screen_height = gdk_screen_height (); + + *x = (screen_width - new_width) / 2; + *y = (screen_height - new_height) / 2; } - if (!window->allow_shrink) - flags |= GDK_HINT_MIN_SIZE; - if (!window->allow_grow) - flags |= GDK_HINT_MAX_SIZE; + break; - gdk_window_set_hints (widget->window, ux, uy, - requisition->width, requisition->height, - requisition->width, requisition->height, - flags); + case GTK_WIN_POS_CENTER_ON_PARENT: + if (window->use_uposition) + { + gint ox, oy; + gdk_window_get_origin (parent_widget->window, + &ox, &oy); + + *x = ox + (parent_widget->allocation.width - new_width) / 2; + *y = oy + (parent_widget->allocation.height - new_height) / 2; + } + break; - if (window->use_uposition && (flags & GDK_HINT_POS)) + case GTK_WIN_POS_MOUSE: + if (window->use_uposition) + { + gint screen_width = gdk_screen_width (); + gint screen_height = gdk_screen_height (); + + gdk_window_get_pointer (NULL, x, y, NULL); + *x -= new_width / 2; + *y -= new_height / 2; + *x = CLAMP (*x, 0, screen_width - new_width); + *y = CLAMP (*y, 0, screen_height - new_height); + } + break; + default: + if (window->use_uposition) { - window->use_uposition = FALSE; - gdk_window_move (widget->window, ux, uy); + GtkWidgetAuxInfo *aux_info; + + aux_info = gtk_object_get_data (GTK_OBJECT (window), "gtk-aux-info"); + if (aux_info && + aux_info->x != -1 && aux_info->y != -1 && + aux_info->x != -2 && aux_info->y != -2) + { + *x = aux_info->x; + *y = aux_info->y; + } } + break; + } + + if (*x != -1 && *y != -1) + { + GtkWidgetAuxInfo *aux_info; + + /* we handle necessary window positioning by hand here, + * so we can coalesce the window movement with possible + * resizes to get only one configure event. + * keep this in sync with gtk_widget_set_uposition() + * and gtk_window_reposition(). + */ + gtk_widget_set_uposition (widget, -1, -1); /* ensure we have aux_info */ + + aux_info = gtk_object_get_data (GTK_OBJECT (widget), "gtk-aux-info"); + aux_info->x = *x; + aux_info->y = *y; + + window->use_uposition = FALSE; } } -static gint -gtk_window_check_accelerator (GtkWindow *window, - gint key, - guint mods) +/*********************** + * Redrawing functions * + ***********************/ + +static void +gtk_window_paint (GtkWidget *widget, + GdkRectangle *area) { - GtkAcceleratorTable *table; - GList *tmp; + gtk_paint_flat_box (widget->style, widget->window, GTK_STATE_NORMAL, + GTK_SHADOW_NONE, area, widget, "base", 0, 0, -1, -1); +} - if ((key >= 0x20) && (key <= 0xFF)) - { - tmp = window->accelerator_tables; - while (tmp) - { - table = tmp->data; - tmp = tmp->next; +static gint +gtk_window_expose (GtkWidget *widget, + GdkEventExpose *event) +{ + g_return_val_if_fail (widget != NULL, FALSE); + g_return_val_if_fail (GTK_IS_WINDOW (widget), FALSE); + g_return_val_if_fail (event != NULL, FALSE); - if (gtk_accelerator_table_check (table, key, mods)) - return TRUE; - } + if (!GTK_WIDGET_APP_PAINTABLE (widget)) + gtk_window_paint (widget, &event->area); + + if (GTK_WIDGET_CLASS (parent_class)->expose_event) + return GTK_WIDGET_CLASS (parent_class)->expose_event (widget, event); - if (gtk_accelerator_table_check (NULL, key, mods)) - return TRUE; - } + return TRUE; +} - return FALSE; +static void +gtk_window_draw (GtkWidget *widget, + GdkRectangle *area) +{ + if (!GTK_WIDGET_APP_PAINTABLE (widget)) + gtk_window_paint (widget, area); + + if (GTK_WIDGET_CLASS (parent_class)->draw) + (* GTK_WIDGET_CLASS (parent_class)->draw) (widget, area); }