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, write to the
16 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17 * Boston, MA 02111-1307, USA.
21 * Modified by the GTK+ Team and others 1997-2000. See the AUTHORS
22 * file for a list of people on the GTK+ Team. See the ChangeLog
23 * files for a list of changes. These files are distributed with
24 * GTK+ at ftp://ftp.gtk.org/pub/gtk/.
29 #include "gtkeventbox.h"
31 #include "gtksizerequest.h"
33 #include "gtkprivate.h"
39 * @Short_description: A widget used to catch events for widgets which
40 * do not have their own window
43 * The #GtkEventBox widget is a subclass of #GtkBin which also has its
44 * own window. It is useful since it allows you to catch events for widgets
45 * which do not have their own window.
48 struct _GtkEventBoxPrivate
51 GdkWindow *event_window;
60 static void gtk_event_box_realize (GtkWidget *widget);
61 static void gtk_event_box_unrealize (GtkWidget *widget);
62 static void gtk_event_box_map (GtkWidget *widget);
63 static void gtk_event_box_unmap (GtkWidget *widget);
64 static void gtk_event_box_get_preferred_width (GtkWidget *widget,
67 static void gtk_event_box_get_preferred_height (GtkWidget *widget,
70 static void gtk_event_box_size_allocate (GtkWidget *widget,
71 GtkAllocation *allocation);
72 static gboolean gtk_event_box_draw (GtkWidget *widget,
74 static void gtk_event_box_set_property (GObject *object,
78 static void gtk_event_box_get_property (GObject *object,
83 G_DEFINE_TYPE (GtkEventBox, gtk_event_box, GTK_TYPE_BIN)
86 gtk_event_box_class_init (GtkEventBoxClass *class)
88 GObjectClass *gobject_class = G_OBJECT_CLASS (class);
89 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
90 GtkContainerClass *container_class = GTK_CONTAINER_CLASS (class);
92 gobject_class->set_property = gtk_event_box_set_property;
93 gobject_class->get_property = gtk_event_box_get_property;
95 widget_class->realize = gtk_event_box_realize;
96 widget_class->unrealize = gtk_event_box_unrealize;
97 widget_class->map = gtk_event_box_map;
98 widget_class->unmap = gtk_event_box_unmap;
99 widget_class->get_preferred_width = gtk_event_box_get_preferred_width;
100 widget_class->get_preferred_height = gtk_event_box_get_preferred_height;
101 widget_class->size_allocate = gtk_event_box_size_allocate;
102 widget_class->draw = gtk_event_box_draw;
104 gtk_container_class_handle_border_width (container_class);
106 g_object_class_install_property (gobject_class,
108 g_param_spec_boolean ("visible-window",
109 P_("Visible Window"),
110 P_("Whether the event box is visible, as opposed to invisible and only used to trap events."),
112 GTK_PARAM_READWRITE));
113 g_object_class_install_property (gobject_class,
115 g_param_spec_boolean ("above-child",
117 P_("Whether the event-trapping window of the eventbox is above the window of the child widget as opposed to below it."),
119 GTK_PARAM_READWRITE));
121 g_type_class_add_private (class, sizeof (GtkEventBoxPrivate));
125 gtk_event_box_init (GtkEventBox *event_box)
127 GtkEventBoxPrivate *priv;
129 gtk_widget_set_has_window (GTK_WIDGET (event_box), TRUE);
131 priv = G_TYPE_INSTANCE_GET_PRIVATE (event_box,
135 event_box->priv = priv;
136 priv->above_child = FALSE;
142 * Creates a new #GtkEventBox.
144 * Returns: a new #GtkEventBox
147 gtk_event_box_new (void)
149 return g_object_new (GTK_TYPE_EVENT_BOX, NULL);
153 gtk_event_box_set_property (GObject *object,
158 GtkEventBox *event_box;
160 event_box = GTK_EVENT_BOX (object);
164 case PROP_VISIBLE_WINDOW:
165 gtk_event_box_set_visible_window (event_box, g_value_get_boolean (value));
168 case PROP_ABOVE_CHILD:
169 gtk_event_box_set_above_child (event_box, g_value_get_boolean (value));
173 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
179 gtk_event_box_get_property (GObject *object,
184 GtkEventBox *event_box;
186 event_box = GTK_EVENT_BOX (object);
190 case PROP_VISIBLE_WINDOW:
191 g_value_set_boolean (value, gtk_event_box_get_visible_window (event_box));
194 case PROP_ABOVE_CHILD:
195 g_value_set_boolean (value, gtk_event_box_get_above_child (event_box));
199 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
205 * gtk_event_box_get_visible_window:
206 * @event_box: a #GtkEventBox
208 * Returns whether the event box has a visible window.
209 * See gtk_event_box_set_visible_window() for details.
211 * Return value: %TRUE if the event box window is visible
216 gtk_event_box_get_visible_window (GtkEventBox *event_box)
218 g_return_val_if_fail (GTK_IS_EVENT_BOX (event_box), FALSE);
220 return gtk_widget_get_has_window (GTK_WIDGET (event_box));
224 * gtk_event_box_set_visible_window:
225 * @event_box: a #GtkEventBox
226 * @visible_window: %TRUE to make the event box have a visible window
228 * Set whether the event box uses a visible or invisible child
229 * window. The default is to use visible windows.
231 * In an invisible window event box, the window that the
232 * event box creates is a %GDK_INPUT_ONLY window, which
233 * means that it is invisible and only serves to receive
236 * A visible window event box creates a visible (%GDK_INPUT_OUTPUT)
237 * window that acts as the parent window for all the widgets
238 * contained in the event box.
240 * You should generally make your event box invisible if
241 * you just want to trap events. Creating a visible window
242 * may cause artifacts that are visible to the user, especially
243 * if the user is using a theme with gradients or pixmaps.
245 * The main reason to create a non input-only event box is if
246 * you want to set the background to a different color or
250 * There is one unexpected issue for an invisible event box that has its
251 * window below the child. (See gtk_event_box_set_above_child().)
252 * Since the input-only window is not an ancestor window of any windows
253 * that descendent widgets of the event box create, events on these
254 * windows aren't propagated up by the windowing system, but only by GTK+.
255 * The practical effect of this is if an event isn't in the event
256 * mask for the descendant window (see gtk_widget_add_events()),
257 * it won't be received by the event box.
259 * This problem doesn't occur for visible event boxes, because in
260 * that case, the event box window is actually the ancestor of the
261 * descendant windows, not just at the same place on the screen.
267 gtk_event_box_set_visible_window (GtkEventBox *event_box,
268 gboolean visible_window)
272 g_return_if_fail (GTK_IS_EVENT_BOX (event_box));
274 widget = GTK_WIDGET (event_box);
276 visible_window = visible_window != FALSE;
278 if (visible_window != gtk_widget_get_has_window (widget))
280 if (gtk_widget_get_realized (widget))
282 gboolean visible = gtk_widget_get_visible (widget);
285 gtk_widget_hide (widget);
287 gtk_widget_unrealize (widget);
289 gtk_widget_set_has_window (widget, visible_window);
291 gtk_widget_realize (widget);
294 gtk_widget_show (widget);
298 gtk_widget_set_has_window (widget, visible_window);
301 if (gtk_widget_get_visible (widget))
302 gtk_widget_queue_resize (widget);
304 g_object_notify (G_OBJECT (event_box), "visible-window");
309 * gtk_event_box_get_above_child:
310 * @event_box: a #GtkEventBox
312 * Returns whether the event box window is above or below the
313 * windows of its child. See gtk_event_box_set_above_child()
316 * Return value: %TRUE if the event box window is above the
317 * window of its child
322 gtk_event_box_get_above_child (GtkEventBox *event_box)
324 GtkEventBoxPrivate *priv = event_box->priv;
326 g_return_val_if_fail (GTK_IS_EVENT_BOX (event_box), FALSE);
328 return priv->above_child;
332 * gtk_event_box_set_above_child:
333 * @event_box: a #GtkEventBox
334 * @above_child: %TRUE if the event box window is above its child
336 * Set whether the event box window is positioned above the windows
337 * of its child, as opposed to below it. If the window is above, all
338 * events inside the event box will go to the event box. If the window
339 * is below, events in windows of child widgets will first got to that
340 * widget, and then to its parents.
342 * The default is to keep the window below the child.
347 gtk_event_box_set_above_child (GtkEventBox *event_box,
348 gboolean above_child)
350 GtkEventBoxPrivate *priv = event_box->priv;
353 g_return_if_fail (GTK_IS_EVENT_BOX (event_box));
355 widget = GTK_WIDGET (event_box);
357 above_child = above_child != FALSE;
359 if (priv->above_child != above_child)
361 priv->above_child = above_child;
363 if (gtk_widget_get_realized (widget))
365 if (!gtk_widget_get_has_window (widget))
368 gdk_window_raise (priv->event_window);
370 gdk_window_lower (priv->event_window);
374 gboolean visible = gtk_widget_get_visible (widget);
377 gtk_widget_hide (widget);
379 gtk_widget_unrealize (widget);
380 gtk_widget_realize (widget);
383 gtk_widget_show (widget);
387 if (gtk_widget_get_visible (widget))
388 gtk_widget_queue_resize (widget);
390 g_object_notify (G_OBJECT (event_box), "above-child");
395 gtk_event_box_realize (GtkWidget *widget)
397 GtkEventBoxPrivate *priv;
398 GtkAllocation allocation;
400 GdkWindowAttr attributes;
401 gint attributes_mask;
402 gboolean visible_window;
404 gtk_widget_get_allocation (widget, &allocation);
406 gtk_widget_set_realized (widget, TRUE);
408 attributes.x = allocation.x;
409 attributes.y = allocation.y;
410 attributes.width = allocation.width;
411 attributes.height = allocation.height;
412 attributes.window_type = GDK_WINDOW_CHILD;
413 attributes.event_mask = gtk_widget_get_events (widget)
414 | GDK_BUTTON_MOTION_MASK
415 | GDK_BUTTON_PRESS_MASK
416 | GDK_BUTTON_RELEASE_MASK
418 | GDK_ENTER_NOTIFY_MASK
419 | GDK_LEAVE_NOTIFY_MASK;
421 priv = GTK_EVENT_BOX (widget)->priv;
423 visible_window = gtk_widget_get_has_window (widget);
426 attributes.visual = gtk_widget_get_visual (widget);
427 attributes.wclass = GDK_INPUT_OUTPUT;
429 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL;
431 window = gdk_window_new (gtk_widget_get_parent_window (widget),
432 &attributes, attributes_mask);
433 gtk_widget_set_window (widget, window);
434 gdk_window_set_user_data (window, widget);
438 window = gtk_widget_get_parent_window (widget);
439 gtk_widget_set_window (widget, window);
440 g_object_ref (window);
443 if (!visible_window || priv->above_child)
445 attributes.wclass = GDK_INPUT_ONLY;
447 attributes_mask = GDK_WA_X | GDK_WA_Y;
451 priv->event_window = gdk_window_new (window,
452 &attributes, attributes_mask);
453 gdk_window_set_user_data (priv->event_window, widget);
457 gtk_style_context_set_background (gtk_widget_get_style_context (widget), window);
461 gtk_event_box_unrealize (GtkWidget *widget)
463 GtkEventBoxPrivate *priv = GTK_EVENT_BOX (widget)->priv;
465 if (priv->event_window != NULL)
467 gdk_window_set_user_data (priv->event_window, NULL);
468 gdk_window_destroy (priv->event_window);
469 priv->event_window = NULL;
472 GTK_WIDGET_CLASS (gtk_event_box_parent_class)->unrealize (widget);
476 gtk_event_box_map (GtkWidget *widget)
478 GtkEventBoxPrivate *priv = GTK_EVENT_BOX (widget)->priv;
480 if (priv->event_window != NULL && !priv->above_child)
481 gdk_window_show (priv->event_window);
483 GTK_WIDGET_CLASS (gtk_event_box_parent_class)->map (widget);
485 if (priv->event_window != NULL && priv->above_child)
486 gdk_window_show (priv->event_window);
490 gtk_event_box_unmap (GtkWidget *widget)
492 GtkEventBoxPrivate *priv = GTK_EVENT_BOX (widget)->priv;
494 if (priv->event_window != NULL)
495 gdk_window_hide (priv->event_window);
497 GTK_WIDGET_CLASS (gtk_event_box_parent_class)->unmap (widget);
501 gtk_event_box_get_preferred_width (GtkWidget *widget,
505 GtkBin *bin = GTK_BIN (widget);
514 child = gtk_bin_get_child (bin);
515 if (child && gtk_widget_get_visible (child))
516 gtk_widget_get_preferred_width (child, minimum, natural);
520 gtk_event_box_get_preferred_height (GtkWidget *widget,
524 GtkBin *bin = GTK_BIN (widget);
533 child = gtk_bin_get_child (bin);
534 if (child && gtk_widget_get_visible (child))
535 gtk_widget_get_preferred_height (child, minimum, natural);
539 gtk_event_box_size_allocate (GtkWidget *widget,
540 GtkAllocation *allocation)
543 GtkAllocation child_allocation;
544 GtkEventBoxPrivate *priv;
547 bin = GTK_BIN (widget);
549 gtk_widget_set_allocation (widget, allocation);
551 if (!gtk_widget_get_has_window (widget))
553 child_allocation.x = allocation->x;
554 child_allocation.y = allocation->y;
558 child_allocation.x = 0;
559 child_allocation.y = 0;
561 child_allocation.width = allocation->width;
562 child_allocation.height = allocation->height;
564 if (gtk_widget_get_realized (widget))
566 priv = GTK_EVENT_BOX (widget)->priv;
568 if (priv->event_window != NULL)
569 gdk_window_move_resize (priv->event_window,
572 child_allocation.width,
573 child_allocation.height);
575 if (gtk_widget_get_has_window (widget))
576 gdk_window_move_resize (gtk_widget_get_window (widget),
579 child_allocation.width,
580 child_allocation.height);
583 child = gtk_bin_get_child (bin);
585 gtk_widget_size_allocate (child, &child_allocation);
589 gtk_event_box_draw (GtkWidget *widget,
592 if (gtk_widget_get_has_window (widget) &&
593 !gtk_widget_get_app_paintable (widget))
595 GtkStyleContext *context;
597 context = gtk_widget_get_style_context (widget);
599 gtk_style_context_save (context);
600 gtk_style_context_set_state (context, gtk_widget_get_state_flags (widget));
601 gtk_render_background (context, cr, 0, 0,
602 gtk_widget_get_allocated_width (widget),
603 gtk_widget_get_allocated_height (widget));
604 gtk_style_context_restore (context);
607 GTK_WIDGET_CLASS (gtk_event_box_parent_class)->draw (widget, cr);