gtk2/gtk/gtkwindow.c
Owen Taylor 2c613ffd95 Don't reference the widgets in the resulting list. (#11821) This makes
Mon Mar 26 15:48:45 2001  Owen Taylor  <otaylor@redhat.com>

	* gtk/gtkwindow.c (gtk_window_list_toplevels): Don't
	reference the widgets in the resulting list. (#11821)
	This makes this consistent with gtk_container_children.

	* gtk/gtkwindow.c gtk/gtkwidget.c: Fix up the two
	uses of gtk_window_list_toplevels.
2001-03-26 20:54:17 +00:00

3491 lines
98 KiB
C

/* GTK - The GIMP Toolkit
* 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 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
* Lesser General Public License for more details.
*
* 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 <string.h>
#include <limits.h>
#include "gdk/gdk.h"
#include "gdk/gdkkeysyms.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 "gtkwindow-decorate.h"
#include "gtkbindings.h"
#include "gtkmain.h"
#include "gtkiconfactory.h"
#include "gtkintl.h"
/* TODO: remove this define and assorted code in 1.3 and fix up the
* real culprits.
*/
#define FIXME_ZVT_ME_HARDER
enum {
SET_FOCUS,
FRAME_EVENT,
LAST_SIGNAL
};
enum {
PROP_0,
/* Construct */
PROP_TYPE,
/* Style Props */
PROP_TITLE,
PROP_AUTO_SHRINK,
PROP_ALLOW_SHRINK,
PROP_ALLOW_GROW,
PROP_MODAL,
PROP_WIN_POS,
PROP_DEFAULT_WIDTH,
PROP_DEFAULT_HEIGHT,
PROP_DESTROY_WITH_PARENT,
LAST_ARG
};
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;
typedef struct {
GtkWindow *window;
guint keyval;
GSList *targets;
} GtkWindowMnemonic;
static void gtk_window_class_init (GtkWindowClass *klass);
static void gtk_window_init (GtkWindow *window);
static void gtk_window_shutdown (GObject *object);
static void gtk_window_destroy (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);
static void gtk_window_unmap (GtkWidget *widget);
static void gtk_window_realize (GtkWidget *widget);
static void gtk_window_unrealize (GtkWidget *widget);
static void gtk_window_size_request (GtkWidget *widget,
GtkRequisition *requisition);
static void gtk_window_size_allocate (GtkWidget *widget,
GtkAllocation *allocation);
static gint gtk_window_event (GtkWidget *widget,
GdkEvent *event);
static gboolean gtk_window_frame_event (GtkWidget *widget,
GdkEvent *event);
static gint gtk_window_configure_event (GtkWidget *widget,
GdkEventConfigure *event);
static gint gtk_window_key_press_event (GtkWidget *widget,
GdkEventKey *event);
static gint gtk_window_key_release_event (GtkWidget *widget,
GdkEventKey *event);
static gint gtk_window_enter_notify_event (GtkWidget *widget,
GdkEventCrossing *event);
static gint gtk_window_leave_notify_event (GtkWidget *widget,
GdkEventCrossing *event);
static gint gtk_window_focus_in_event (GtkWidget *widget,
GdkEventFocus *event);
static gint gtk_window_focus_out_event (GtkWidget *widget,
GdkEventFocus *event);
static gint gtk_window_client_event (GtkWidget *widget,
GdkEventClient *event);
static void gtk_window_check_resize (GtkContainer *container);
static gint gtk_window_focus (GtkContainer *container,
GtkDirectionType direction);
static void gtk_window_real_set_focus (GtkWindow *window,
GtkWidget *focus);
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_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 GHashTable *mnemonic_hash_table = NULL;
static GtkBinClass *parent_class = NULL;
static guint window_signals[LAST_SIGNAL] = { 0 };
static void gtk_window_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec);
static void gtk_window_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec);
static guint
mnemonic_hash (gconstpointer key)
{
const GtkWindowMnemonic *k;
guint h;
k = (GtkWindowMnemonic *)key;
h = (gulong) k->window;
h ^= k->keyval << 16;
h ^= k->keyval >> 16;
return h;
}
static gboolean
mnemonic_equal (gconstpointer a, gconstpointer b)
{
const GtkWindowMnemonic *ka;
const GtkWindowMnemonic *kb;
ka = (GtkWindowMnemonic *)a;
kb = (GtkWindowMnemonic *)b;
return
(ka->window == kb->window) &&
(ka->keyval == kb->keyval);
}
GtkType
gtk_window_get_type (void)
{
static GtkType window_type = 0;
if (!window_type)
{
static const GtkTypeInfo window_info =
{
"GtkWindow",
sizeof (GtkWindow),
sizeof (GtkWindowClass),
(GtkClassInitFunc) gtk_window_class_init,
(GtkObjectInitFunc) gtk_window_init,
/* reserved_1 */ NULL,
/* reserved_2 */ NULL,
(GtkClassInitFunc) NULL,
};
window_type = gtk_type_unique (gtk_bin_get_type (), &window_info);
}
return window_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;
object_class = (GtkObjectClass*) klass;
widget_class = (GtkWidgetClass*) klass;
container_class = (GtkContainerClass*) klass;
parent_class = gtk_type_class (gtk_bin_get_type ());
gobject_class->shutdown = gtk_window_shutdown;
gobject_class->finalize = gtk_window_finalize;
gobject_class->set_property = gtk_window_set_property;
gobject_class->get_property = gtk_window_get_property;
object_class->destroy = gtk_window_destroy;
widget_class->show = gtk_window_show;
widget_class->hide = gtk_window_hide;
widget_class->map = gtk_window_map;
widget_class->unmap = gtk_window_unmap;
widget_class->realize = gtk_window_realize;
widget_class->unrealize = gtk_window_unrealize;
widget_class->size_request = gtk_window_size_request;
widget_class->size_allocate = gtk_window_size_allocate;
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;
widget_class->enter_notify_event = gtk_window_enter_notify_event;
widget_class->leave_notify_event = gtk_window_leave_notify_event;
widget_class->focus_in_event = gtk_window_focus_in_event;
widget_class->focus_out_event = gtk_window_focus_out_event;
widget_class->client_event = gtk_window_client_event;
widget_class->expose_event = gtk_window_expose;
container_class->check_resize = gtk_window_check_resize;
container_class->focus = gtk_window_focus;
klass->set_focus = gtk_window_real_set_focus;
klass->frame_event = gtk_window_frame_event;
/* Construct */
g_object_class_install_property (gobject_class,
PROP_TYPE,
g_param_spec_enum ("type",
_("Window Type"),
_("The type of the window"),
GTK_TYPE_WINDOW_TYPE,
GTK_WINDOW_TOPLEVEL,
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
/* Style Props */
g_object_class_install_property (gobject_class,
PROP_TITLE,
g_param_spec_string ("title",
_("Window Title"),
_("The title of the window"),
NULL,
G_PARAM_READWRITE));
g_object_class_install_property (gobject_class,
PROP_AUTO_SHRINK,
g_param_spec_boolean ("auto_shrink",
_("Auto Shrink"),
_("If TRUE, the window automatically shrinks to its size request anytime a resize occurs. Don't use this feature, it makes no sense."),
FALSE,
G_PARAM_READWRITE));
g_object_class_install_property (gobject_class,
PROP_ALLOW_SHRINK,
g_param_spec_boolean ("allow_shrink",
_("Allow Shrink"),
_("If TRUE, the window has no mimimum size. Don't use this feature, it makes no sense."),
FALSE,
G_PARAM_READWRITE));
g_object_class_install_property (gobject_class,
PROP_ALLOW_GROW,
g_param_spec_boolean ("allow_grow",
_("Allow Grow"),
_("If TRUE, users can expand the window beyond its minimum size."),
TRUE,
G_PARAM_READWRITE));
g_object_class_install_property (gobject_class,
PROP_MODAL,
g_param_spec_boolean ("modal",
_("Modal"),
_("If TRUE, the window is modal (other windows are not usable while this one is up)."),
FALSE,
G_PARAM_READWRITE));
g_object_class_install_property (gobject_class,
PROP_WIN_POS,
g_param_spec_enum ("window_position",
_("Window Position"),
_("The initial position of the window."),
GTK_TYPE_WINDOW_POSITION,
GTK_WIN_POS_NONE,
G_PARAM_READWRITE));
g_object_class_install_property (gobject_class,
PROP_DEFAULT_WIDTH,
g_param_spec_int ("default_width",
_("Default Width"),
_("The default width of the window, or 0 to use the size request."),
0,
G_MAXINT,
0,
G_PARAM_READWRITE));
g_object_class_install_property (gobject_class,
PROP_DEFAULT_HEIGHT,
g_param_spec_int ("default_height",
_("Default Height"),
_("The default height of the windo, or 0 to use the size request."),
0,
G_MAXINT,
0,
G_PARAM_READWRITE));
g_object_class_install_property (gobject_class,
PROP_DESTROY_WITH_PARENT,
g_param_spec_boolean ("destroy_with_parent",
_("Destroy with Parent"),
_("If this window should be destroyed when the parent is destroyed,"),
FALSE,
G_PARAM_READWRITE));
/* Style props are set or not */
window_signals[SET_FOCUS] =
gtk_signal_new ("set_focus",
GTK_RUN_LAST,
GTK_CLASS_TYPE (object_class),
GTK_SIGNAL_OFFSET (GtkWindowClass, set_focus),
gtk_marshal_VOID__OBJECT,
GTK_TYPE_NONE, 1,
GTK_TYPE_WIDGET);
window_signals[FRAME_EVENT] =
gtk_signal_new ("frame_event",
GTK_RUN_LAST,
GTK_CLASS_TYPE (object_class),
GTK_SIGNAL_OFFSET (GtkWindowClass, frame_event),
gtk_marshal_BOOLEAN__BOXED,
GTK_TYPE_BOOL, 1,
GTK_TYPE_GDK_EVENT);
if (!mnemonic_hash_table)
mnemonic_hash_table = g_hash_table_new (mnemonic_hash,
mnemonic_equal);
}
static void
gtk_window_init (GtkWindow *window)
{
GTK_WIDGET_UNSET_FLAGS (window, GTK_NO_WINDOW);
GTK_WIDGET_SET_FLAGS (window, GTK_TOPLEVEL);
GTK_PRIVATE_SET_FLAG (window, GTK_ANCHORED);
gtk_container_set_resize_mode (GTK_CONTAINER (window), GTK_RESIZE_QUEUE);
window->title = NULL;
window->wmclass_name = g_strdup (g_get_prgname ());
window->wmclass_class = g_strdup (gdk_progclass);
window->type = GTK_WINDOW_TOPLEVEL;
window->focus_widget = NULL;
window->default_widget = NULL;
window->resize_count = 0;
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;
window->modal = FALSE;
window->frame = NULL;
window->has_frame = FALSE;
window->frame_left = 0;
window->frame_right = 0;
window->frame_top = 0;
window->frame_bottom = 0;
window->type_hint = GDK_WINDOW_TYPE_HINT_NORMAL;
window->decorated = TRUE;
window->mnemonic_modifier = GDK_MOD1_MASK;
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);
gtk_decorated_window_init (window);
gtk_signal_connect (GTK_OBJECT (window),
"event",
GTK_SIGNAL_FUNC (gtk_window_event),
NULL);
}
static void
gtk_window_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
GtkWindow *window;
window = GTK_WINDOW (object);
switch (prop_id)
{
case PROP_TYPE:
window->type = g_value_get_enum (value);
break;
case PROP_TITLE:
gtk_window_set_title (window, g_value_get_string (value));
break;
case PROP_AUTO_SHRINK:
window->auto_shrink = g_value_get_boolean (value);
gtk_widget_queue_resize (GTK_WIDGET (window));
break;
case PROP_ALLOW_SHRINK:
window->allow_shrink = g_value_get_boolean (value);
gtk_widget_queue_resize (GTK_WIDGET (window));
break;
case PROP_ALLOW_GROW:
window->allow_grow = g_value_get_boolean (value);
gtk_widget_queue_resize (GTK_WIDGET (window));
break;
case PROP_MODAL:
gtk_window_set_modal (window, g_value_get_boolean (value));
break;
case PROP_WIN_POS:
gtk_window_set_position (window, g_value_get_enum (value));
break;
case PROP_DEFAULT_WIDTH:
gtk_window_set_default_size (window, g_value_get_int (value), -1);
break;
case PROP_DEFAULT_HEIGHT:
gtk_window_set_default_size (window, -1, g_value_get_int (value));
break;
case PROP_DESTROY_WITH_PARENT:
gtk_window_set_destroy_with_parent (window, g_value_get_boolean (value));
break;
default:
break;
}
}
static void
gtk_window_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
GtkWindow *window;
window = GTK_WINDOW (object);
switch (prop_id)
{
GtkWindowGeometryInfo *info;
case PROP_TYPE:
g_value_set_enum (value, window->type);
break;
case PROP_TITLE:
g_value_set_string (value, window->title);
break;
case PROP_AUTO_SHRINK:
g_value_set_boolean (value, window->auto_shrink);
break;
case PROP_ALLOW_SHRINK:
g_value_set_boolean (value, window->allow_shrink);
break;
case PROP_ALLOW_GROW:
g_value_set_boolean (value, window->allow_grow);
break;
case PROP_MODAL:
g_value_set_boolean (value, window->modal);
break;
case PROP_WIN_POS:
g_value_set_enum (value, window->position);
break;
case PROP_DEFAULT_WIDTH:
info = gtk_window_get_geometry_info (window, FALSE);
if (!info)
g_value_set_int (value, 0);
else
g_value_set_int (value, info->width);
break;
case PROP_DEFAULT_HEIGHT:
info = gtk_window_get_geometry_info (window, FALSE);
if (!info)
g_value_set_int (value, 0);
else
g_value_set_int (value, info->height);
break;
case PROP_DESTROY_WITH_PARENT:
g_value_set_boolean (value, window->destroy_with_parent);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
/**
* gtk_window_new:
* @type: type of window
*
* Creates a new #GtkWindow, which is a toplevel window that can
* contain other widgets. Nearly always, the type of the window should
* be #GTK_WINDOW_TOPLEVEL. If you're implementing something like a
* popup menu from scratch (which is a bad idea, just use #GtkMenu),
* you might use #GTK_WINDOW_POPUP. #GTK_WINDOW_POPUP is not for
* dialogs, though in some other toolkits dialogs are called "popups."
* In GTK+, #GTK_WINDOW_POPUP means a pop-up menu or pop-up tooltip.
* Popup windows are not controlled by the window manager.
*
* If you simply want an undecorated window (no window borders), use
* gtk_window_set_decorated(), don't use #GTK_WINDOW_POPUP.
*
* Return value: a new #GtkWindow.
**/
GtkWidget*
gtk_window_new (GtkWindowType type)
{
GtkWindow *window;
g_return_val_if_fail (type >= GTK_WINDOW_TOPLEVEL && type <= GTK_WINDOW_POPUP, NULL);
window = gtk_type_new (GTK_TYPE_WINDOW);
window->type = type;
return GTK_WIDGET (window);
}
/**
* gtk_window_set_title:
* @window: a #GtkWindow
* @title: title of the window
*
* Sets the title of the #GtkWindow. The title of a window will be displayed in
* its title bar; on the X Window System, the title bar is rendered by the
* window manager, so exactly how the title appears to users may vary according
* to a user's exact configuration. The title should help a user distinguish
* this window from other windows they may have open. A good title might
* include the application name and current document filename, for example.
*
**/
void
gtk_window_set_title (GtkWindow *window,
const gchar *title)
{
g_return_if_fail (window != NULL);
g_return_if_fail (GTK_IS_WINDOW (window));
if (window->title)
g_free (window->title);
window->title = g_strdup (title);
if (GTK_WIDGET_REALIZED (window))
{
gdk_window_set_title (GTK_WIDGET (window)->window, window->title);
gtk_decorated_window_set_title (window, title);
}
g_object_notify (G_OBJECT (window), "title");
}
/**
* gtk_window_set_wmclass:
* @window: a #GtkWindow
* @wmclass_name: window name hint
* @wmclass_class: window class hint
*
* Don't use this function. It sets the X Window System "class" and
* "name" hints for a window. According to the ICCCM, you should
* always set these to the same value for all windows in an
* application, and GTK sets them to that value by default, so calling
* this function is sort of pointless. However, you may want to call
* gtk_window_set_role() on each window in your application, for the
* benefit of the session manager. Setting the role allows the window
* manager to restore window positions when loading a saved session.
*
**/
void
gtk_window_set_wmclass (GtkWindow *window,
const gchar *wmclass_name,
const gchar *wmclass_class)
{
g_return_if_fail (window != NULL);
g_return_if_fail (GTK_IS_WINDOW (window));
g_free (window->wmclass_name);
window->wmclass_name = g_strdup (wmclass_name);
g_free (window->wmclass_class);
window->wmclass_class = g_strdup (wmclass_class);
if (GTK_WIDGET_REALIZED (window))
g_warning ("gtk_window_set_wmclass: shouldn't set wmclass after window is realized!\n");
}
/**
* gtk_window_set_role:
* @window: a #GtkWindow
* @role: unique identifier for the window to be used when restoring a session
*
* In combination with the window title, the window role allows a
* window manager to identify "the same" window when an application is
* restarted. So for example you might set the "toolbox" role on your
* app's toolbox window, so that when the user restarts their session,
* the window manager can put the toolbox back in the same place.
*
* If a window already has a unique title, you don't need to set the
* role, since the WM can use the title to identify the window when
* restoring the session.
*
**/
void
gtk_window_set_role (GtkWindow *window,
const gchar *role)
{
g_return_if_fail (GTK_IS_WINDOW (window));
if (role == window->wm_role)
return;
g_free (window->wm_role);
window->wm_role = g_strdup (role);
if (GTK_WIDGET_REALIZED (window))
g_warning ("gtk_window_set_role(): shouldn't set role after window is realized!\n");
}
/**
* gtk_window_set_focus:
* @window: a #GtkWindow
* @focus: widget to be the new focus widget
*
* If @focus is not the current focus widget, and is focusable, emits
* the "set_focus" signal to set @focus as the focus widget for the
* window. This function is more or less GTK-internal; to focus an
* entry widget or the like, you should use gtk_widget_grab_focus()
* instead of this function.
*
**/
void
gtk_window_set_focus (GtkWindow *window,
GtkWidget *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);
}
/**
* gtk_window_set_default:
* @window: a #GtkWindow
* @default_widget: widget to be the default
*
* The default widget is the widget that's activated when the user
* presses Enter in a dialog (for example). This function tells a
* #GtkWindow about the current default widget; it's really a GTK
* internal function and you shouldn't need it. Instead, to change the
* default widget, first set the #GTK_CAN_DEFAULT flag on the widget
* you'd like to make the default using GTK_WIDGET_SET_FLAGS(), then
* call gtk_widget_grab_default() to move the default.
*
**/
void
gtk_window_set_default (GtkWindow *window,
GtkWidget *default_widget)
{
g_return_if_fail (window != NULL);
g_return_if_fail (GTK_IS_WINDOW (window));
if (default_widget)
g_return_if_fail (GTK_WIDGET_CAN_DEFAULT (default_widget));
if (window->default_widget != default_widget)
{
if (window->default_widget)
{
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_queue_draw (window->default_widget);
}
window->default_widget = default_widget;
if (window->default_widget)
{
if (window->focus_widget == NULL ||
!GTK_WIDGET_RECEIVES_DEFAULT (window->focus_widget))
GTK_WIDGET_SET_FLAGS (window->default_widget, GTK_HAS_DEFAULT);
gtk_widget_queue_draw (window->default_widget);
}
}
}
void
gtk_window_set_policy (GtkWindow *window,
gboolean allow_shrink,
gboolean allow_grow,
gboolean auto_shrink)
{
g_return_if_fail (window != NULL);
g_return_if_fail (GTK_IS_WINDOW (window));
window->allow_shrink = (allow_shrink != FALSE);
window->allow_grow = (allow_grow != FALSE);
window->auto_shrink = (auto_shrink != FALSE);
g_object_notify (G_OBJECT (window), "allow_shrink");
g_object_notify (G_OBJECT (window), "allow_grow");
g_object_notify (G_OBJECT (window), "auto_shrink");
gtk_widget_queue_resize (GTK_WIDGET (window));
}
void
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_accel_group_attach (accel_group, GTK_OBJECT (window));
}
void
gtk_window_remove_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_accel_group_detach (accel_group, GTK_OBJECT (window));
}
void
gtk_window_add_mnemonic (GtkWindow *window,
guint keyval,
GtkWidget *target)
{
GtkWindowMnemonic key;
GtkWindowMnemonic *mnemonic;
g_return_if_fail (GTK_IS_WINDOW (window));
g_return_if_fail (GTK_IS_WIDGET (target));
key.window = window;
key.keyval = keyval;
mnemonic = g_hash_table_lookup (mnemonic_hash_table, &key);
if (mnemonic)
{
g_return_if_fail (g_slist_find (mnemonic->targets, target) == NULL);
mnemonic->targets = g_slist_prepend (mnemonic->targets, target);
}
else
{
mnemonic = g_new (GtkWindowMnemonic, 1);
*mnemonic = key;
mnemonic->targets = g_slist_prepend (NULL, target);
g_hash_table_insert (mnemonic_hash_table, mnemonic, mnemonic);
}
}
void
gtk_window_remove_mnemonic (GtkWindow *window,
guint keyval,
GtkWidget *target)
{
GtkWindowMnemonic key;
GtkWindowMnemonic *mnemonic;
g_return_if_fail (GTK_IS_WINDOW (window));
g_return_if_fail (GTK_IS_WIDGET (target));
key.window = window;
key.keyval = keyval;
mnemonic = g_hash_table_lookup (mnemonic_hash_table, &key);
g_return_if_fail (mnemonic && g_slist_find (mnemonic->targets, target) != NULL);
mnemonic->targets = g_slist_remove (mnemonic->targets, target);
if (mnemonic->targets == NULL)
{
g_hash_table_remove (mnemonic_hash_table, mnemonic);
g_free (mnemonic);
}
}
gboolean
gtk_window_activate_mnemonic (GtkWindow *window,
guint keyval,
GdkModifierType modifier)
{
GtkWindowMnemonic key;
GtkWindowMnemonic *mnemonic;
GSList *list;
GtkWidget *widget, *chosen_widget;
gboolean overloaded;
g_return_val_if_fail (GTK_IS_WINDOW (window), FALSE);
if (window->mnemonic_modifier != (modifier & gtk_accelerator_get_default_mod_mask ()))
return FALSE;
key.window = window;
key.keyval = keyval;
mnemonic = g_hash_table_lookup (mnemonic_hash_table, &key);
if (!mnemonic)
return FALSE;
overloaded = FALSE;
chosen_widget = NULL;
list = mnemonic->targets;
while (list)
{
widget = GTK_WIDGET (list->data);
if (GTK_WIDGET_IS_SENSITIVE (widget) &&
GTK_WIDGET_MAPPED (widget))
{
if (chosen_widget)
{
overloaded = TRUE;
break;
}
else
chosen_widget = widget;
}
list = g_slist_next (list);
}
if (chosen_widget)
{
/* For round robin we put the activated entry on
* the end of the list after activation
*/
mnemonic->targets = g_slist_remove (mnemonic->targets, chosen_widget);
mnemonic->targets = g_slist_append (mnemonic->targets, chosen_widget);
return gtk_widget_activate_mnemonic (chosen_widget, overloaded);
}
return FALSE;
}
void
gtk_window_set_mnemonic_modifier (GtkWindow *window,
GdkModifierType modifier)
{
g_return_if_fail (GTK_IS_WINDOW (window));
g_return_if_fail ((modifier & ~GDK_MODIFIER_MASK) == 0);
window->mnemonic_modifier = modifier;
}
void
gtk_window_set_position (GtkWindow *window,
GtkWindowPosition position)
{
g_return_if_fail (GTK_IS_WINDOW (window));
window->position = position;
g_object_notify (G_OBJECT (window), "window_position");
}
gboolean
gtk_window_activate_focus (GtkWindow *window)
{
g_return_val_if_fail (window != NULL, FALSE);
g_return_val_if_fail (GTK_IS_WINDOW (window), FALSE);
if (window->focus_widget)
{
if (GTK_WIDGET_IS_SENSITIVE (window->focus_widget))
gtk_widget_activate (window->focus_widget);
return TRUE;
}
return FALSE;
}
gboolean
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 && GTK_WIDGET_IS_SENSITIVE (window->default_widget))
{
gtk_widget_activate (window->default_widget);
return TRUE;
}
return FALSE;
}
/**
* gtk_window_set_modal:
* @window: a #GtkWindow
* @modal: whether the window is modal
*
* Sets a window modal or non-modal. Modal windows prevent interaction
* with other windows in the same application. To keep modal dialogs
* on top of main application windows, use
* gtk_window_set_transient_for() to make the dialog transient for the
* parent; most window managers will then disallow lowering the dialog
* below the parent.
*
*
**/
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));
g_object_notify (G_OBJECT (window), "modal");
}
/**
* gtk_window_list_toplevels:
*
* Returns a list of all existing toplevel windows. The widgets
* in the list are not individually referenced. If you want
* to iterate through the list and perform actions involving
* callbacks that might destroy the widgets, you MUST call
* g_list_foreach (result, (GFunc)g_object_ref, NULL) first, and
* then unref all the widgets afterwards.
*
* Return value: list of toplevel widgets
**/
GList*
gtk_window_list_toplevels (void)
{
GList *list = NULL;
GSList *slist;
for (slist = toplevel_list; slist; slist = slist->next)
list = g_list_prepend (list, 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);
}
if (window->frame)
gdk_window_move (window->frame, x - window->frame_left, y - window->frame_top);
else
gdk_window_move (GTK_WIDGET (window)->window, x, y);
}
}
static void
gtk_window_shutdown (GObject *object)
{
GtkWindow *window;
g_return_if_fail (GTK_IS_WINDOW (object));
window = GTK_WINDOW (object);
gtk_window_set_focus (window, NULL);
gtk_window_set_default (window, NULL);
G_OBJECT_CLASS (parent_class)->shutdown (object);
}
static void
parent_destroyed_callback (GtkWindow *parent, GtkWindow *child)
{
gtk_widget_destroy (GTK_WIDGET (child));
}
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);
}
}
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_transient_parent_realized (GtkWidget *parent,
GtkWidget *window)
{
if (GTK_WIDGET_REALIZED (window))
gdk_window_set_transient_for (window->window, parent->window);
}
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));
}
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;
}
}
/**
* gtk_window_set_transient_for:
* @window: a #GtkWindow
* @parent: parent window
*
* Dialog windows should be set transient for the main application
* window they were spawned from. This allows window managers to
* e.g. keep the dialog on top of the main window, or center the
* dialog over the main window. gtk_dialog_new_with_buttons() and
* other convenience functions in GTK+ will sometimes call
* gtk_window_set_transient_for() on your behalf.
*
**/
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_type_hint:
* @window: a #GtkWindow
* @hint: the window type
*
* By setting the type hint for the window, you allow the window
* manager to decorate and handle the window in a way which is
* suitable to the function of the window in your application.
*
* This function should be called before the window becomes visible.
*
* gtk_dialog_new_with_buttons() and other convenience functions in GTK+
* will sometimes call gtk_window_set_type_hint() on your behalf.
*
**/
void
gtk_window_set_type_hint (GtkWindow *window,
GdkWindowTypeHint hint)
{
g_return_if_fail (GTK_IS_WINDOW (window));
g_return_if_fail (!GTK_WIDGET_VISIBLE (window));
window->type_hint = hint;
}
/**
* 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;
g_object_notify (G_OBJECT (window), "destroy_with_parent");
}
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;
}
/**
* gtk_window_set_geometry_hints:
* @window: a #GdkWindow
* @geometry_widget: widget the geometry hints will be applied to
* @geometry: struct containing geometry information
* @geom_mask: mask indicating which struct fields should be paid attention to
*
* This function sets up hints about how a window can be resized by
* the user. You can set a minimum and maximum size; allowed resize
* increments (e.g. for xterm, you can only resize by the size of a
* character); aspect ratios; and more. See the #GdkGeometry struct.
*
**/
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));
}
/**
* gtk_window_set_decorated:
* @window: a #GtkWindow
* @setting: %TRUE to decorate the window
*
* By default, windows are decorated with a title bar, resize
* controls, etc. Some window managers allow GTK+ to disable these
* decorations, creating a borderless window. If you set the decorated
* property to %FALSE using this function, GTK+ will do its best to
* convince the window manager not to decorate the window.
*
**/
void
gtk_window_set_decorated (GtkWindow *window,
gboolean setting)
{
g_return_if_fail (GTK_IS_WINDOW (window));
setting = setting != FALSE;
if (setting == window->decorated)
return;
if (GTK_WIDGET (window)->window)
{
if (window->decorated)
gdk_window_set_decorations (GTK_WIDGET (window)->window,
GDK_DECOR_ALL);
else
gdk_window_set_decorations (GTK_WIDGET (window)->window,
0);
}
}
/**
* gtk_window_set_default_size:
* @window: a #GtkWindow
* @width: width in pixels, 0 to unset, or -1 to leave the width unchanged
* @height: height in pixels, 0 to unset, or -1 to leave the height unchanged
*
* Sets the default size of a window. If the window's "natural" size
* (its size request) is larger than the default, the default will be
* ignored. So the default size is a minimum initial size. Unlike
* gtk_widget_set_usize(), which sets a size request for a widget and
* thus would keep users from shrinking the window, this function only
* sets the initial size, just as if the user had resized the window
* themselves. Users can still shrink the window again as they
* normally would. Setting a default size of 0 means to use the
* "natural" default size (the size request of the window).
*
* For more control over a window's initial size and how resizing works,
* investigate gtk_window_set_geometry_hints().
*
* A useful feature: if you set the "geometry widget" via
* gtk_window_set_geometry_hints(), the default size specified by
* gtk_window_set_default_size() will be the default size of that
* widget, not of the entire 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;
if (width >= 0)
g_object_notify (G_OBJECT (window), "width");
if (height >= 0)
g_object_notify (G_OBJECT (window), "height");
gtk_widget_queue_resize (GTK_WIDGET (window));
}
static void
gtk_window_destroy (GtkObject *object)
{
GtkWindow *window;
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 gboolean
gtk_window_mnemonic_hash_remove (gpointer key,
gpointer value,
gpointer user)
{
GtkWindowMnemonic *mnemonic = key;
GtkWindow *window = user;
if (mnemonic->window == window)
{
if (mnemonic->targets)
{
gchar *name = gtk_accelerator_name (mnemonic->keyval, 0);
g_warning ("mnemonic \"%s\" wasn't removed for widget (%p)",
name, mnemonic->targets->data);
g_free (name);
}
g_slist_free (mnemonic->targets);
g_free (mnemonic);
return TRUE;
}
return FALSE;
}
static void
gtk_window_finalize (GObject *object)
{
GtkWindow *window;
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);
g_hash_table_foreach_remove (mnemonic_hash_table,
gtk_window_mnemonic_hash_remove,
window);
G_OBJECT_CLASS (parent_class)->finalize (object);
}
static void
gtk_window_show (GtkWidget *widget)
{
GtkWindow *window = GTK_WINDOW (widget);
GtkContainer *container = GTK_CONTAINER (window);
gboolean need_resize;
gboolean was_realized;
GTK_WIDGET_SET_FLAGS (widget, GTK_VISIBLE);
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);
was_realized = FALSE;
if (!GTK_WIDGET_REALIZED (widget))
{
gtk_widget_realize (widget);
was_realized = TRUE;;
}
/* Must be done after the windows are realized,
so that the decorations can be read */
gtk_decorated_window_calculate_frame_size (window);
if (!was_realized)
gdk_window_resize (widget->window, width, height);
}
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
gtk_window_map (GtkWidget *widget)
{
GtkWindow *window;
GdkWindow *toplevel;
g_return_if_fail (widget != NULL);
g_return_if_fail (GTK_IS_WINDOW (widget));
GTK_WIDGET_SET_FLAGS (widget, GTK_MAPPED);
window = GTK_WINDOW (widget);
if (window->bin.child &&
GTK_WIDGET_VISIBLE (window->bin.child) &&
!GTK_WIDGET_MAPPED (window->bin.child))
gtk_widget_map (window->bin.child);
if (window->frame)
toplevel = window->frame;
else
toplevel = widget->window;
if (window->maximize_initially)
gdk_window_maximize (toplevel);
else
gdk_window_unmaximize (toplevel);
if (window->stick_initially)
gdk_window_stick (toplevel);
else
gdk_window_unstick (toplevel);
if (window->iconify_initially)
gdk_window_iconify (toplevel);
else
gdk_window_deiconify (toplevel);
gdk_window_show (widget->window);
if (window->frame)
gdk_window_show (window->frame);
}
static void
gtk_window_unmap (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_MAPPED);
if (window->frame)
gdk_window_withdraw (window->frame);
else
gdk_window_withdraw (widget->window);
window->use_uposition = TRUE;
window->resize_count = 0;
window->handling_resize = FALSE;
}
static void
gtk_window_realize (GtkWidget *widget)
{
GtkWindow *window;
GdkWindow *parent_window;
GdkWindowAttr attributes;
gint attributes_mask;
g_return_if_fail (GTK_IS_WINDOW (widget));
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:
attributes.window_type = GDK_WINDOW_TOPLEVEL;
break;
case GTK_WINDOW_POPUP:
attributes.window_type = GDK_WINDOW_TEMP;
break;
default:
g_warning (G_STRLOC": Unknown window type %d!", window->type);
break;
}
attributes.title = window->title;
attributes.wmclass_name = window->wmclass_name;
attributes.wmclass_class = window->wmclass_class;
attributes.wclass = GDK_INPUT_OUTPUT;
attributes.visual = gtk_widget_get_visual (widget);
attributes.colormap = gtk_widget_get_colormap (widget);
if (window->has_frame)
{
attributes.width = widget->allocation.width + window->frame_left + window->frame_right;
attributes.height = widget->allocation.height + window->frame_top + window->frame_bottom;
attributes.event_mask = (GDK_EXPOSURE_MASK |
GDK_KEY_PRESS_MASK |
GDK_ENTER_NOTIFY_MASK |
GDK_LEAVE_NOTIFY_MASK |
GDK_FOCUS_CHANGE_MASK |
GDK_STRUCTURE_MASK |
GDK_BUTTON_MOTION_MASK |
GDK_POINTER_MOTION_HINT_MASK |
GDK_BUTTON_PRESS_MASK |
GDK_BUTTON_RELEASE_MASK);
attributes_mask = GDK_WA_VISUAL | GDK_WA_COLORMAP;
window->frame = gdk_window_new (NULL, &attributes, attributes_mask);
gdk_window_set_user_data (window->frame, widget);
attributes.window_type = GDK_WINDOW_CHILD;
attributes.x = window->frame_left;
attributes.y = window->frame_right;
attributes_mask = GDK_WA_X | GDK_WA_Y;
parent_window = window->frame;
}
else
{
attributes_mask = 0;
parent_window = NULL;
}
attributes.width = widget->allocation.width;
attributes.height = widget->allocation.height;
attributes.event_mask = gtk_widget_get_events (widget);
attributes.event_mask |= (GDK_EXPOSURE_MASK |
GDK_KEY_PRESS_MASK |
GDK_ENTER_NOTIFY_MASK |
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 (parent_window, &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);
if (window->frame)
gtk_style_set_background (widget->style, window->frame, GTK_STATE_NORMAL);
/* This is a bad hack to set the window background. */
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);
if (window->wm_role)
gdk_window_set_role (widget->window, window->wm_role);
if (!window->decorated)
gdk_window_set_decorations (widget->window, 0);
gdk_window_set_type_hint (widget->window, window->type_hint);
/* transient_for must be set to allow the modal hint */
if (window->transient_parent && window->modal)
gdk_window_set_modal_hint (widget->window, TRUE);
else
gdk_window_set_modal_hint (widget->window, FALSE);
}
static void
gtk_window_unrealize (GtkWidget *widget)
{
GtkWindow *window;
g_return_if_fail (widget != NULL);
g_return_if_fail (GTK_IS_WINDOW (widget));
window = GTK_WINDOW (widget);
if (window->frame)
{
gdk_window_set_user_data (window->frame, NULL);
gdk_window_destroy (window->frame);
window->frame = NULL;
}
(* GTK_WIDGET_CLASS (parent_class)->unrealize) (widget);
}
static void
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 (bin->child && GTK_WIDGET_VISIBLE (bin->child))
{
GtkRequisition child_requisition;
gtk_widget_size_request (bin->child, &child_requisition);
requisition->width += child_requisition.width;
requisition->height += child_requisition.height;
}
}
static void
gtk_window_size_allocate (GtkWidget *widget,
GtkAllocation *allocation)
{
GtkWindow *window;
GtkAllocation child_allocation;
g_return_if_fail (widget != NULL);
g_return_if_fail (GTK_IS_WINDOW (widget));
g_return_if_fail (allocation != NULL);
window = GTK_WINDOW (widget);
widget->allocation = *allocation;
if (window->bin.child && GTK_WIDGET_VISIBLE (window->bin.child))
{
child_allocation.x = GTK_CONTAINER (window)->border_width;
child_allocation.y = GTK_CONTAINER (window)->border_width;
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);
}
if (GTK_WIDGET_REALIZED (widget) && window->frame)
{
gdk_window_resize (window->frame,
allocation->width + window->frame_left + window->frame_right,
allocation->height + window->frame_top + window->frame_bottom);
}
}
static gint
gtk_window_event (GtkWidget *widget, GdkEvent *event)
{
GtkWindow *window;
gboolean return_val;
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);
if (window->frame && (event->any.window == window->frame))
{
if ((event->type != GDK_KEY_PRESS) &&
(event->type != GDK_KEY_RELEASE) &&
(event->type != GDK_FOCUS_CHANGE))
{
gtk_signal_emit_stop_by_name (GTK_OBJECT (widget), "event");
return_val = FALSE;
gtk_signal_emit (GTK_OBJECT (widget), window_signals[FRAME_EVENT], event, &return_val);
return TRUE;
}
else
{
g_object_unref (event->any.window);
event->any.window = g_object_ref (widget->window);
}
}
return FALSE;
}
static gboolean
gtk_window_frame_event (GtkWidget *widget, GdkEvent *event)
{
GdkEventConfigure *configure_event;
GtkWindow *window = GTK_WINDOW (widget);
GdkRectangle rect;
switch (event->type)
{
case GDK_CONFIGURE:
configure_event = (GdkEventConfigure *)event;
/* Invalidate the decorations */
rect.x = 0;
rect.y = 0;
rect.width = configure_event->width;
rect.height = configure_event->height;
gdk_window_invalidate_rect (window->frame, &rect, FALSE);
/* Pass on the (modified) configure event */
configure_event->width -= window->frame_left + window->frame_right;
configure_event->height -= window->frame_top + window->frame_bottom;
return gtk_window_configure_event (widget, configure_event);
break;
default:
break;
}
return FALSE;
}
static gint
gtk_window_configure_event (GtkWidget *widget,
GdkEventConfigure *event)
{
GtkWindow *window;
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);
/* 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
gtk_window_key_press_event (GtkWidget *widget,
GdkEventKey *event)
{
GtkWindow *window;
GtkDirectionType direction = 0;
gboolean 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);
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_window_activate_mnemonic (window,
event->keyval,
event->state);
if (!handled)
handled = gtk_accel_groups_activate (GTK_OBJECT (window), event->keyval, event->state);
if (!handled)
{
switch (event->keyval)
{
case GDK_space:
if (window->focus_widget)
{
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 && GTK_WIDGET_IS_SENSITIVE (window->default_widget) &&
(!window->focus_widget || !GTK_WIDGET_RECEIVES_DEFAULT (window->focus_widget)))
{
gtk_widget_activate (window->default_widget);
handled = TRUE;
}
else if (window->focus_widget)
{
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:
case GDK_ISO_Left_Tab:
if (event->state & GDK_SHIFT_MASK)
direction = GTK_DIR_TAB_BACKWARD;
else
direction = GTK_DIR_TAB_FORWARD;
break;
default :
direction = GTK_DIR_UP; /* never reached, but makes compiler happy */
}
gtk_container_focus (GTK_CONTAINER (widget), direction);
if (!GTK_CONTAINER (window)->focus_child)
gtk_window_set_focus (GTK_WINDOW (widget), NULL);
else
handled = TRUE;
break;
}
}
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
gtk_window_key_release_event (GtkWidget *widget,
GdkEventKey *event)
{
GtkWindow *window;
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);
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);
}
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
gtk_window_enter_notify_event (GtkWidget *widget,
GdkEventCrossing *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);
return FALSE;
}
static gint
gtk_window_leave_notify_event (GtkWidget *widget,
GdkEventCrossing *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);
return FALSE;
}
static gint
gtk_window_focus_in_event (GtkWidget *widget,
GdkEventFocus *event)
{
GtkWindow *window;
GdkEventFocus fevent;
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);
/* It appears spurious focus in events can occur when
* the window is hidden. So we'll just check to see if
* the window is visible before actually handling the
* event
*/
if (GTK_WIDGET_VISIBLE (widget))
{
window = GTK_WINDOW (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;
fevent.in = TRUE;
gtk_widget_event (window->focus_widget, (GdkEvent*) &fevent);
}
}
return FALSE;
}
static gint
gtk_window_focus_out_event (GtkWidget *widget,
GdkEventFocus *event)
{
GtkWindow *window;
GdkEventFocus fevent;
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);
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;
fevent.in = FALSE;
gtk_widget_event (window->focus_widget, (GdkEvent*) &fevent);
}
return FALSE;
}
static GdkAtom atom_rcfiles = GDK_NONE;
static void
gtk_window_read_rcfiles (GtkWidget *widget,
GdkEventClient *event)
{
GList *embedded_windows;
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;
}
}
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;
_gtk_icon_set_invalidate_caches ();
toplevels = gtk_window_list_toplevels ();
g_list_foreach (toplevels, (GFunc)g_object_ref, NULL);
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)
{
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 (!atom_rcfiles)
atom_rcfiles = gdk_atom_intern("_GTK_READ_RCFILES", FALSE);
if(event->message_type == atom_rcfiles)
gtk_window_read_rcfiles (widget, event);
return FALSE;
}
static void
gtk_window_check_resize (GtkContainer *container)
{
GtkWindow *window;
g_return_if_fail (container != NULL);
g_return_if_fail (GTK_IS_WINDOW (container));
window = GTK_WINDOW (container);
if (GTK_WIDGET_VISIBLE (container))
gtk_window_move_resize (window);
}
static gboolean
gtk_window_focus (GtkContainer *container,
GtkDirectionType direction)
{
GtkBin *bin = GTK_BIN (container);
GtkWindow *window = GTK_WINDOW (container);
GtkWidget *old_focus_child = container->focus_child;
GtkWidget *parent;
/* We need a special implementation here to deal properly with wrapping
* around in the tab chain without the danger of going into an
* infinite loop.
*/
if (old_focus_child)
{
if (GTK_IS_CONTAINER (old_focus_child) &&
GTK_WIDGET_DRAWABLE (old_focus_child) &&
GTK_WIDGET_IS_SENSITIVE (old_focus_child) &&
gtk_container_focus (GTK_CONTAINER (old_focus_child), direction))
return TRUE;
}
if (window->focus_widget)
{
/* Wrapped off the end, clear the focus setting for the toplpevel */
parent = window->focus_widget->parent;
while (parent)
{
gtk_container_set_focus_child (GTK_CONTAINER (parent), NULL);
parent = GTK_WIDGET (parent)->parent;
}
gtk_window_set_focus (GTK_WINDOW (container), NULL);
}
/* Now try to focus the first widget in the window */
if (GTK_WIDGET_DRAWABLE (bin->child) &&
GTK_WIDGET_IS_SENSITIVE (bin->child))
{
if (GTK_IS_CONTAINER (bin->child))
{
if (gtk_container_focus (GTK_CONTAINER (bin->child), direction))
return TRUE;
}
else if (GTK_WIDGET_CAN_FOCUS (bin->child))
{
gtk_widget_grab_focus (bin->child);
return TRUE;
}
}
return FALSE;
}
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 (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);
if (window->default_widget)
GTK_WIDGET_UNSET_FLAGS (window->default_widget, GTK_HAS_DEFAULT);
}
gtk_widget_event (window->focus_widget, (GdkEvent*) &event);
}
if (window->default_widget &&
(def_flags != GTK_WIDGET_FLAGS (window->default_widget)))
gtk_widget_queue_draw (window->default_widget);
}
/*********************************
* Functions related to resizing *
*********************************/
static void
gtk_window_move_resize (GtkWindow *window)
{
GtkWidget *widget;
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 (info->last.width < 0 ||
info->last.width != new_width ||
info->last.height != new_height)
{
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;
}
/* 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))
{
hints_changed = TRUE;
info->last.geometry = new_geometry;
info->last.flags = new_flags;
}
/* 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))
{
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;
/* 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).
*/
window->handling_resize = FALSE;
allocation = widget->allocation;
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))
{
/* 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 (x != -1 && y != -1)
{
if (window->frame)
gdk_window_move (window->frame, x - window->frame_left, y - window->frame_top);
else
gdk_window_move (GTK_WIDGET (window)->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;
gtk_widget_queue_resize (widget);
return;
}
#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.
*/
/* request a new window size */
if (x != -1 && y != -1)
{
if (window->frame)
{
gdk_window_move_resize (window->frame,
x - window->frame_left, y - window->frame_top,
new_width + window->frame_left + window->frame_right,
new_height + window->frame_top + window->frame_bottom);
gdk_window_resize (GTK_WIDGET (window)->window, new_width, new_height);
}
else
gdk_window_move_resize (GTK_WIDGET (window)->window, x, y, new_width, new_height);
}
else
{
if (window->frame)
gdk_window_resize (window->frame,
new_width + window->frame_left + window->frame_right,
new_height + window->frame_top + window->frame_bottom);
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)
{
if (window->frame)
gdk_window_move (window->frame, x - window->frame_left, y - window->frame_top);
else
gdk_window_move (widget->window, x, y);
}
if (container->resize_widgets)
gtk_container_resize_children (GTK_CONTAINER (window));
}
}
/* 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)
{
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;
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;
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 ((flags_a & GDK_HINT_ASPECT) &&
(geometry_a->min_aspect != geometry_b->min_aspect ||
geometry_a->max_aspect != geometry_b->max_aspect))
return FALSE;
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;
return TRUE;
}
/* 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;
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;
}
}
void
_gtk_window_constrain_size (GtkWindow *window,
gint width,
gint height,
gint *new_width,
gint *new_height)
{
GtkWindowGeometryInfo *info = (GtkWindowGeometryInfo *)gtk_object_get_data (GTK_OBJECT (window), "gtk-window-geometry");
if (info)
{
GdkWindowHints flags = info->last.flags;
GdkGeometry *geometry = &info->last.geometry;
gtk_window_constrain_size (window,
geometry,
flags,
width,
height,
new_width,
new_height);
}
}
/* 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)
{
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) )
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 (flags & GDK_HINT_MAX_SIZE)
{
max_width = geometry->max_width ;
max_height = geometry->max_height;
}
if (flags & GDK_HINT_RESIZE_INC)
{
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;
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;
}
}
if (geometry->max_aspect * height < width)
{
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_compute_hints (GtkWindow *window,
GdkGeometry *new_geometry,
guint *new_flags)
{
GtkWidget *widget;
GtkWidgetAuxInfo *aux_info;
gint ux, uy;
gint extra_width = 0;
gint extra_height = 0;
GtkWindowGeometryInfo *geometry_info;
GtkRequisition requisition;
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);
g_return_if_fail (geometry_info != NULL);
*new_flags = geometry_info->mask;
*new_geometry = geometry_info->geometry;
if (geometry_info->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);
*x = -1;
*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)
{
gint screen_width = gdk_screen_width ();
gint screen_height = gdk_screen_height ();
*x = (screen_width - new_width) / 2;
*y = (screen_height - new_height) / 2;
}
break;
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;
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)
{
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;
}
}
/***********************
* Redrawing functions *
***********************/
static void
gtk_window_paint (GtkWidget *widget,
GdkRectangle *area)
{
gtk_paint_flat_box (widget->style, widget->window, GTK_STATE_NORMAL,
GTK_SHADOW_NONE, area, widget, "base", 0, 0, -1, -1);
}
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_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);
return TRUE;
}
/**
* gtk_window_set_has_frame:
* @window: a #GtkWindow
*
* If this function is called on a window before it is realized
* or showed it will have a "frame" window around widget-window,
* accessible in window->frame. Using the signal frame_event
* you can recieve all events targeted at the frame.
*
* This function is used by the linux-fb port to implement managed
* windows, but it could concievably be used by X-programs that
* want to do their own window decorations.
**/
void
gtk_window_set_has_frame (GtkWindow *window)
{
g_return_if_fail (window != NULL);
g_return_if_fail (GTK_IS_WINDOW (window));
g_return_if_fail (!GTK_WIDGET_REALIZED (window));
window->has_frame = TRUE;
}
/**
* gtk_window_set_frame_dimensions:
* @window: a #GtkWindow that has a frame
* @left: The width of the left border
* @top: The height of the top border
* @right: The width of the right border
* @bottom: The height of the bottom border
*
* For windows with frames (see #gtk_window_set_has_frame) this function
* can be used to change the size of the frame border.
**/
void
gtk_window_set_frame_dimensions (GtkWindow *window,
gint left,
gint top,
gint right,
gint bottom)
{
GtkWidget *widget = GTK_WIDGET (window);
g_return_if_fail (window != NULL);
g_return_if_fail (GTK_IS_WINDOW (window));
if (window->frame_left == left &&
window->frame_top == top &&
window->frame_right == right &&
window->frame_bottom == bottom)
return;
window->frame_left = left;
window->frame_top = top;
window->frame_right = right;
window->frame_bottom = bottom;
if (GTK_WIDGET_REALIZED (widget) && window->frame)
{
gint width = widget->allocation.width + left + right;
gint height = widget->allocation.height + top + bottom;
gdk_window_resize (window->frame, width, height);
gtk_decorated_window_move_resize_window (window,
left, top,
widget->allocation.width,
widget->allocation.height);
}
}
/**
* gtk_window_present:
* @window: a #GtkWindow
*
* Presents a window to the user. This may mean raising the window
* in the stacking order, deiconifying it, moving it to the current
* desktop, and/or giving it the keyboard focus, possibly dependent
* on the user's platform, window manager, and preferences.
*
* If @window is hidden, this function calls gtk_widget_show()
* as well.
*
* This function should be used when the user tries to open a window
* that's already open. Say for example the preferences dialog is
* currently open, and the user chooses Preferences from the menu
* a second time; use gtk_window_present() to move the already-open dialog
* where the user can see it.
*
**/
void
gtk_window_present (GtkWindow *window)
{
GtkWidget *widget;
g_return_if_fail (GTK_IS_WINDOW (window));
widget = GTK_WIDGET (window);
if (GTK_WIDGET_VISIBLE (window))
{
g_assert (widget->window != NULL);
gdk_window_show (widget->window);
/* note that gdk_window_focus() will also move the window to
* the current desktop, for WM spec compliant window managers.
*/
gdk_window_focus (widget->window,
gtk_get_current_event_time ());
}
else
{
gtk_widget_show (widget);
}
}
/**
* gtk_window_iconify:
* @window: a #GtkWindow
*
* Asks to iconify @window. Note that you shouldn't assume the window
* is definitely iconified afterward, because other entities (e.g. the
* user or window manager) could deiconify it again, or there may not
* be a window manager in which case iconification isn't possible,
* etc. But normally the window will end up iconified. Just don't write
* code that crashes if not.
*
* It's permitted to call this function before showing a window,
* in which case the window will be iconified before it ever appears
* onscreen.
*
* You can track iconification via the "window_state_event" signal
* on #GtkWidget.
*
**/
void
gtk_window_iconify (GtkWindow *window)
{
GtkWidget *widget;
GdkWindow *toplevel;
g_return_if_fail (GTK_IS_WINDOW (window));
widget = GTK_WIDGET (window);
window->iconify_initially = TRUE;
if (window->frame)
toplevel = window->frame;
else
toplevel = widget->window;
if (toplevel != NULL)
gdk_window_iconify (toplevel);
}
/**
* gtk_window_deiconify:
* @window: a #GtkWindow
*
* Asks to deiconify @window. Note that you shouldn't assume the
* window is definitely deiconified afterward, because other entities
* (e.g. the user or window manager) could iconify it again before
* your code which assumes deiconification gets to run.
*
* You can track iconification via the "window_state_event" signal
* on #GtkWidget.
**/
void
gtk_window_deiconify (GtkWindow *window)
{
GtkWidget *widget;
GdkWindow *toplevel;
g_return_if_fail (GTK_IS_WINDOW (window));
widget = GTK_WIDGET (window);
window->iconify_initially = FALSE;
if (window->frame)
toplevel = window->frame;
else
toplevel = widget->window;
if (toplevel != NULL)
gdk_window_deiconify (toplevel);
}
/**
* gtk_window_stick:
* @window: a #GtkWindow
*
* Asks to stick @window, which means that it will appear on all user
* desktops. Note that you shouldn't assume the window is definitely
* stuck afterward, because other entities (e.g. the user or window
* manager) could unstick it again, and some window managers do not
* support sticking windows. But normally the window will end up
* stuck. Just don't write code that crashes if not.
*
* It's permitted to call this function before showing a window.
*
* You can track stickiness via the "window_state_event" signal
* on #GtkWidget.
*
**/
void
gtk_window_stick (GtkWindow *window)
{
GtkWidget *widget;
GdkWindow *toplevel;
g_return_if_fail (GTK_IS_WINDOW (window));
widget = GTK_WIDGET (window);
window->stick_initially = TRUE;
if (window->frame)
toplevel = window->frame;
else
toplevel = widget->window;
if (toplevel != NULL)
gdk_window_stick (toplevel);
}
/**
* gtk_window_unstick:
* @window: a #GtkWindow
*
* Asks to unstick @window, which means that it will appear on only
* one of the user's desktops. Note that you shouldn't assume the
* window is definitely unstuck afterward, because other entities
* (e.g. the user or window manager) could stick it again. But
* normally the window will end up stuck. Just don't write code that
* crashes if not.
*
* You can track stickiness via the "window_state_event" signal
* on #GtkWidget.
*
**/
void
gtk_window_unstick (GtkWindow *window)
{
GtkWidget *widget;
GdkWindow *toplevel;
g_return_if_fail (GTK_IS_WINDOW (window));
widget = GTK_WIDGET (window);
window->stick_initially = FALSE;
if (window->frame)
toplevel = window->frame;
else
toplevel = widget->window;
if (toplevel != NULL)
gdk_window_unstick (toplevel);
}
/**
* gtk_window_maximize:
* @window: a #GtkWindow
*
* Asks to maximize @window, so that it becomes full-screen. Note that
* you shouldn't assume the window is definitely maximized afterward,
* because other entities (e.g. the user or window manager) could
* unmaximize it again, and not all window managers support
* maximization. But normally the window will end up maximized. Just
* don't write code that crashes if not.
*
* It's permitted to call this function before showing a window,
* in which case the window will be maximized when it appears onscreen
* initially.
*
* You can track maximization via the "window_state_event" signal
* on #GtkWidget.
*
**/
void
gtk_window_maximize (GtkWindow *window)
{
GtkWidget *widget;
GdkWindow *toplevel;
g_return_if_fail (GTK_IS_WINDOW (window));
widget = GTK_WIDGET (window);
window->maximize_initially = TRUE;
if (window->frame)
toplevel = window->frame;
else
toplevel = widget->window;
if (toplevel != NULL)
gdk_window_maximize (toplevel);
}
/**
* gtk_window_unmaximize:
* @window: a #GtkWindow
*
* Asks to unmaximize @window. Note that you shouldn't assume the
* window is definitely unmaximized afterward, because other entities
* (e.g. the user or window manager) could maximize it again, and not
* all window managers honor requests to unmaximize. But normally the
* window will end up unmaximized. Just don't write code that crashes
* if not.
*
* You can track maximization via the "window_state_event" signal
* on #GtkWidget.
*
**/
void
gtk_window_unmaximize (GtkWindow *window)
{
GtkWidget *widget;
GdkWindow *toplevel;
g_return_if_fail (GTK_IS_WINDOW (window));
widget = GTK_WIDGET (window);
window->maximize_initially = FALSE;
if (window->frame)
toplevel = window->frame;
else
toplevel = widget->window;
if (toplevel != NULL)
gdk_window_unmaximize (toplevel);
}