]> Pileus Git - ~andy/gtk/blob - gtk/gtkhandlebox.c
[GI] Add (type) annotations to real types
[~andy/gtk] / gtk / gtkhandlebox.c
1 /* GTK - The GIMP Toolkit
2  * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
3  * Copyright (C) 1998 Elliot Lee
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with this library; if not, write to the
17  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18  * Boston, MA 02111-1307, USA.
19  */
20
21 /*
22  * Modified by the GTK+ Team and others 1997-2000.  See the AUTHORS
23  * file for a list of people on the GTK+ Team.  See the ChangeLog
24  * files for a list of changes.  These files are distributed with
25  * GTK+ at ftp://ftp.gtk.org/pub/gtk/. 
26  */
27
28 #include "config.h"
29
30 #include <stdlib.h>
31
32 #include "gtkhandlebox.h"
33 #include "gtkinvisible.h"
34 #include "gtkmain.h"
35 #include "gtkmarshalers.h"
36 #include "gtkwindow.h"
37 #include "gtktypebuiltins.h"
38 #include "gtkprivate.h"
39 #include "gtkintl.h"
40
41
42
43 struct _GtkHandleBoxPrivate
44 {
45   /* Properties */
46   GtkPositionType handle_position;
47   GtkPositionType snap_edge;
48   GtkShadowType   shadow_type;
49   gboolean        child_detached;
50   /* Properties */
51
52   GtkAllocation   attach_allocation;
53   GtkAllocation   float_allocation;
54
55   GdkDevice      *grab_device;
56
57   GdkWindow      *bin_window;     /* parent window for children */
58   GdkWindow      *float_window;
59
60   /* Variables used during a drag
61    */
62   gint            orig_x;
63   gint            orig_y;
64
65   guint           float_window_mapped : 1;
66   guint           in_drag : 1;
67   guint           shrink_on_detach : 1;
68 };
69
70 enum {
71   PROP_0,
72   PROP_SHADOW_TYPE,
73   PROP_HANDLE_POSITION,
74   PROP_SNAP_EDGE,
75   PROP_SNAP_EDGE_SET,
76   PROP_CHILD_DETACHED
77 };
78
79 #define DRAG_HANDLE_SIZE 10
80 #define CHILDLESS_SIZE  25
81 #define GHOST_HEIGHT 3
82 #define TOLERANCE 5
83
84 enum {
85   SIGNAL_CHILD_ATTACHED,
86   SIGNAL_CHILD_DETACHED,
87   SIGNAL_LAST
88 };
89
90 /* The algorithm for docking and redocking implemented here
91  * has a couple of nice properties:
92  *
93  * 1) During a single drag, docking always occurs at the
94  *    the same cursor position. This means that the users
95  *    motions are reversible, and that you won't
96  *    undock/dock oscillations.
97  *
98  * 2) Docking generally occurs at user-visible features.
99  *    The user, once they figure out to redock, will
100  *    have useful information about doing it again in
101  *    the future.
102  *
103  * Please try to preserve these properties if you
104  * change the algorithm. (And the current algorithm
105  * is far from ideal). Briefly, the current algorithm
106  * for deciding whether the handlebox is docked or not:
107  *
108  * 1) The decision is done by comparing two rectangles - the
109  *    allocation if the widget at the start of the drag,
110  *    and the boundary of hb->bin_window at the start of
111  *    of the drag offset by the distance that the cursor
112  *    has moved.
113  *
114  * 2) These rectangles must have one edge, the "snap_edge"
115  *    of the handlebox, aligned within TOLERANCE.
116  * 
117  * 3) On the other dimension, the extents of one rectangle
118  *    must be contained in the extents of the other,
119  *    extended by tolerance. That is, either we can have:
120  *
121  * <-TOLERANCE-|--------bin_window--------------|-TOLERANCE->
122  *         <--------float_window-------------------->
123  *
124  * or we can have:
125  *
126  * <-TOLERANCE-|------float_window--------------|-TOLERANCE->
127  *          <--------bin_window-------------------->
128  */
129
130 static void     gtk_handle_box_set_property  (GObject        *object,
131                                               guint           param_id,
132                                               const GValue   *value,
133                                               GParamSpec     *pspec);
134 static void     gtk_handle_box_get_property  (GObject        *object,
135                                               guint           param_id,
136                                               GValue         *value,
137                                               GParamSpec     *pspec);
138 static void     gtk_handle_box_map           (GtkWidget      *widget);
139 static void     gtk_handle_box_unmap         (GtkWidget      *widget);
140 static void     gtk_handle_box_realize       (GtkWidget      *widget);
141 static void     gtk_handle_box_unrealize     (GtkWidget      *widget);
142 static void     gtk_handle_box_style_updated (GtkWidget      *widget);
143 static void     gtk_handle_box_size_request  (GtkWidget      *widget,
144                                               GtkRequisition *requisition);
145 static void     gtk_handle_box_get_preferred_width (GtkWidget *widget,
146                                                     gint      *minimum,
147                                                     gint      *natural);
148 static void     gtk_handle_box_get_preferred_height (GtkWidget *widget,
149                                                     gint      *minimum,
150                                                     gint      *natural);
151 static void     gtk_handle_box_size_allocate (GtkWidget      *widget,
152                                               GtkAllocation  *real_allocation);
153 static void     gtk_handle_box_add           (GtkContainer   *container,
154                                               GtkWidget      *widget);
155 static void     gtk_handle_box_remove        (GtkContainer   *container,
156                                               GtkWidget      *widget);
157 static gboolean gtk_handle_box_draw          (GtkWidget      *widget,
158                                               cairo_t        *cr);
159 static gboolean gtk_handle_box_button_press  (GtkWidget      *widget,
160                                               GdkEventButton *event);
161 static gboolean gtk_handle_box_motion        (GtkWidget      *widget,
162                                               GdkEventMotion *event);
163 static gboolean gtk_handle_box_delete_event  (GtkWidget      *widget,
164                                               GdkEventAny    *event);
165 static void     gtk_handle_box_reattach      (GtkHandleBox   *hb);
166 static void     gtk_handle_box_end_drag      (GtkHandleBox   *hb,
167                                               guint32         time);
168
169 static guint handle_box_signals[SIGNAL_LAST] = { 0 };
170
171 G_DEFINE_TYPE (GtkHandleBox, gtk_handle_box, GTK_TYPE_BIN)
172
173 static void
174 gtk_handle_box_class_init (GtkHandleBoxClass *class)
175 {
176   GObjectClass *gobject_class;
177   GtkWidgetClass *widget_class;
178   GtkContainerClass *container_class;
179
180   gobject_class = (GObjectClass *) class;
181   widget_class = (GtkWidgetClass *) class;
182   container_class = (GtkContainerClass *) class;
183
184   gobject_class->set_property = gtk_handle_box_set_property;
185   gobject_class->get_property = gtk_handle_box_get_property;
186   
187   g_object_class_install_property (gobject_class,
188                                    PROP_SHADOW_TYPE,
189                                    g_param_spec_enum ("shadow-type",
190                                                       P_("Shadow type"),
191                                                       P_("Appearance of the shadow that surrounds the container"),
192                                                       GTK_TYPE_SHADOW_TYPE,
193                                                       GTK_SHADOW_OUT,
194                                                       GTK_PARAM_READWRITE));
195   
196   g_object_class_install_property (gobject_class,
197                                    PROP_HANDLE_POSITION,
198                                    g_param_spec_enum ("handle-position",
199                                                       P_("Handle position"),
200                                                       P_("Position of the handle relative to the child widget"),
201                                                       GTK_TYPE_POSITION_TYPE,
202                                                       GTK_POS_LEFT,
203                                                       GTK_PARAM_READWRITE));
204   
205   g_object_class_install_property (gobject_class,
206                                    PROP_SNAP_EDGE,
207                                    g_param_spec_enum ("snap-edge",
208                                                       P_("Snap edge"),
209                                                       P_("Side of the handlebox that's lined up with the docking point to dock the handlebox"),
210                                                       GTK_TYPE_POSITION_TYPE,
211                                                       GTK_POS_TOP,
212                                                       GTK_PARAM_READWRITE));
213
214   g_object_class_install_property (gobject_class,
215                                    PROP_SNAP_EDGE_SET,
216                                    g_param_spec_boolean ("snap-edge-set",
217                                                          P_("Snap edge set"),
218                                                          P_("Whether to use the value from the snap_edge property or a value derived from handle_position"),
219                                                          FALSE,
220                                                          GTK_PARAM_READWRITE));
221
222   g_object_class_install_property (gobject_class,
223                                    PROP_CHILD_DETACHED,
224                                    g_param_spec_boolean ("child-detached",
225                                                          P_("Child Detached"),
226                                                          P_("A boolean value indicating whether the handlebox's child is attached or detached."),
227                                                          FALSE,
228                                                          GTK_PARAM_READABLE));
229
230   widget_class->map = gtk_handle_box_map;
231   widget_class->unmap = gtk_handle_box_unmap;
232   widget_class->realize = gtk_handle_box_realize;
233   widget_class->unrealize = gtk_handle_box_unrealize;
234   widget_class->style_updated = gtk_handle_box_style_updated;
235   widget_class->get_preferred_width = gtk_handle_box_get_preferred_width;
236   widget_class->get_preferred_height = gtk_handle_box_get_preferred_height;
237   widget_class->size_allocate = gtk_handle_box_size_allocate;
238   widget_class->draw = gtk_handle_box_draw;
239   widget_class->button_press_event = gtk_handle_box_button_press;
240   widget_class->delete_event = gtk_handle_box_delete_event;
241
242   container_class->add = gtk_handle_box_add;
243   container_class->remove = gtk_handle_box_remove;
244
245   class->child_attached = NULL;
246   class->child_detached = NULL;
247
248   handle_box_signals[SIGNAL_CHILD_ATTACHED] =
249     g_signal_new (I_("child-attached"),
250                   G_OBJECT_CLASS_TYPE (gobject_class),
251                   G_SIGNAL_RUN_FIRST,
252                   G_STRUCT_OFFSET (GtkHandleBoxClass, child_attached),
253                   NULL, NULL,
254                   _gtk_marshal_VOID__OBJECT,
255                   G_TYPE_NONE, 1,
256                   GTK_TYPE_WIDGET);
257   handle_box_signals[SIGNAL_CHILD_DETACHED] =
258     g_signal_new (I_("child-detached"),
259                   G_OBJECT_CLASS_TYPE (gobject_class),
260                   G_SIGNAL_RUN_FIRST,
261                   G_STRUCT_OFFSET (GtkHandleBoxClass, child_detached),
262                   NULL, NULL,
263                   _gtk_marshal_VOID__OBJECT,
264                   G_TYPE_NONE, 1,
265                   GTK_TYPE_WIDGET);
266
267   g_type_class_add_private (gobject_class, sizeof (GtkHandleBoxPrivate));
268 }
269
270 static void
271 gtk_handle_box_init (GtkHandleBox *handle_box)
272 {
273   GtkHandleBoxPrivate *priv;
274   GtkStyleContext *context;
275
276   handle_box->priv = G_TYPE_INSTANCE_GET_PRIVATE (handle_box,
277                                                   GTK_TYPE_HANDLE_BOX,
278                                                   GtkHandleBoxPrivate);
279   priv = handle_box->priv;
280
281   gtk_widget_set_has_window (GTK_WIDGET (handle_box), TRUE);
282
283   priv->bin_window = NULL;
284   priv->float_window = NULL;
285   priv->shadow_type = GTK_SHADOW_OUT;
286   priv->handle_position = GTK_POS_LEFT;
287   priv->float_window_mapped = FALSE;
288   priv->child_detached = FALSE;
289   priv->in_drag = FALSE;
290   priv->shrink_on_detach = TRUE;
291   priv->snap_edge = -1;
292
293   context = gtk_widget_get_style_context (GTK_WIDGET (handle_box));
294   gtk_style_context_add_class (context, GTK_STYLE_CLASS_DOCK);
295 }
296
297 static void 
298 gtk_handle_box_set_property (GObject         *object,
299                              guint            prop_id,
300                              const GValue    *value,
301                              GParamSpec      *pspec)
302 {
303   GtkHandleBox *handle_box = GTK_HANDLE_BOX (object);
304
305   switch (prop_id)
306     {
307     case PROP_SHADOW_TYPE:
308       gtk_handle_box_set_shadow_type (handle_box, g_value_get_enum (value));
309       break;
310     case PROP_HANDLE_POSITION:
311       gtk_handle_box_set_handle_position (handle_box, g_value_get_enum (value));
312       break;
313     case PROP_SNAP_EDGE:
314       gtk_handle_box_set_snap_edge (handle_box, g_value_get_enum (value));
315       break;
316     case PROP_SNAP_EDGE_SET:
317       if (!g_value_get_boolean (value))
318         gtk_handle_box_set_snap_edge (handle_box, (GtkPositionType)-1);
319       break;
320     default:
321       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
322       break;
323     }
324 }
325
326 static void 
327 gtk_handle_box_get_property (GObject         *object,
328                              guint            prop_id,
329                              GValue          *value,
330                              GParamSpec      *pspec)
331 {
332   GtkHandleBox *handle_box = GTK_HANDLE_BOX (object);
333   GtkHandleBoxPrivate *priv = handle_box->priv;
334
335   switch (prop_id)
336     {
337     case PROP_SHADOW_TYPE:
338       g_value_set_enum (value, priv->shadow_type);
339       break;
340     case PROP_HANDLE_POSITION:
341       g_value_set_enum (value, priv->handle_position);
342       break;
343     case PROP_SNAP_EDGE:
344       g_value_set_enum (value,
345                         (priv->snap_edge == -1 ?
346                          GTK_POS_TOP : priv->snap_edge));
347       break;
348     case PROP_SNAP_EDGE_SET:
349       g_value_set_boolean (value, priv->snap_edge != -1);
350       break;
351     case PROP_CHILD_DETACHED:
352       g_value_set_boolean (value, priv->child_detached);
353       break;
354     default:
355       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
356       break;
357     }
358 }
359  
360 GtkWidget*
361 gtk_handle_box_new (void)
362 {
363   return g_object_new (GTK_TYPE_HANDLE_BOX, NULL);
364 }
365
366 static void
367 gtk_handle_box_map (GtkWidget *widget)
368 {
369   GtkHandleBox *hb = GTK_HANDLE_BOX (widget);
370   GtkHandleBoxPrivate *priv = hb->priv;
371   GtkBin *bin = GTK_BIN (widget);
372   GtkWidget *child;
373
374   gtk_widget_set_mapped (widget, TRUE);
375
376   child = gtk_bin_get_child (bin);
377   if (child != NULL &&
378       gtk_widget_get_visible (child) &&
379       !gtk_widget_get_mapped (child))
380     gtk_widget_map (child);
381
382   if (priv->child_detached && !priv->float_window_mapped)
383     {
384       gdk_window_show (priv->float_window);
385       priv->float_window_mapped = TRUE;
386     }
387
388   gdk_window_show (priv->bin_window);
389   gdk_window_show (gtk_widget_get_window (widget));
390 }
391
392 static void
393 gtk_handle_box_unmap (GtkWidget *widget)
394 {
395   GtkHandleBox *hb = GTK_HANDLE_BOX (widget);
396   GtkHandleBoxPrivate *priv = hb->priv;
397
398   gtk_widget_set_mapped (widget, FALSE);
399
400   gdk_window_hide (gtk_widget_get_window (widget));
401   if (priv->float_window_mapped)
402     {
403       gdk_window_hide (priv->float_window);
404       priv->float_window_mapped = FALSE;
405     }
406
407   GTK_WIDGET_CLASS (gtk_handle_box_parent_class)->unmap (widget);
408 }
409
410 static void
411 gtk_handle_box_realize (GtkWidget *widget)
412 {
413   GtkHandleBox *hb = GTK_HANDLE_BOX (widget);
414   GtkHandleBoxPrivate *priv = hb->priv;
415   GtkAllocation allocation;
416   GtkRequisition requisition;
417   GtkStateFlags state;
418   GtkStyleContext *context;
419   GtkWidget *child;
420   GdkWindow *window;
421   GdkWindowAttr attributes;
422   gint attributes_mask;
423
424   gtk_widget_set_realized (widget, TRUE);
425
426   gtk_widget_get_allocation (widget, &allocation);
427
428   attributes.x = allocation.x;
429   attributes.y = allocation.y;
430   attributes.width = allocation.width;
431   attributes.height = allocation.height;
432   attributes.window_type = GDK_WINDOW_CHILD;
433   attributes.wclass = GDK_INPUT_OUTPUT;
434   attributes.visual = gtk_widget_get_visual (widget);
435   attributes.event_mask = (gtk_widget_get_events (widget)
436                            | GDK_EXPOSURE_MASK);
437   attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL;
438
439   window = gdk_window_new (gtk_widget_get_parent_window (widget),
440                            &attributes, attributes_mask);
441   gtk_widget_set_window (widget, window);
442   gdk_window_set_user_data (window, widget);
443
444   attributes.x = 0;
445   attributes.y = 0;
446   attributes.width = allocation.width;
447   attributes.height = allocation.height;
448   attributes.window_type = GDK_WINDOW_CHILD;
449   attributes.event_mask = (gtk_widget_get_events (widget) |
450                            GDK_EXPOSURE_MASK |
451                            GDK_BUTTON1_MOTION_MASK |
452                            GDK_POINTER_MOTION_HINT_MASK |
453                            GDK_BUTTON_PRESS_MASK |
454                             GDK_BUTTON_RELEASE_MASK);
455   attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL;
456
457   priv->bin_window = gdk_window_new (window,
458                                      &attributes, attributes_mask);
459   gdk_window_set_user_data (priv->bin_window, widget);
460
461   child = gtk_bin_get_child (GTK_BIN (hb));
462   if (child)
463     gtk_widget_set_parent_window (child, priv->bin_window);
464   
465   gtk_widget_get_preferred_size (widget, &requisition, NULL);
466
467   attributes.x = 0;
468   attributes.y = 0;
469   attributes.width = requisition.width;
470   attributes.height = requisition.height;
471   attributes.window_type = GDK_WINDOW_TOPLEVEL;
472   attributes.wclass = GDK_INPUT_OUTPUT;
473   attributes.visual = gtk_widget_get_visual (widget);
474   attributes.event_mask = (gtk_widget_get_events (widget) |
475                            GDK_KEY_PRESS_MASK |
476                            GDK_ENTER_NOTIFY_MASK |
477                            GDK_LEAVE_NOTIFY_MASK |
478                            GDK_FOCUS_CHANGE_MASK |
479                            GDK_STRUCTURE_MASK);
480   attributes.type_hint = GDK_WINDOW_TYPE_HINT_TOOLBAR;
481   attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_TYPE_HINT;
482   priv->float_window = gdk_window_new (gtk_widget_get_root_window (widget),
483                                      &attributes, attributes_mask);
484   gdk_window_set_user_data (priv->float_window, widget);
485   gdk_window_set_decorations (priv->float_window, 0);
486   gdk_window_set_type_hint (priv->float_window, GDK_WINDOW_TYPE_HINT_TOOLBAR);
487
488   context = gtk_widget_get_style_context (widget);
489   state = gtk_widget_get_state_flags (widget);
490
491   gtk_style_context_set_background (context, window);
492   gtk_style_context_set_background (context, priv->bin_window);
493   gtk_style_context_set_background (context, priv->float_window);
494 }
495
496 static void
497 gtk_handle_box_unrealize (GtkWidget *widget)
498 {
499   GtkHandleBox *hb = GTK_HANDLE_BOX (widget);
500   GtkHandleBoxPrivate *priv = hb->priv;
501
502   gdk_window_set_user_data (priv->bin_window, NULL);
503   gdk_window_destroy (priv->bin_window);
504   priv->bin_window = NULL;
505   gdk_window_set_user_data (priv->float_window, NULL);
506   gdk_window_destroy (priv->float_window);
507   priv->float_window = NULL;
508
509   GTK_WIDGET_CLASS (gtk_handle_box_parent_class)->unrealize (widget);
510 }
511
512 static void
513 gtk_handle_box_style_updated (GtkWidget *widget)
514 {
515   GtkHandleBox *hb = GTK_HANDLE_BOX (widget);
516   GtkHandleBoxPrivate *priv = hb->priv;
517
518   if (gtk_widget_get_realized (widget) &&
519       gtk_widget_get_has_window (widget))
520     {
521       GtkStateFlags state;
522       GtkStyleContext *context;
523
524       context = gtk_widget_get_style_context (widget);
525       state = gtk_widget_get_state_flags (widget);
526
527       gtk_style_context_save (context);
528       gtk_style_context_set_state (context, state);
529
530       gtk_style_context_set_background (context, gtk_widget_get_window (widget));
531       gtk_style_context_set_background (context, priv->bin_window);
532       gtk_style_context_set_background (context, priv->float_window);
533
534       gtk_style_context_restore (context);
535     }
536 }
537
538 static int
539 effective_handle_position (GtkHandleBox *hb)
540 {
541   GtkHandleBoxPrivate *priv = hb->priv;
542   int handle_position;
543
544   if (gtk_widget_get_direction (GTK_WIDGET (hb)) == GTK_TEXT_DIR_LTR)
545     handle_position = priv->handle_position;
546   else
547     {
548       switch (priv->handle_position)
549         {
550         case GTK_POS_LEFT:
551           handle_position = GTK_POS_RIGHT;
552           break;
553         case GTK_POS_RIGHT:
554           handle_position = GTK_POS_LEFT;
555           break;
556         default:
557           handle_position = priv->handle_position;
558           break;
559         }
560     }
561
562   return handle_position;
563 }
564
565 static void
566 gtk_handle_box_size_request (GtkWidget      *widget,
567                              GtkRequisition *requisition)
568 {
569   GtkBin *bin = GTK_BIN (widget);
570   GtkHandleBox *hb = GTK_HANDLE_BOX (widget);
571   GtkHandleBoxPrivate *priv = hb->priv;
572   GtkRequisition child_requisition;
573   GtkWidget *child;
574   gint handle_position;
575
576   handle_position = effective_handle_position (hb);
577
578   if (handle_position == GTK_POS_LEFT ||
579       handle_position == GTK_POS_RIGHT)
580     {
581       requisition->width = DRAG_HANDLE_SIZE;
582       requisition->height = 0;
583     }
584   else
585     {
586       requisition->width = 0;
587       requisition->height = DRAG_HANDLE_SIZE;
588     }
589
590   child = gtk_bin_get_child (bin);
591   /* if our child is not visible, we still request its size, since we
592    * won't have any useful hint for our size otherwise.
593    */
594   if (child)
595     {
596       gtk_widget_get_preferred_size (child, &child_requisition, NULL);
597     }
598   else
599     {
600       child_requisition.width = 0;
601       child_requisition.height = 0;
602     }      
603
604   if (priv->child_detached)
605     {
606       /* FIXME: This doesn't work currently */
607       if (!priv->shrink_on_detach)
608         {
609           if (handle_position == GTK_POS_LEFT ||
610               handle_position == GTK_POS_RIGHT)
611             requisition->height += child_requisition.height;
612           else
613             requisition->width += child_requisition.width;
614         }
615       else
616         {
617           GtkStyleContext *context;
618           GtkStateFlags state;
619           GtkBorder padding;
620
621           context = gtk_widget_get_style_context (widget);
622           state = gtk_widget_get_state_flags (widget);
623           gtk_style_context_get_padding (context, state, &padding);
624
625           if (handle_position == GTK_POS_LEFT ||
626               handle_position == GTK_POS_RIGHT)
627             requisition->height += padding.top;
628           else
629             requisition->width += padding.left;
630         }
631     }
632   else
633     {
634       guint border_width;
635
636       border_width = gtk_container_get_border_width (GTK_CONTAINER (widget));
637       requisition->width += border_width * 2;
638       requisition->height += border_width * 2;
639       
640       if (child)
641         {
642           requisition->width += child_requisition.width;
643           requisition->height += child_requisition.height;
644         }
645       else
646         {
647           requisition->width += CHILDLESS_SIZE;
648           requisition->height += CHILDLESS_SIZE;
649         }
650     }
651 }
652
653 static void
654 gtk_handle_box_get_preferred_width (GtkWidget *widget,
655                                      gint      *minimum,
656                                      gint      *natural)
657 {
658   GtkRequisition requisition;
659
660   gtk_handle_box_size_request (widget, &requisition);
661
662   *minimum = *natural = requisition.width;
663 }
664
665 static void
666 gtk_handle_box_get_preferred_height (GtkWidget *widget,
667                                      gint      *minimum,
668                                      gint      *natural)
669 {
670   GtkRequisition requisition;
671
672   gtk_handle_box_size_request (widget, &requisition);
673
674   *minimum = *natural = requisition.height;
675 }
676
677
678 static void
679 gtk_handle_box_size_allocate (GtkWidget     *widget,
680                               GtkAllocation *allocation)
681 {
682   GtkBin *bin = GTK_BIN (widget);
683   GtkHandleBox *hb = GTK_HANDLE_BOX (widget);
684   GtkHandleBoxPrivate *priv = hb->priv;
685   GtkRequisition child_requisition;
686   GtkWidget *child;
687   gint handle_position;
688
689   handle_position = effective_handle_position (hb);
690
691   child = gtk_bin_get_child (bin);
692
693   if (child)
694     {
695       gtk_widget_get_preferred_size (child, &child_requisition, NULL);
696     }
697   else
698     {
699       child_requisition.width = 0;
700       child_requisition.height = 0;
701     }
702
703   gtk_widget_set_allocation (widget, allocation);
704
705   if (gtk_widget_get_realized (widget))
706     gdk_window_move_resize (gtk_widget_get_window (widget),
707                             allocation->x, allocation->y,
708                             allocation->width, allocation->height);
709
710   if (child != NULL && gtk_widget_get_visible (child))
711     {
712       GtkAllocation child_allocation;
713       guint border_width;
714
715       border_width = gtk_container_get_border_width (GTK_CONTAINER (widget));
716
717       child_allocation.x = border_width;
718       child_allocation.y = border_width;
719       if (handle_position == GTK_POS_LEFT)
720         child_allocation.x += DRAG_HANDLE_SIZE;
721       else if (handle_position == GTK_POS_TOP)
722         child_allocation.y += DRAG_HANDLE_SIZE;
723
724       if (priv->child_detached)
725         {
726           guint float_width;
727           guint float_height;
728           
729           child_allocation.width = child_requisition.width;
730           child_allocation.height = child_requisition.height;
731           
732           float_width = child_allocation.width + 2 * border_width;
733           float_height = child_allocation.height + 2 * border_width;
734           
735           if (handle_position == GTK_POS_LEFT ||
736               handle_position == GTK_POS_RIGHT)
737             float_width += DRAG_HANDLE_SIZE;
738           else
739             float_height += DRAG_HANDLE_SIZE;
740
741           if (gtk_widget_get_realized (widget))
742             {
743               gdk_window_resize (priv->float_window,
744                                  float_width,
745                                  float_height);
746               gdk_window_move_resize (priv->bin_window,
747                                       0,
748                                       0,
749                                       float_width,
750                                       float_height);
751             }
752         }
753       else
754         {
755           child_allocation.width = MAX (1, (gint) allocation->width - 2 * border_width);
756           child_allocation.height = MAX (1, (gint) allocation->height - 2 * border_width);
757
758           if (handle_position == GTK_POS_LEFT ||
759               handle_position == GTK_POS_RIGHT)
760             child_allocation.width -= DRAG_HANDLE_SIZE;
761           else
762             child_allocation.height -= DRAG_HANDLE_SIZE;
763           
764           if (gtk_widget_get_realized (widget))
765             gdk_window_move_resize (priv->bin_window,
766                                     0,
767                                     0,
768                                     allocation->width,
769                                     allocation->height);
770         }
771
772       gtk_widget_size_allocate (child, &child_allocation);
773     }
774 }
775
776 static void
777 gtk_handle_box_draw_ghost (GtkHandleBox *hb,
778                            cairo_t      *cr)
779 {
780   GtkWidget *widget = GTK_WIDGET (hb);
781   GtkStateFlags state;
782   GtkStyleContext *context;
783   GdkWindow *window;
784   guint x;
785   guint y;
786   guint width;
787   guint height;
788   gint allocation_width;
789   gint allocation_height;
790   gint handle_position;
791
792   handle_position = effective_handle_position (hb);
793   allocation_width = gtk_widget_get_allocated_width (widget);
794   allocation_height = gtk_widget_get_allocated_height (widget);
795
796   if (handle_position == GTK_POS_LEFT ||
797       handle_position == GTK_POS_RIGHT)
798     {
799       x = handle_position == GTK_POS_LEFT ? 0 : allocation_width - DRAG_HANDLE_SIZE;
800       y = 0;
801       width = DRAG_HANDLE_SIZE;
802       height = allocation_height;
803     }
804   else
805     {
806       x = 0;
807       y = handle_position == GTK_POS_TOP ? 0 : allocation_height - DRAG_HANDLE_SIZE;
808       width = allocation_width;
809       height = DRAG_HANDLE_SIZE;
810     }
811
812   context = gtk_widget_get_style_context (widget);
813   window = gtk_widget_get_window (widget);
814   state = gtk_widget_get_state_flags (widget);
815
816   gtk_style_context_save (context);
817   gtk_style_context_set_state (context, state);
818
819   gtk_render_background (context, cr, x, y, width, height);
820   gtk_render_frame (context, cr, x, y, width, height);
821
822   if (handle_position == GTK_POS_LEFT ||
823       handle_position == GTK_POS_RIGHT)
824     gtk_render_line (context, cr,
825                      handle_position == GTK_POS_LEFT ? DRAG_HANDLE_SIZE : 0,
826                      allocation_height / 2,
827                      handle_position == GTK_POS_LEFT ? allocation_width : allocation_width - DRAG_HANDLE_SIZE,
828                      allocation_height / 2);
829   else
830     gtk_render_line (context, cr,
831                      allocation_width / 2,
832                      handle_position == GTK_POS_TOP ? DRAG_HANDLE_SIZE : 0,
833                      allocation_width / 2,
834                      handle_position == GTK_POS_TOP ? allocation_height : allocation_height - DRAG_HANDLE_SIZE);
835
836   gtk_style_context_restore (context);
837 }
838
839 void
840 gtk_handle_box_set_shadow_type (GtkHandleBox  *handle_box,
841                                 GtkShadowType  type)
842 {
843   GtkHandleBoxPrivate *priv;
844
845   g_return_if_fail (GTK_IS_HANDLE_BOX (handle_box));
846
847   priv = handle_box->priv;
848
849   if ((GtkShadowType) priv->shadow_type != type)
850     {
851       priv->shadow_type = type;
852       g_object_notify (G_OBJECT (handle_box), "shadow-type");
853       gtk_widget_queue_resize (GTK_WIDGET (handle_box));
854     }
855 }
856
857 /**
858  * gtk_handle_box_get_shadow_type:
859  * @handle_box: a #GtkHandleBox
860  * 
861  * Gets the type of shadow drawn around the handle box. See
862  * gtk_handle_box_set_shadow_type().
863  *
864  * Return value: the type of shadow currently drawn around the handle box.
865  **/
866 GtkShadowType
867 gtk_handle_box_get_shadow_type (GtkHandleBox *handle_box)
868 {
869   g_return_val_if_fail (GTK_IS_HANDLE_BOX (handle_box), GTK_SHADOW_ETCHED_OUT);
870
871   return handle_box->priv->shadow_type;
872 }
873
874 void        
875 gtk_handle_box_set_handle_position  (GtkHandleBox    *handle_box,
876                                      GtkPositionType  position)
877 {
878   GtkHandleBoxPrivate *priv;
879
880   g_return_if_fail (GTK_IS_HANDLE_BOX (handle_box));
881
882   priv = handle_box->priv;
883
884   if ((GtkPositionType) priv->handle_position != position)
885     {
886       priv->handle_position = position;
887       g_object_notify (G_OBJECT (handle_box), "handle-position");
888       gtk_widget_queue_resize (GTK_WIDGET (handle_box));
889     }
890 }
891
892 /**
893  * gtk_handle_box_get_handle_position:
894  * @handle_box: a #GtkHandleBox
895  *
896  * Gets the handle position of the handle box. See
897  * gtk_handle_box_set_handle_position().
898  *
899  * Return value: the current handle position.
900  **/
901 GtkPositionType
902 gtk_handle_box_get_handle_position (GtkHandleBox *handle_box)
903 {
904   g_return_val_if_fail (GTK_IS_HANDLE_BOX (handle_box), GTK_POS_LEFT);
905
906   return handle_box->priv->handle_position;
907 }
908
909 void        
910 gtk_handle_box_set_snap_edge        (GtkHandleBox    *handle_box,
911                                      GtkPositionType  edge)
912 {
913   GtkHandleBoxPrivate *priv;
914
915   g_return_if_fail (GTK_IS_HANDLE_BOX (handle_box));
916
917   priv = handle_box->priv;
918
919   if (priv->snap_edge != edge)
920     {
921       priv->snap_edge = edge;
922
923       g_object_freeze_notify (G_OBJECT (handle_box));
924       g_object_notify (G_OBJECT (handle_box), "snap-edge");
925       g_object_notify (G_OBJECT (handle_box), "snap-edge-set");
926       g_object_thaw_notify (G_OBJECT (handle_box));
927     }
928 }
929
930 /**
931  * gtk_handle_box_get_snap_edge:
932  * @handle_box: a #GtkHandleBox
933  * 
934  * Gets the edge used for determining reattachment of the handle box. See
935  * gtk_handle_box_set_snap_edge().
936  *
937  * Return value: the edge used for determining reattachment, or (GtkPositionType)-1 if this
938  *               is determined (as per default) from the handle position. 
939  **/
940 GtkPositionType
941 gtk_handle_box_get_snap_edge (GtkHandleBox *handle_box)
942 {
943   g_return_val_if_fail (GTK_IS_HANDLE_BOX (handle_box), (GtkPositionType)-1);
944
945   return handle_box->priv->snap_edge;
946 }
947
948 /**
949  * gtk_handle_box_get_child_detached:
950  * @handle_box: a #GtkHandleBox
951  *
952  * Whether the handlebox's child is currently detached.
953  *
954  * Return value: %TRUE if the child is currently detached, otherwise %FALSE
955  *
956  * Since: 2.14
957  **/
958 gboolean
959 gtk_handle_box_get_child_detached (GtkHandleBox *handle_box)
960 {
961   g_return_val_if_fail (GTK_IS_HANDLE_BOX (handle_box), FALSE);
962
963   return handle_box->priv->child_detached;
964 }
965
966 static void
967 gtk_handle_box_paint (GtkWidget      *widget,
968                       cairo_t        *cr)
969 {
970   GtkHandleBox *hb = GTK_HANDLE_BOX (widget);
971   GtkHandleBoxPrivate *priv = hb->priv;
972   GtkBin *bin = GTK_BIN (widget);
973   GtkStyleContext *context;
974   GtkStateFlags state;
975   GtkWidget *child;
976   gint width, height;
977   GdkRectangle rect;
978   gint handle_position;
979   GtkOrientation handle_orientation;
980
981   handle_position = effective_handle_position (hb);
982
983   width = gdk_window_get_width (priv->bin_window);
984   height = gdk_window_get_height (priv->bin_window);
985
986   context = gtk_widget_get_style_context (widget);
987   state = gtk_widget_get_state_flags (widget);
988
989   gtk_style_context_save (context);
990   gtk_style_context_set_state (context, state);
991
992   gtk_render_background (context, cr, 0, 0, width, height);
993   gtk_render_frame (context, cr, 0, 0, width, height);
994
995   switch (handle_position)
996     {
997     case GTK_POS_LEFT:
998       rect.x = 0;
999       rect.y = 0; 
1000       rect.width = DRAG_HANDLE_SIZE;
1001       rect.height = height;
1002       handle_orientation = GTK_ORIENTATION_VERTICAL;
1003       break;
1004     case GTK_POS_RIGHT:
1005       rect.x = width - DRAG_HANDLE_SIZE; 
1006       rect.y = 0;
1007       rect.width = DRAG_HANDLE_SIZE;
1008       rect.height = height;
1009       handle_orientation = GTK_ORIENTATION_VERTICAL;
1010       break;
1011     case GTK_POS_TOP:
1012       rect.x = 0;
1013       rect.y = 0; 
1014       rect.width = width;
1015       rect.height = DRAG_HANDLE_SIZE;
1016       handle_orientation = GTK_ORIENTATION_HORIZONTAL;
1017       break;
1018     case GTK_POS_BOTTOM:
1019       rect.x = 0;
1020       rect.y = height - DRAG_HANDLE_SIZE;
1021       rect.width = width;
1022       rect.height = DRAG_HANDLE_SIZE;
1023       handle_orientation = GTK_ORIENTATION_HORIZONTAL;
1024       break;
1025     default: 
1026       g_assert_not_reached ();
1027       break;
1028     }
1029
1030   gtk_render_handle (context, cr,
1031                      rect.x, rect.y, rect.width, rect.height);
1032
1033   child = gtk_bin_get_child (bin);
1034   if (child != NULL && gtk_widget_get_visible (child))
1035     GTK_WIDGET_CLASS (gtk_handle_box_parent_class)->draw (widget, cr);
1036
1037   gtk_style_context_restore (context);
1038 }
1039
1040 static gboolean
1041 gtk_handle_box_draw (GtkWidget *widget,
1042                      cairo_t   *cr)
1043 {
1044   GtkHandleBox *hb = GTK_HANDLE_BOX (widget);
1045   GtkHandleBoxPrivate *priv = hb->priv;
1046
1047   if (gtk_cairo_should_draw_window (cr, gtk_widget_get_window (widget)))
1048     {
1049       if (priv->child_detached)
1050         gtk_handle_box_draw_ghost (hb, cr);
1051     }
1052   else if (gtk_cairo_should_draw_window (cr, priv->bin_window))
1053     gtk_handle_box_paint (widget, cr);
1054   
1055   return FALSE;
1056 }
1057
1058 static GtkWidget *
1059 gtk_handle_box_get_invisible (void)
1060 {
1061   static GtkWidget *handle_box_invisible = NULL;
1062
1063   if (!handle_box_invisible)
1064     {
1065       handle_box_invisible = gtk_invisible_new ();
1066       gtk_widget_show (handle_box_invisible);
1067     }
1068   
1069   return handle_box_invisible;
1070 }
1071
1072 static gboolean
1073 gtk_handle_box_grab_event (GtkWidget    *widget,
1074                            GdkEvent     *event,
1075                            GtkHandleBox *hb)
1076 {
1077   GtkHandleBoxPrivate *priv = hb->priv;
1078
1079   switch (event->type)
1080     {
1081     case GDK_BUTTON_RELEASE:
1082       if (priv->in_drag)                /* sanity check */
1083         {
1084           gtk_handle_box_end_drag (hb, event->button.time);
1085           return TRUE;
1086         }
1087       break;
1088
1089     case GDK_MOTION_NOTIFY:
1090       return gtk_handle_box_motion (GTK_WIDGET (hb), (GdkEventMotion *)event);
1091       break;
1092
1093     default:
1094       break;
1095     }
1096
1097   return FALSE;
1098 }
1099
1100 static gboolean
1101 gtk_handle_box_button_press (GtkWidget      *widget,
1102                              GdkEventButton *event)
1103 {
1104   GtkHandleBox *hb = GTK_HANDLE_BOX (widget);
1105   GtkHandleBoxPrivate *priv = hb->priv;
1106   gboolean event_handled;
1107   GdkCursor *fleur;
1108   gint handle_position;
1109
1110   handle_position = effective_handle_position (hb);
1111
1112   event_handled = FALSE;
1113   if ((event->button == 1) && 
1114       (event->type == GDK_BUTTON_PRESS || event->type == GDK_2BUTTON_PRESS))
1115     {
1116       GtkWidget *child;
1117       gboolean in_handle;
1118       
1119       if (event->window != priv->bin_window)
1120         return FALSE;
1121
1122       child = gtk_bin_get_child (GTK_BIN (hb));
1123
1124       if (child)
1125         {
1126           GtkAllocation child_allocation;
1127           guint border_width;
1128
1129           gtk_widget_get_allocation (child, &child_allocation);
1130           border_width = gtk_container_get_border_width (GTK_CONTAINER (hb));
1131
1132           switch (handle_position)
1133             {
1134             case GTK_POS_LEFT:
1135               in_handle = event->x < DRAG_HANDLE_SIZE;
1136               break;
1137             case GTK_POS_TOP:
1138               in_handle = event->y < DRAG_HANDLE_SIZE;
1139               break;
1140             case GTK_POS_RIGHT:
1141               in_handle = event->x > 2 * border_width + child_allocation.width;
1142               break;
1143             case GTK_POS_BOTTOM:
1144               in_handle = event->y > 2 * border_width + child_allocation.height;
1145               break;
1146             default:
1147               in_handle = FALSE;
1148               break;
1149             }
1150         }
1151       else
1152         {
1153           in_handle = FALSE;
1154           event_handled = TRUE;
1155         }
1156       
1157       if (in_handle)
1158         {
1159           if (event->type == GDK_BUTTON_PRESS) /* Start a drag */
1160             {
1161               GtkWidget *invisible = gtk_handle_box_get_invisible ();
1162               GdkWindow *window;
1163               gint root_x, root_y;
1164
1165               gtk_invisible_set_screen (GTK_INVISIBLE (invisible),
1166                                         gtk_widget_get_screen (GTK_WIDGET (hb)));
1167               gdk_window_get_origin (priv->bin_window, &root_x, &root_y);
1168
1169               priv->orig_x = event->x_root;
1170               priv->orig_y = event->y_root;
1171
1172               priv->float_allocation.x = root_x - event->x_root;
1173               priv->float_allocation.y = root_y - event->y_root;
1174               priv->float_allocation.width = gdk_window_get_width (priv->bin_window);
1175               priv->float_allocation.height = gdk_window_get_height (priv->bin_window);
1176
1177               window = gtk_widget_get_window (widget);
1178               if (gdk_window_is_viewable (window))
1179                 {
1180                   gdk_window_get_origin (window, &root_x, &root_y);
1181
1182                   priv->attach_allocation.x = root_x;
1183                   priv->attach_allocation.y = root_y;
1184                   priv->attach_allocation.width = gdk_window_get_width (window);
1185                   priv->attach_allocation.height = gdk_window_get_height (window);
1186                 }
1187               else
1188                 {
1189                   priv->attach_allocation.x = -1;
1190                   priv->attach_allocation.y = -1;
1191                   priv->attach_allocation.width = 0;
1192                   priv->attach_allocation.height = 0;
1193                 }
1194               priv->in_drag = TRUE;
1195               priv->grab_device = event->device;
1196               fleur = gdk_cursor_new_for_display (gtk_widget_get_display (widget),
1197                                                   GDK_FLEUR);
1198               if (gdk_device_grab (event->device,
1199                                    gtk_widget_get_window (invisible),
1200                                    GDK_OWNERSHIP_WINDOW,
1201                                    FALSE,
1202                                    (GDK_BUTTON1_MOTION_MASK |
1203                                     GDK_POINTER_MOTION_HINT_MASK |
1204                                     GDK_BUTTON_RELEASE_MASK),
1205                                    fleur,
1206                                    event->time) != GDK_GRAB_SUCCESS)
1207                 {
1208                   priv->in_drag = FALSE;
1209                   priv->grab_device = NULL;
1210                 }
1211               else
1212                 {
1213                   gtk_device_grab_add (invisible, priv->grab_device, TRUE);
1214                   g_signal_connect (invisible, "event",
1215                                     G_CALLBACK (gtk_handle_box_grab_event), hb);
1216                 }
1217
1218               g_object_unref (fleur);
1219               event_handled = TRUE;
1220             }
1221           else if (priv->child_detached) /* Double click */
1222             {
1223               gtk_handle_box_reattach (hb);
1224             }
1225         }
1226     }
1227   
1228   return event_handled;
1229 }
1230
1231 static gboolean
1232 gtk_handle_box_motion (GtkWidget      *widget,
1233                        GdkEventMotion *event)
1234 {
1235   GtkHandleBox *hb = GTK_HANDLE_BOX (widget);
1236   GtkHandleBoxPrivate *priv = hb->priv;
1237   GtkWidget *child;
1238   gint new_x, new_y;
1239   gint snap_edge;
1240   gboolean is_snapped = FALSE;
1241   gint handle_position;
1242   GdkGeometry geometry;
1243   GdkScreen *screen, *pointer_screen;
1244
1245   if (!priv->in_drag)
1246     return FALSE;
1247   handle_position = effective_handle_position (hb);
1248
1249   /* Calculate the attachment point on the float, if the float
1250    * were detached
1251    */
1252   new_x = 0;
1253   new_y = 0;
1254   screen = gtk_widget_get_screen (widget);
1255   gdk_device_get_position (event->device,
1256                            &pointer_screen,
1257                            &new_x, &new_y);
1258   if (pointer_screen != screen)
1259     {
1260       new_x = priv->orig_x;
1261       new_y = priv->orig_y;
1262     }
1263
1264   new_x += priv->float_allocation.x;
1265   new_y += priv->float_allocation.y;
1266
1267   snap_edge = priv->snap_edge;
1268   if (snap_edge == -1)
1269     snap_edge = (handle_position == GTK_POS_LEFT ||
1270                  handle_position == GTK_POS_RIGHT) ?
1271       GTK_POS_TOP : GTK_POS_LEFT;
1272
1273   if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL) 
1274     switch (snap_edge) 
1275       {
1276       case GTK_POS_LEFT:
1277         snap_edge = GTK_POS_RIGHT;
1278         break;
1279       case GTK_POS_RIGHT:
1280         snap_edge = GTK_POS_LEFT;
1281         break;
1282       default:
1283         break;
1284       }
1285
1286   /* First, check if the snapped edge is aligned
1287    */
1288   switch (snap_edge)
1289     {
1290     case GTK_POS_TOP:
1291       is_snapped = abs (priv->attach_allocation.y - new_y) < TOLERANCE;
1292       break;
1293     case GTK_POS_BOTTOM:
1294       is_snapped = abs (priv->attach_allocation.y + (gint)priv->attach_allocation.height -
1295                         new_y - (gint)priv->float_allocation.height) < TOLERANCE;
1296       break;
1297     case GTK_POS_LEFT:
1298       is_snapped = abs (priv->attach_allocation.x - new_x) < TOLERANCE;
1299       break;
1300     case GTK_POS_RIGHT:
1301       is_snapped = abs (priv->attach_allocation.x + (gint)priv->attach_allocation.width -
1302                         new_x - (gint)priv->float_allocation.width) < TOLERANCE;
1303       break;
1304     }
1305
1306   /* Next, check if coordinates in the other direction are sufficiently
1307    * aligned
1308    */
1309   if (is_snapped)
1310     {
1311       gint float_pos1 = 0;      /* Initialize to suppress warnings */
1312       gint float_pos2 = 0;
1313       gint attach_pos1 = 0;
1314       gint attach_pos2 = 0;
1315       
1316       switch (snap_edge)
1317         {
1318         case GTK_POS_TOP:
1319         case GTK_POS_BOTTOM:
1320           attach_pos1 = priv->attach_allocation.x;
1321           attach_pos2 = priv->attach_allocation.x + priv->attach_allocation.width;
1322           float_pos1 = new_x;
1323           float_pos2 = new_x + priv->float_allocation.width;
1324           break;
1325         case GTK_POS_LEFT:
1326         case GTK_POS_RIGHT:
1327           attach_pos1 = priv->attach_allocation.y;
1328           attach_pos2 = priv->attach_allocation.y + priv->attach_allocation.height;
1329           float_pos1 = new_y;
1330           float_pos2 = new_y + priv->float_allocation.height;
1331           break;
1332         }
1333
1334       is_snapped = ((attach_pos1 - TOLERANCE < float_pos1) && 
1335                     (attach_pos2 + TOLERANCE > float_pos2)) ||
1336                    ((float_pos1 - TOLERANCE < attach_pos1) &&
1337                     (float_pos2 + TOLERANCE > attach_pos2));
1338     }
1339
1340   child = gtk_bin_get_child (GTK_BIN (hb));
1341
1342   if (is_snapped)
1343     {
1344       if (priv->child_detached)
1345         {
1346           priv->child_detached = FALSE;
1347           gdk_window_hide (priv->float_window);
1348           gdk_window_reparent (priv->bin_window, gtk_widget_get_window (widget), 0, 0);
1349           priv->float_window_mapped = FALSE;
1350           g_signal_emit (hb,
1351                          handle_box_signals[SIGNAL_CHILD_ATTACHED],
1352                          0,
1353                          child);
1354           
1355           gtk_widget_queue_resize (widget);
1356         }
1357     }
1358   else
1359     {
1360       gint width, height;
1361
1362       width = gdk_window_get_width (priv->float_window);
1363       height = gdk_window_get_height (priv->float_window);
1364
1365       switch (handle_position)
1366         {
1367         case GTK_POS_LEFT:
1368           new_y += ((gint)priv->float_allocation.height - height) / 2;
1369           break;
1370         case GTK_POS_RIGHT:
1371           new_x += (gint)priv->float_allocation.width - width;
1372           new_y += ((gint)priv->float_allocation.height - height) / 2;
1373           break;
1374         case GTK_POS_TOP:
1375           new_x += ((gint)priv->float_allocation.width - width) / 2;
1376           break;
1377         case GTK_POS_BOTTOM:
1378           new_x += ((gint)priv->float_allocation.width - width) / 2;
1379           new_y += (gint)priv->float_allocation.height - height;
1380           break;
1381         }
1382
1383       if (priv->child_detached)
1384         {
1385           gdk_window_move (priv->float_window, new_x, new_y);
1386           gdk_window_raise (priv->float_window);
1387         }
1388       else
1389         {
1390           guint border_width;
1391           GtkRequisition child_requisition;
1392
1393           priv->child_detached = TRUE;
1394
1395           if (child)
1396             {
1397               gtk_widget_get_preferred_size (child, &child_requisition, NULL);
1398             }
1399           else
1400             {
1401               child_requisition.width = 0;
1402               child_requisition.height = 0;
1403             }      
1404
1405           border_width = gtk_container_get_border_width (GTK_CONTAINER (hb));
1406           width = child_requisition.width + 2 * border_width;
1407           height = child_requisition.height + 2 * border_width;
1408
1409           if (handle_position == GTK_POS_LEFT || handle_position == GTK_POS_RIGHT)
1410             width += DRAG_HANDLE_SIZE;
1411           else
1412             height += DRAG_HANDLE_SIZE;
1413           
1414           gdk_window_move_resize (priv->float_window, new_x, new_y, width, height);
1415           gdk_window_reparent (priv->bin_window, priv->float_window, 0, 0);
1416           gdk_window_set_geometry_hints (priv->float_window, &geometry, GDK_HINT_POS);
1417           gdk_window_show (priv->float_window);
1418           priv->float_window_mapped = TRUE;
1419 #if     0
1420           /* this extra move is necessary if we use decorations, or our
1421            * window manager insists on decorations.
1422            */
1423           gdk_display_sync (gtk_widget_get_display (widget));
1424           gdk_window_move (priv->float_window, new_x, new_y);
1425           gdk_display_sync (gtk_widget_get_display (widget));
1426 #endif  /* 0 */
1427           g_signal_emit (hb,
1428                          handle_box_signals[SIGNAL_CHILD_DETACHED],
1429                          0,
1430                          child);
1431           
1432           gtk_widget_queue_resize (widget);
1433         }
1434     }
1435
1436   return TRUE;
1437 }
1438
1439 static void
1440 gtk_handle_box_add (GtkContainer *container,
1441                     GtkWidget    *widget)
1442 {
1443   GtkHandleBoxPrivate *priv = GTK_HANDLE_BOX (container)->priv;
1444
1445   gtk_widget_set_parent_window (widget, priv->bin_window);
1446   GTK_CONTAINER_CLASS (gtk_handle_box_parent_class)->add (container, widget);
1447 }
1448
1449 static void
1450 gtk_handle_box_remove (GtkContainer *container,
1451                        GtkWidget    *widget)
1452 {
1453   GTK_CONTAINER_CLASS (gtk_handle_box_parent_class)->remove (container, widget);
1454
1455   gtk_handle_box_reattach (GTK_HANDLE_BOX (container));
1456 }
1457
1458 static gint
1459 gtk_handle_box_delete_event (GtkWidget *widget,
1460                              GdkEventAny  *event)
1461 {
1462   GtkHandleBox *hb = GTK_HANDLE_BOX (widget);
1463   GtkHandleBoxPrivate *priv = hb->priv;
1464
1465   if (event->window == priv->float_window)
1466     {
1467       gtk_handle_box_reattach (hb);
1468       
1469       return TRUE;
1470     }
1471
1472   return FALSE;
1473 }
1474
1475 static void
1476 gtk_handle_box_reattach (GtkHandleBox *hb)
1477 {
1478   GtkHandleBoxPrivate *priv = hb->priv;
1479   GtkWidget *child;
1480   GtkWidget *widget = GTK_WIDGET (hb);
1481   
1482   if (priv->child_detached)
1483     {
1484       priv->child_detached = FALSE;
1485       if (gtk_widget_get_realized (widget))
1486         {
1487           gdk_window_hide (priv->float_window);
1488           gdk_window_reparent (priv->bin_window, gtk_widget_get_window (widget),
1489                                0, 0);
1490
1491           child = gtk_bin_get_child (GTK_BIN (hb));
1492           if (child)
1493             g_signal_emit (hb,
1494                            handle_box_signals[SIGNAL_CHILD_ATTACHED],
1495                            0,
1496                            child);
1497
1498         }
1499       priv->float_window_mapped = FALSE;
1500     }
1501   if (priv->in_drag)
1502     gtk_handle_box_end_drag (hb, GDK_CURRENT_TIME);
1503
1504   gtk_widget_queue_resize (GTK_WIDGET (hb));
1505 }
1506
1507 static void
1508 gtk_handle_box_end_drag (GtkHandleBox *hb,
1509                          guint32       time)
1510 {
1511   GtkHandleBoxPrivate *priv = hb->priv;
1512   GtkWidget *invisible = gtk_handle_box_get_invisible ();
1513
1514   priv->in_drag = FALSE;
1515
1516   gtk_device_grab_remove (invisible, priv->grab_device);
1517   gdk_device_ungrab (priv->grab_device, time);
1518   g_signal_handlers_disconnect_by_func (invisible,
1519                                         G_CALLBACK (gtk_handle_box_grab_event),
1520                                         hb);
1521
1522   priv->grab_device = NULL;
1523 }