1 /* GTK - The GIMP Toolkit
2 * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
19 * Modified by the GTK+ Team and others 1997-2000. See the AUTHORS
20 * file for a list of people on the GTK+ Team. See the ChangeLog
21 * files for a list of changes. These files are distributed with
22 * GTK+ at ftp://ftp.gtk.org/pub/gtk/.
27 #include "gtkeventbox.h"
29 #include "gtksizerequest.h"
31 #include "gtkprivate.h"
37 * @Short_description: A widget used to catch events for widgets which
38 * do not have their own window
41 * The #GtkEventBox widget is a subclass of #GtkBin which also has its
42 * own window. It is useful since it allows you to catch events for widgets
43 * which do not have their own window.
46 struct _GtkEventBoxPrivate
49 GdkWindow *event_window;
58 static void gtk_event_box_realize (GtkWidget *widget);
59 static void gtk_event_box_unrealize (GtkWidget *widget);
60 static void gtk_event_box_map (GtkWidget *widget);
61 static void gtk_event_box_unmap (GtkWidget *widget);
62 static void gtk_event_box_get_preferred_width (GtkWidget *widget,
65 static void gtk_event_box_get_preferred_height (GtkWidget *widget,
68 static void gtk_event_box_size_allocate (GtkWidget *widget,
69 GtkAllocation *allocation);
70 static gboolean gtk_event_box_draw (GtkWidget *widget,
72 static void gtk_event_box_set_property (GObject *object,
76 static void gtk_event_box_get_property (GObject *object,
81 G_DEFINE_TYPE (GtkEventBox, gtk_event_box, GTK_TYPE_BIN)
84 gtk_event_box_class_init (GtkEventBoxClass *class)
86 GObjectClass *gobject_class = G_OBJECT_CLASS (class);
87 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
88 GtkContainerClass *container_class = GTK_CONTAINER_CLASS (class);
90 gobject_class->set_property = gtk_event_box_set_property;
91 gobject_class->get_property = gtk_event_box_get_property;
93 widget_class->realize = gtk_event_box_realize;
94 widget_class->unrealize = gtk_event_box_unrealize;
95 widget_class->map = gtk_event_box_map;
96 widget_class->unmap = gtk_event_box_unmap;
97 widget_class->get_preferred_width = gtk_event_box_get_preferred_width;
98 widget_class->get_preferred_height = gtk_event_box_get_preferred_height;
99 widget_class->size_allocate = gtk_event_box_size_allocate;
100 widget_class->draw = gtk_event_box_draw;
102 gtk_container_class_handle_border_width (container_class);
104 g_object_class_install_property (gobject_class,
106 g_param_spec_boolean ("visible-window",
107 P_("Visible Window"),
108 P_("Whether the event box is visible, as opposed to invisible and only used to trap events."),
110 GTK_PARAM_READWRITE));
111 g_object_class_install_property (gobject_class,
113 g_param_spec_boolean ("above-child",
115 P_("Whether the event-trapping window of the eventbox is above the window of the child widget as opposed to below it."),
117 GTK_PARAM_READWRITE));
119 g_type_class_add_private (class, sizeof (GtkEventBoxPrivate));
123 gtk_event_box_init (GtkEventBox *event_box)
125 GtkEventBoxPrivate *priv;
127 gtk_widget_set_has_window (GTK_WIDGET (event_box), TRUE);
129 priv = G_TYPE_INSTANCE_GET_PRIVATE (event_box,
133 event_box->priv = priv;
134 priv->above_child = FALSE;
140 * Creates a new #GtkEventBox.
142 * Returns: a new #GtkEventBox
145 gtk_event_box_new (void)
147 return g_object_new (GTK_TYPE_EVENT_BOX, NULL);
151 gtk_event_box_set_property (GObject *object,
156 GtkEventBox *event_box;
158 event_box = GTK_EVENT_BOX (object);
162 case PROP_VISIBLE_WINDOW:
163 gtk_event_box_set_visible_window (event_box, g_value_get_boolean (value));
166 case PROP_ABOVE_CHILD:
167 gtk_event_box_set_above_child (event_box, g_value_get_boolean (value));
171 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
177 gtk_event_box_get_property (GObject *object,
182 GtkEventBox *event_box;
184 event_box = GTK_EVENT_BOX (object);
188 case PROP_VISIBLE_WINDOW:
189 g_value_set_boolean (value, gtk_event_box_get_visible_window (event_box));
192 case PROP_ABOVE_CHILD:
193 g_value_set_boolean (value, gtk_event_box_get_above_child (event_box));
197 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
203 * gtk_event_box_get_visible_window:
204 * @event_box: a #GtkEventBox
206 * Returns whether the event box has a visible window.
207 * See gtk_event_box_set_visible_window() for details.
209 * Return value: %TRUE if the event box window is visible
214 gtk_event_box_get_visible_window (GtkEventBox *event_box)
216 g_return_val_if_fail (GTK_IS_EVENT_BOX (event_box), FALSE);
218 return gtk_widget_get_has_window (GTK_WIDGET (event_box));
222 * gtk_event_box_set_visible_window:
223 * @event_box: a #GtkEventBox
224 * @visible_window: %TRUE to make the event box have a visible window
226 * Set whether the event box uses a visible or invisible child
227 * window. The default is to use visible windows.
229 * In an invisible window event box, the window that the
230 * event box creates is a %GDK_INPUT_ONLY window, which
231 * means that it is invisible and only serves to receive
234 * A visible window event box creates a visible (%GDK_INPUT_OUTPUT)
235 * window that acts as the parent window for all the widgets
236 * contained in the event box.
238 * You should generally make your event box invisible if
239 * you just want to trap events. Creating a visible window
240 * may cause artifacts that are visible to the user, especially
241 * if the user is using a theme with gradients or pixmaps.
243 * The main reason to create a non input-only event box is if
244 * you want to set the background to a different color or
248 * There is one unexpected issue for an invisible event box that has its
249 * window below the child. (See gtk_event_box_set_above_child().)
250 * Since the input-only window is not an ancestor window of any windows
251 * that descendent widgets of the event box create, events on these
252 * windows aren't propagated up by the windowing system, but only by GTK+.
253 * The practical effect of this is if an event isn't in the event
254 * mask for the descendant window (see gtk_widget_add_events()),
255 * it won't be received by the event box.
257 * This problem doesn't occur for visible event boxes, because in
258 * that case, the event box window is actually the ancestor of the
259 * descendant windows, not just at the same place on the screen.
265 gtk_event_box_set_visible_window (GtkEventBox *event_box,
266 gboolean visible_window)
270 g_return_if_fail (GTK_IS_EVENT_BOX (event_box));
272 widget = GTK_WIDGET (event_box);
274 visible_window = visible_window != FALSE;
276 if (visible_window != gtk_widget_get_has_window (widget))
278 if (gtk_widget_get_realized (widget))
280 gboolean visible = gtk_widget_get_visible (widget);
283 gtk_widget_hide (widget);
285 gtk_widget_unrealize (widget);
287 gtk_widget_set_has_window (widget, visible_window);
289 gtk_widget_realize (widget);
292 gtk_widget_show (widget);
296 gtk_widget_set_has_window (widget, visible_window);
299 if (gtk_widget_get_visible (widget))
300 gtk_widget_queue_resize (widget);
302 g_object_notify (G_OBJECT (event_box), "visible-window");
307 * gtk_event_box_get_above_child:
308 * @event_box: a #GtkEventBox
310 * Returns whether the event box window is above or below the
311 * windows of its child. See gtk_event_box_set_above_child()
314 * Return value: %TRUE if the event box window is above the
315 * window of its child
320 gtk_event_box_get_above_child (GtkEventBox *event_box)
322 GtkEventBoxPrivate *priv = event_box->priv;
324 g_return_val_if_fail (GTK_IS_EVENT_BOX (event_box), FALSE);
326 return priv->above_child;
330 * gtk_event_box_set_above_child:
331 * @event_box: a #GtkEventBox
332 * @above_child: %TRUE if the event box window is above its child
334 * Set whether the event box window is positioned above the windows
335 * of its child, as opposed to below it. If the window is above, all
336 * events inside the event box will go to the event box. If the window
337 * is below, events in windows of child widgets will first got to that
338 * widget, and then to its parents.
340 * The default is to keep the window below the child.
345 gtk_event_box_set_above_child (GtkEventBox *event_box,
346 gboolean above_child)
348 GtkEventBoxPrivate *priv = event_box->priv;
351 g_return_if_fail (GTK_IS_EVENT_BOX (event_box));
353 widget = GTK_WIDGET (event_box);
355 above_child = above_child != FALSE;
357 if (priv->above_child != above_child)
359 priv->above_child = above_child;
361 if (gtk_widget_get_realized (widget))
363 if (!gtk_widget_get_has_window (widget))
366 gdk_window_raise (priv->event_window);
368 gdk_window_lower (priv->event_window);
372 gboolean visible = gtk_widget_get_visible (widget);
375 gtk_widget_hide (widget);
377 gtk_widget_unrealize (widget);
378 gtk_widget_realize (widget);
381 gtk_widget_show (widget);
385 if (gtk_widget_get_visible (widget))
386 gtk_widget_queue_resize (widget);
388 g_object_notify (G_OBJECT (event_box), "above-child");
393 gtk_event_box_realize (GtkWidget *widget)
395 GtkEventBoxPrivate *priv;
396 GtkAllocation allocation;
398 GdkWindowAttr attributes;
399 gint attributes_mask;
400 gboolean visible_window;
402 gtk_widget_get_allocation (widget, &allocation);
404 gtk_widget_set_realized (widget, TRUE);
406 attributes.x = allocation.x;
407 attributes.y = allocation.y;
408 attributes.width = allocation.width;
409 attributes.height = allocation.height;
410 attributes.window_type = GDK_WINDOW_CHILD;
411 attributes.event_mask = gtk_widget_get_events (widget)
412 | GDK_BUTTON_MOTION_MASK
413 | GDK_BUTTON_PRESS_MASK
414 | GDK_BUTTON_RELEASE_MASK
416 | GDK_ENTER_NOTIFY_MASK
417 | GDK_LEAVE_NOTIFY_MASK;
419 priv = GTK_EVENT_BOX (widget)->priv;
421 visible_window = gtk_widget_get_has_window (widget);
424 attributes.visual = gtk_widget_get_visual (widget);
425 attributes.wclass = GDK_INPUT_OUTPUT;
427 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL;
429 window = gdk_window_new (gtk_widget_get_parent_window (widget),
430 &attributes, attributes_mask);
431 gtk_widget_set_window (widget, window);
432 gdk_window_set_user_data (window, widget);
436 window = gtk_widget_get_parent_window (widget);
437 gtk_widget_set_window (widget, window);
438 g_object_ref (window);
441 if (!visible_window || priv->above_child)
443 attributes.wclass = GDK_INPUT_ONLY;
445 attributes_mask = GDK_WA_X | GDK_WA_Y;
449 priv->event_window = gdk_window_new (window,
450 &attributes, attributes_mask);
451 gdk_window_set_user_data (priv->event_window, widget);
455 gtk_style_context_set_background (gtk_widget_get_style_context (widget), window);
459 gtk_event_box_unrealize (GtkWidget *widget)
461 GtkEventBoxPrivate *priv = GTK_EVENT_BOX (widget)->priv;
463 if (priv->event_window != NULL)
465 gdk_window_set_user_data (priv->event_window, NULL);
466 gdk_window_destroy (priv->event_window);
467 priv->event_window = NULL;
470 GTK_WIDGET_CLASS (gtk_event_box_parent_class)->unrealize (widget);
474 gtk_event_box_map (GtkWidget *widget)
476 GtkEventBoxPrivate *priv = GTK_EVENT_BOX (widget)->priv;
478 if (priv->event_window != NULL && !priv->above_child)
479 gdk_window_show (priv->event_window);
481 GTK_WIDGET_CLASS (gtk_event_box_parent_class)->map (widget);
483 if (priv->event_window != NULL && priv->above_child)
484 gdk_window_show (priv->event_window);
488 gtk_event_box_unmap (GtkWidget *widget)
490 GtkEventBoxPrivate *priv = GTK_EVENT_BOX (widget)->priv;
492 if (priv->event_window != NULL)
493 gdk_window_hide (priv->event_window);
495 GTK_WIDGET_CLASS (gtk_event_box_parent_class)->unmap (widget);
499 gtk_event_box_get_preferred_width (GtkWidget *widget,
503 GtkBin *bin = GTK_BIN (widget);
512 child = gtk_bin_get_child (bin);
513 if (child && gtk_widget_get_visible (child))
514 gtk_widget_get_preferred_width (child, minimum, natural);
518 gtk_event_box_get_preferred_height (GtkWidget *widget,
522 GtkBin *bin = GTK_BIN (widget);
531 child = gtk_bin_get_child (bin);
532 if (child && gtk_widget_get_visible (child))
533 gtk_widget_get_preferred_height (child, minimum, natural);
537 gtk_event_box_size_allocate (GtkWidget *widget,
538 GtkAllocation *allocation)
541 GtkAllocation child_allocation;
542 GtkEventBoxPrivate *priv;
545 bin = GTK_BIN (widget);
547 gtk_widget_set_allocation (widget, allocation);
549 if (!gtk_widget_get_has_window (widget))
551 child_allocation.x = allocation->x;
552 child_allocation.y = allocation->y;
556 child_allocation.x = 0;
557 child_allocation.y = 0;
559 child_allocation.width = allocation->width;
560 child_allocation.height = allocation->height;
562 if (gtk_widget_get_realized (widget))
564 priv = GTK_EVENT_BOX (widget)->priv;
566 if (priv->event_window != NULL)
567 gdk_window_move_resize (priv->event_window,
570 child_allocation.width,
571 child_allocation.height);
573 if (gtk_widget_get_has_window (widget))
574 gdk_window_move_resize (gtk_widget_get_window (widget),
577 child_allocation.width,
578 child_allocation.height);
581 child = gtk_bin_get_child (bin);
583 gtk_widget_size_allocate (child, &child_allocation);
587 gtk_event_box_draw (GtkWidget *widget,
590 if (gtk_widget_get_has_window (widget) &&
591 !gtk_widget_get_app_paintable (widget))
593 GtkStyleContext *context;
595 context = gtk_widget_get_style_context (widget);
597 gtk_render_background (context, cr, 0, 0,
598 gtk_widget_get_allocated_width (widget),
599 gtk_widget_get_allocated_height (widget));
602 GTK_WIDGET_CLASS (gtk_event_box_parent_class)->draw (widget, cr);