]> Pileus Git - ~andy/gtk/blob - gtk/gtkeventbox.c
Merge ssh://git.gnome.org/git/gtk+
[~andy/gtk] / gtk / gtkeventbox.c
1 /* GTK - The GIMP Toolkit
2  * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
3  *
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.
8  *
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.
13  *
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.
18  */
19
20 /*
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/.
25  */
26
27 #include "config.h"
28
29 #include "gtkeventbox.h"
30
31 #include "gtksizerequest.h"
32
33 #include "gtkprivate.h"
34 #include "gtkintl.h"
35
36
37 /**
38  * SECTION:gtkeventbox
39  * @Short_description: A widget used to catch events for widgets which
40  *     do not have their own window
41  * @Title: GtkEventBox
42  *
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.
46  */
47
48 struct _GtkEventBoxPrivate
49 {
50   gboolean above_child;
51   GdkWindow *event_window;
52 };
53
54 enum {
55   PROP_0,
56   PROP_VISIBLE_WINDOW,
57   PROP_ABOVE_CHILD
58 };
59
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,
65                                                     gint      *minimum,
66                                                     gint      *natural);
67 static void     gtk_event_box_get_preferred_height (GtkWidget *widget,
68                                                     gint      *minimum,
69                                                     gint      *natural);
70 static void     gtk_event_box_size_allocate (GtkWidget        *widget,
71                                              GtkAllocation    *allocation);
72 static gboolean gtk_event_box_draw          (GtkWidget        *widget,
73                                              cairo_t          *cr);
74 static void     gtk_event_box_set_property  (GObject          *object,
75                                              guint             prop_id,
76                                              const GValue     *value,
77                                              GParamSpec       *pspec);
78 static void     gtk_event_box_get_property  (GObject          *object,
79                                              guint             prop_id,
80                                              GValue           *value,
81                                              GParamSpec       *pspec);
82
83 G_DEFINE_TYPE (GtkEventBox, gtk_event_box, GTK_TYPE_BIN)
84
85 static void
86 gtk_event_box_class_init (GtkEventBoxClass *class)
87 {
88   GObjectClass *gobject_class = G_OBJECT_CLASS (class);
89   GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
90   GtkContainerClass *container_class = GTK_CONTAINER_CLASS (class);
91
92   gobject_class->set_property = gtk_event_box_set_property;
93   gobject_class->get_property = gtk_event_box_get_property;
94
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;
103
104   gtk_container_class_handle_border_width (container_class);
105
106   g_object_class_install_property (gobject_class,
107                                    PROP_VISIBLE_WINDOW,
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."),
111                                                         TRUE,
112                                                         GTK_PARAM_READWRITE));
113   g_object_class_install_property (gobject_class,
114                                    PROP_ABOVE_CHILD,
115                                    g_param_spec_boolean ("above-child",
116                                                         P_("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."),
118                                                         FALSE,
119                                                         GTK_PARAM_READWRITE));
120
121   g_type_class_add_private (class, sizeof (GtkEventBoxPrivate));
122 }
123
124 static void
125 gtk_event_box_init (GtkEventBox *event_box)
126 {
127   GtkEventBoxPrivate *priv;
128
129   gtk_widget_set_has_window (GTK_WIDGET (event_box), TRUE);
130
131   priv = G_TYPE_INSTANCE_GET_PRIVATE (event_box,
132                                       GTK_TYPE_EVENT_BOX,
133                                       GtkEventBoxPrivate);
134
135   event_box->priv = priv;
136   priv->above_child = FALSE;
137 }
138
139 /**
140  * gtk_event_box_new:
141  *
142  * Creates a new #GtkEventBox.
143  *
144  * Returns: a new #GtkEventBox
145  */
146 GtkWidget*
147 gtk_event_box_new (void)
148 {
149   return g_object_new (GTK_TYPE_EVENT_BOX, NULL);
150 }
151
152 static void
153 gtk_event_box_set_property (GObject      *object,
154                             guint         prop_id,
155                             const GValue *value,
156                             GParamSpec   *pspec)
157 {
158   GtkEventBox *event_box;
159
160   event_box = GTK_EVENT_BOX (object);
161
162   switch (prop_id)
163     {
164     case PROP_VISIBLE_WINDOW:
165       gtk_event_box_set_visible_window (event_box, g_value_get_boolean (value));
166       break;
167
168     case PROP_ABOVE_CHILD:
169       gtk_event_box_set_above_child (event_box, g_value_get_boolean (value));
170       break;
171
172     default:
173       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
174       break;
175     }
176 }
177
178 static void
179 gtk_event_box_get_property (GObject     *object,
180                             guint        prop_id,
181                             GValue      *value,
182                             GParamSpec  *pspec)
183 {
184   GtkEventBox *event_box;
185
186   event_box = GTK_EVENT_BOX (object);
187
188   switch (prop_id)
189     {
190     case PROP_VISIBLE_WINDOW:
191       g_value_set_boolean (value, gtk_event_box_get_visible_window (event_box));
192       break;
193
194     case PROP_ABOVE_CHILD:
195       g_value_set_boolean (value, gtk_event_box_get_above_child (event_box));
196       break;
197
198     default:
199       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
200       break;
201     }
202 }
203
204 /**
205  * gtk_event_box_get_visible_window:
206  * @event_box: a #GtkEventBox
207  *
208  * Returns whether the event box has a visible window.
209  * See gtk_event_box_set_visible_window() for details.
210  *
211  * Return value: %TRUE if the event box window is visible
212  *
213  * Since: 2.4
214  */
215 gboolean
216 gtk_event_box_get_visible_window (GtkEventBox *event_box)
217 {
218   g_return_val_if_fail (GTK_IS_EVENT_BOX (event_box), FALSE);
219
220   return gtk_widget_get_has_window (GTK_WIDGET (event_box));
221 }
222
223 /**
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
227  *
228  * Set whether the event box uses a visible or invisible child
229  * window. The default is to use visible windows.
230  *
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
234  * events.
235  *
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.
239  *
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.
244  *
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
247  * draw on it.
248  *
249  * <note><para>
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.
258  * </para><para>
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.
262  * </para></note>
263  *
264  * Since: 2.4
265  */
266 void
267 gtk_event_box_set_visible_window (GtkEventBox *event_box,
268                                   gboolean     visible_window)
269 {
270   GtkWidget *widget;
271
272   g_return_if_fail (GTK_IS_EVENT_BOX (event_box));
273
274   widget = GTK_WIDGET (event_box);
275
276   visible_window = visible_window != FALSE;
277
278   if (visible_window != gtk_widget_get_has_window (widget))
279     {
280       if (gtk_widget_get_realized (widget))
281         {
282           gboolean visible = gtk_widget_get_visible (widget);
283
284           if (visible)
285             gtk_widget_hide (widget);
286
287           gtk_widget_unrealize (widget);
288
289           gtk_widget_set_has_window (widget, visible_window);
290
291           gtk_widget_realize (widget);
292
293           if (visible)
294             gtk_widget_show (widget);
295         }
296       else
297         {
298           gtk_widget_set_has_window (widget, visible_window);
299         }
300
301       if (gtk_widget_get_visible (widget))
302         gtk_widget_queue_resize (widget);
303
304       g_object_notify (G_OBJECT (event_box), "visible-window");
305     }
306 }
307
308 /**
309  * gtk_event_box_get_above_child:
310  * @event_box: a #GtkEventBox
311  *
312  * Returns whether the event box window is above or below the
313  * windows of its child. See gtk_event_box_set_above_child()
314  * for details.
315  *
316  * Return value: %TRUE if the event box window is above the
317  *     window of its child
318  *
319  * Since: 2.4
320  */
321 gboolean
322 gtk_event_box_get_above_child (GtkEventBox *event_box)
323 {
324   GtkEventBoxPrivate *priv = event_box->priv;
325
326   g_return_val_if_fail (GTK_IS_EVENT_BOX (event_box), FALSE);
327
328   return priv->above_child;
329 }
330
331 /**
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
335  *
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.
341  *
342  * The default is to keep the window below the child.
343  *
344  * Since: 2.4
345  */
346 void
347 gtk_event_box_set_above_child (GtkEventBox *event_box,
348                                gboolean     above_child)
349 {
350   GtkEventBoxPrivate *priv = event_box->priv;
351   GtkWidget *widget;
352
353   g_return_if_fail (GTK_IS_EVENT_BOX (event_box));
354
355   widget = GTK_WIDGET (event_box);
356
357   above_child = above_child != FALSE;
358
359   if (priv->above_child != above_child)
360     {
361       priv->above_child = above_child;
362
363       if (gtk_widget_get_realized (widget))
364         {
365           if (!gtk_widget_get_has_window (widget))
366             {
367               if (above_child)
368                 gdk_window_raise (priv->event_window);
369               else
370                 gdk_window_lower (priv->event_window);
371             }
372           else
373             {
374               gboolean visible = gtk_widget_get_visible (widget);
375
376               if (visible)
377                 gtk_widget_hide (widget);
378
379               gtk_widget_unrealize (widget);
380               gtk_widget_realize (widget);
381
382               if (visible)
383                 gtk_widget_show (widget);
384             }
385         }
386
387       if (gtk_widget_get_visible (widget))
388         gtk_widget_queue_resize (widget);
389
390       g_object_notify (G_OBJECT (event_box), "above-child");
391     }
392 }
393
394 static void
395 gtk_event_box_realize (GtkWidget *widget)
396 {
397   GtkEventBoxPrivate *priv;
398   GtkAllocation allocation;
399   GdkWindow *window;
400   GdkWindowAttr attributes;
401   gint attributes_mask;
402   gboolean visible_window;
403
404   gtk_widget_get_allocation (widget, &allocation);
405
406   gtk_widget_set_realized (widget, TRUE);
407
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
417                         | GDK_EXPOSURE_MASK
418                         | GDK_ENTER_NOTIFY_MASK
419                         | GDK_LEAVE_NOTIFY_MASK;
420
421   priv = GTK_EVENT_BOX (widget)->priv;
422
423   visible_window = gtk_widget_get_has_window (widget);
424   if (visible_window)
425     {
426       attributes.visual = gtk_widget_get_visual (widget);
427       attributes.wclass = GDK_INPUT_OUTPUT;
428
429       attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL;
430
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);
435     }
436   else
437     {
438       window = gtk_widget_get_parent_window (widget);
439       gtk_widget_set_window (widget, window);
440       g_object_ref (window);
441     }
442
443   if (!visible_window || priv->above_child)
444     {
445       attributes.wclass = GDK_INPUT_ONLY;
446       if (!visible_window)
447         attributes_mask = GDK_WA_X | GDK_WA_Y;
448       else
449         attributes_mask = 0;
450
451       priv->event_window = gdk_window_new (window,
452                                            &attributes, attributes_mask);
453       gdk_window_set_user_data (priv->event_window, widget);
454     }
455
456   if (visible_window)
457     gtk_style_context_set_background (gtk_widget_get_style_context (widget), window);
458 }
459
460 static void
461 gtk_event_box_unrealize (GtkWidget *widget)
462 {
463   GtkEventBoxPrivate *priv = GTK_EVENT_BOX (widget)->priv;
464
465   if (priv->event_window != NULL)
466     {
467       gdk_window_set_user_data (priv->event_window, NULL);
468       gdk_window_destroy (priv->event_window);
469       priv->event_window = NULL;
470     }
471
472   GTK_WIDGET_CLASS (gtk_event_box_parent_class)->unrealize (widget);
473 }
474
475 static void
476 gtk_event_box_map (GtkWidget *widget)
477 {
478   GtkEventBoxPrivate *priv = GTK_EVENT_BOX (widget)->priv;
479
480   if (priv->event_window != NULL && !priv->above_child)
481     gdk_window_show (priv->event_window);
482
483   GTK_WIDGET_CLASS (gtk_event_box_parent_class)->map (widget);
484
485   if (priv->event_window != NULL && priv->above_child)
486     gdk_window_show (priv->event_window);
487 }
488
489 static void
490 gtk_event_box_unmap (GtkWidget *widget)
491 {
492   GtkEventBoxPrivate *priv = GTK_EVENT_BOX (widget)->priv;
493
494   if (priv->event_window != NULL)
495     gdk_window_hide (priv->event_window);
496
497   GTK_WIDGET_CLASS (gtk_event_box_parent_class)->unmap (widget);
498 }
499
500 static void
501 gtk_event_box_get_preferred_width (GtkWidget *widget,
502                                    gint      *minimum,
503                                    gint      *natural)
504 {
505   GtkBin *bin = GTK_BIN (widget);
506   GtkWidget *child;
507
508   if (minimum)
509     *minimum = 0;
510
511   if (natural)
512     *natural = 0;
513
514   child = gtk_bin_get_child (bin);
515   if (child && gtk_widget_get_visible (child))
516     gtk_widget_get_preferred_width (child, minimum, natural);
517 }
518
519 static void
520 gtk_event_box_get_preferred_height (GtkWidget *widget,
521                                     gint      *minimum,
522                                     gint      *natural)
523 {
524   GtkBin *bin = GTK_BIN (widget);
525   GtkWidget *child;
526
527   if (minimum)
528     *minimum = 0;
529
530   if (natural)
531     *natural = 0;
532
533   child = gtk_bin_get_child (bin);
534   if (child && gtk_widget_get_visible (child))
535     gtk_widget_get_preferred_height (child, minimum, natural);
536 }
537
538 static void
539 gtk_event_box_size_allocate (GtkWidget     *widget,
540                              GtkAllocation *allocation)
541 {
542   GtkBin *bin;
543   GtkAllocation child_allocation;
544   GtkEventBoxPrivate *priv;
545   GtkWidget *child;
546
547   bin = GTK_BIN (widget);
548
549   gtk_widget_set_allocation (widget, allocation);
550
551   if (!gtk_widget_get_has_window (widget))
552     {
553       child_allocation.x = allocation->x;
554       child_allocation.y = allocation->y;
555     }
556   else
557     {
558       child_allocation.x = 0;
559       child_allocation.y = 0;
560     }
561   child_allocation.width = allocation->width;
562   child_allocation.height = allocation->height;
563
564   if (gtk_widget_get_realized (widget))
565     {
566       priv = GTK_EVENT_BOX (widget)->priv;
567
568       if (priv->event_window != NULL)
569         gdk_window_move_resize (priv->event_window,
570                                 child_allocation.x,
571                                 child_allocation.y,
572                                 child_allocation.width,
573                                 child_allocation.height);
574
575       if (gtk_widget_get_has_window (widget))
576         gdk_window_move_resize (gtk_widget_get_window (widget),
577                                 allocation->x,
578                                 allocation->y,
579                                 child_allocation.width,
580                                 child_allocation.height);
581     }
582
583   child = gtk_bin_get_child (bin);
584   if (child)
585     gtk_widget_size_allocate (child, &child_allocation);
586 }
587
588 static gboolean
589 gtk_event_box_draw (GtkWidget *widget,
590                     cairo_t   *cr)
591 {
592   if (gtk_widget_get_has_window (widget) &&
593       !gtk_widget_get_app_paintable (widget))
594     {
595       GtkStyleContext *context;
596
597       context = gtk_widget_get_style_context (widget);
598
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);
605     }
606
607   GTK_WIDGET_CLASS (gtk_event_box_parent_class)->draw (widget, cr);
608
609   return FALSE;
610 }