If you aren't familiar with the concept of realizing and
mapping a widget, go back and read the section called
Realizing, Mapping, and Showing in the chapter called
GTK+ Basics before reading this section.
GtkEv does not override the
map or unmap method; the default
GtkWidget methods suffice. The defaults set and
unset the
GTK_WIDGET_MAPPED flag, and show or hide widget->window.
Any widget with a
GdkWindow that has
GtkWidget as its immediate parent will need to
override the realize method; the default is only suitable
for windowless widgets. GtkEv
is no exception. GtkEv also
overrides the unrealize method, in order to destroy the
event window.
Here is GtkEv's realize
method:
static void
gtk_ev_realize (GtkWidget *widget)
{
GdkWindowAttr attributes;
gint attributes_mask;
GtkEv* ev;
GdkCursor* cursor;
g_return_if_fail(widget != NULL);
g_return_if_fail(GTK_IS_EV(widget));
ev = GTK_EV(widget);
/* Set realized flag */
GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
/* Main widget window */
attributes.window_type = GDK_WINDOW_CHILD;
attributes.x = widget->allocation.x;
attributes.y = widget->allocation.y;
attributes.width = widget->allocation.width;
attributes.height = widget->allocation.height;
attributes.wclass = GDK_INPUT_OUTPUT;
attributes.visual = gtk_widget_get_visual (widget);
attributes.colormap = gtk_widget_get_colormap (widget);
attributes.event_mask = gtk_widget_get_events (widget) | GDK_EXPOSURE_MASK;
attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
widget->window = gdk_window_new (gtk_widget_get_parent_window (widget),
&attributes, attributes_mask);
gdk_window_set_user_data (widget->window, widget);
/* Event window */
cursor = gdk_cursor_new(GDK_CROSSHAIR);
attributes.window_type = GDK_WINDOW_CHILD;
attributes.x = ev->event_window_rect.x;
attributes.y = ev->event_window_rect.y;
attributes.width = ev->event_window_rect.width;
attributes.height = ev->event_window_rect.height;
attributes.wclass = GDK_INPUT_OUTPUT;
attributes.visual = gtk_widget_get_visual (widget);
attributes.colormap = gtk_widget_get_colormap (widget);
attributes.event_mask = GDK_ALL_EVENTS_MASK;
attributes.cursor = cursor;
attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL |
GDK_WA_COLORMAP | GDK_WA_CURSOR;
ev->event_window = gdk_window_new (widget->window,
&attributes, attributes_mask);
gdk_window_set_user_data (ev->event_window, widget);
gdk_window_show(ev->event_window);
gdk_cursor_destroy(cursor);
/* Style */
widget->style = gtk_style_attach (widget->style, widget->window);
gtk_style_set_background (widget->style, widget->window, GTK_STATE_NORMAL);
gdk_window_set_background (ev->event_window,
&widget->style->base[GTK_STATE_NORMAL]);
}
|
The first step in any realize method is to set the GTK_REALIZED flag; this is a
small but important detail. After that, most of the
realize method is concerned with creating the two GdkWindows, as described in the section called GdkWindow in the chapter
called GDK Basics.
widget->window should be created as a subwindow
of the widget's parent's
GdkWindow; the parent window is obtained with gtk_widget_get_parent_window().
Notice that all events are
requested on the event window, for obvious reasons. Also,
the event window has a special cursor, to give the user
visual feedback when the pointer moves into it. The
client-side cursor handle is destroyed immediately after
attaching the cursor to the window; the X server will
keep it around as long as it's in use.
After creating each
GdkWindow, a pointer to the
GtkEv is stored in the
GdkWindow's "user data" field. GTK+ uses the
contents of this field to determine which widget should
receive events that occur on the window. Recall that GTK+
receives a stream of events from GDK, and that each GdkEvent has a window field indicating the GdkWindow that received it.
GTK+ forwards each event to the widget owning the event's
GdkWindow. (the section called Events in
the chapter called GDK Basics details this
process if you don't remember.)
The code calls
gdk_window_show() on the event window but not widget->window; widget->window should not be shown
until the widget is mapped. Because the event window is a
child of
widget->window, it will remain offscreen until
its parent is shown. Alternatively, GtkEv could implement a map method to
show the child, but this way seems simpler.
All widgets must take create their associated GtkStyle in their realize
method, because a style contains X resources. (See the section called GtkStyle and Themes in the
chapter called GDK Basics for more information
about GtkStyle.) Recall
from the section
called Realizing, Mapping, and Showing in the
chapter called GTK+ Basics that widgets
allocate all X resources in their realize method. GTK+
provides a simple function to create a widget's style:
widget->style = gtk_style_attach (widget->style, widget->window);
|
After filling in widget->style, GtkEv uses colors from the style to set
window backgrounds. It sets the main window's background
using
gtk_style_set_background(), which could do almost
anything (it might invoke a routine from a dynamically
loaded theme module). If the default theme is running, it
simply sets the window's background to an appropriate
color or pixmap tile. There is no special style function
to set the background of the event window, so we set it
to the "base" color (the base color is white by default;
it's the background color for lists and text entries).
Selecting a color from the style means that users will be
able to customize the widget's color. It's also
convenient to avoid allocating and deallocating a custom
color.
Notice that the realize method does not chain up to the
default realize method, because the default isn't
appropriate for GtkEv.
Unrealizing GtkEv is
relatively simple:
static void
gtk_ev_unrealize (GtkWidget *widget)
{
GtkEv* ev;
g_return_if_fail(widget != NULL);
g_return_if_fail(GTK_IS_EV(widget));
ev = GTK_EV(widget);
/* Hide all windows */
if (GTK_WIDGET_MAPPED (widget))
gtk_widget_unmap (widget);
GTK_WIDGET_UNSET_FLAGS (widget, GTK_MAPPED);
/* Destroy our child window */
if (ev->event_window)
{
gdk_window_set_user_data(ev->event_window, NULL);
gdk_window_destroy(ev->event_window);
ev->event_window = NULL;
}
/* This destroys widget->window and unsets the realized flag
*/
if (GTK_WIDGET_CLASS(parent_class)->unrealize)
(* GTK_WIDGET_CLASS(parent_class)->unrealize) (widget);
}
|
First, the unrealize method ensures that the widget is
unmapped. This is essential: GTK+ maintains the invariant
that mapped widgets are also realized. Next, the
unrealize method destroys the event window. It sets the
window's user data to
NULL before destroying it; otherwise GtkEv would receive a useless destroy
event. Finally, GtkEv chains
up to the default unrealize method, which unsets the
GTK_WIDGET_REALIZED flag
and destroys
widget->window. Unrealize implemenations are required to chain up to their base
class's implementation.
When writing your realize and unrealize methods, keep in
mind that they can be called multiple times, but they are
always paired. That is, a widget can be unrealized and
re-realized over and over, but it will never be realized
twice without an intervening unrealize. The pairing is
guaranteed; that is, if a widget is realized it will
definitely be unrealized sooner or later, unless the
program exits.