]> Pileus Git - ~andy/gtk/blob - gtk/gtkhandlebox.c
Silence new gcc warnings
[~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   GtkStyleContext *context;
418   GtkWidget *child;
419   GdkWindow *window;
420   GdkWindowAttr attributes;
421   gint attributes_mask;
422
423   gtk_widget_set_realized (widget, TRUE);
424
425   gtk_widget_get_allocation (widget, &allocation);
426
427   attributes.x = allocation.x;
428   attributes.y = allocation.y;
429   attributes.width = allocation.width;
430   attributes.height = allocation.height;
431   attributes.window_type = GDK_WINDOW_CHILD;
432   attributes.wclass = GDK_INPUT_OUTPUT;
433   attributes.visual = gtk_widget_get_visual (widget);
434   attributes.event_mask = (gtk_widget_get_events (widget)
435                            | GDK_EXPOSURE_MASK);
436   attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL;
437
438   window = gdk_window_new (gtk_widget_get_parent_window (widget),
439                            &attributes, attributes_mask);
440   gtk_widget_set_window (widget, window);
441   gdk_window_set_user_data (window, widget);
442
443   attributes.x = 0;
444   attributes.y = 0;
445   attributes.width = allocation.width;
446   attributes.height = allocation.height;
447   attributes.window_type = GDK_WINDOW_CHILD;
448   attributes.event_mask = (gtk_widget_get_events (widget)
449                            | GDK_EXPOSURE_MASK
450                            | GDK_BUTTON1_MOTION_MASK
451                            | GDK_POINTER_MOTION_HINT_MASK
452                            | GDK_BUTTON_PRESS_MASK
453                            | GDK_BUTTON_RELEASE_MASK);
454   attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL;
455
456   priv->bin_window = gdk_window_new (window,
457                                      &attributes, attributes_mask);
458   gdk_window_set_user_data (priv->bin_window, widget);
459
460   child = gtk_bin_get_child (GTK_BIN (hb));
461   if (child)
462     gtk_widget_set_parent_window (child, priv->bin_window);
463
464   gtk_widget_get_preferred_size (widget, &requisition, NULL);
465
466   attributes.x = 0;
467   attributes.y = 0;
468   attributes.width = requisition.width;
469   attributes.height = requisition.height;
470   attributes.window_type = GDK_WINDOW_TOPLEVEL;
471   attributes.wclass = GDK_INPUT_OUTPUT;
472   attributes.visual = gtk_widget_get_visual (widget);
473   attributes.event_mask = (gtk_widget_get_events (widget)
474                            | GDK_KEY_PRESS_MASK
475                            | GDK_ENTER_NOTIFY_MASK
476                            | GDK_LEAVE_NOTIFY_MASK
477                            | GDK_FOCUS_CHANGE_MASK
478                            | GDK_STRUCTURE_MASK);
479   attributes.type_hint = GDK_WINDOW_TYPE_HINT_TOOLBAR;
480   attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_TYPE_HINT;
481   priv->float_window = gdk_window_new (gtk_widget_get_root_window (widget),
482                                        &attributes, attributes_mask);
483   gdk_window_set_user_data (priv->float_window, widget);
484   gdk_window_set_decorations (priv->float_window, 0);
485   gdk_window_set_type_hint (priv->float_window, GDK_WINDOW_TYPE_HINT_TOOLBAR);
486
487   context = gtk_widget_get_style_context (widget);
488   gtk_style_context_set_background (context, window);
489   gtk_style_context_set_background (context, priv->bin_window);
490   gtk_style_context_set_background (context, priv->float_window);
491 }
492
493 static void
494 gtk_handle_box_unrealize (GtkWidget *widget)
495 {
496   GtkHandleBox *hb = GTK_HANDLE_BOX (widget);
497   GtkHandleBoxPrivate *priv = hb->priv;
498
499   gdk_window_set_user_data (priv->bin_window, NULL);
500   gdk_window_destroy (priv->bin_window);
501   priv->bin_window = NULL;
502   gdk_window_set_user_data (priv->float_window, NULL);
503   gdk_window_destroy (priv->float_window);
504   priv->float_window = NULL;
505
506   GTK_WIDGET_CLASS (gtk_handle_box_parent_class)->unrealize (widget);
507 }
508
509 static void
510 gtk_handle_box_style_updated (GtkWidget *widget)
511 {
512   GtkHandleBox *hb = GTK_HANDLE_BOX (widget);
513   GtkHandleBoxPrivate *priv = hb->priv;
514
515   if (gtk_widget_get_realized (widget) &&
516       gtk_widget_get_has_window (widget))
517     {
518       GtkStateFlags state;
519       GtkStyleContext *context;
520
521       context = gtk_widget_get_style_context (widget);
522       state = gtk_widget_get_state_flags (widget);
523
524       gtk_style_context_save (context);
525       gtk_style_context_set_state (context, state);
526
527       gtk_style_context_set_background (context, gtk_widget_get_window (widget));
528       gtk_style_context_set_background (context, priv->bin_window);
529       gtk_style_context_set_background (context, priv->float_window);
530
531       gtk_style_context_restore (context);
532     }
533 }
534
535 static int
536 effective_handle_position (GtkHandleBox *hb)
537 {
538   GtkHandleBoxPrivate *priv = hb->priv;
539   int handle_position;
540
541   if (gtk_widget_get_direction (GTK_WIDGET (hb)) == GTK_TEXT_DIR_LTR)
542     handle_position = priv->handle_position;
543   else
544     {
545       switch (priv->handle_position)
546         {
547         case GTK_POS_LEFT:
548           handle_position = GTK_POS_RIGHT;
549           break;
550         case GTK_POS_RIGHT:
551           handle_position = GTK_POS_LEFT;
552           break;
553         default:
554           handle_position = priv->handle_position;
555           break;
556         }
557     }
558
559   return handle_position;
560 }
561
562 static void
563 gtk_handle_box_size_request (GtkWidget      *widget,
564                              GtkRequisition *requisition)
565 {
566   GtkBin *bin = GTK_BIN (widget);
567   GtkHandleBox *hb = GTK_HANDLE_BOX (widget);
568   GtkHandleBoxPrivate *priv = hb->priv;
569   GtkRequisition child_requisition;
570   GtkWidget *child;
571   gint handle_position;
572
573   handle_position = effective_handle_position (hb);
574
575   if (handle_position == GTK_POS_LEFT ||
576       handle_position == GTK_POS_RIGHT)
577     {
578       requisition->width = DRAG_HANDLE_SIZE;
579       requisition->height = 0;
580     }
581   else
582     {
583       requisition->width = 0;
584       requisition->height = DRAG_HANDLE_SIZE;
585     }
586
587   child = gtk_bin_get_child (bin);
588   /* if our child is not visible, we still request its size, since we
589    * won't have any useful hint for our size otherwise.
590    */
591   if (child)
592     {
593       gtk_widget_get_preferred_size (child, &child_requisition, NULL);
594     }
595   else
596     {
597       child_requisition.width = 0;
598       child_requisition.height = 0;
599     }      
600
601   if (priv->child_detached)
602     {
603       /* FIXME: This doesn't work currently */
604       if (!priv->shrink_on_detach)
605         {
606           if (handle_position == GTK_POS_LEFT ||
607               handle_position == GTK_POS_RIGHT)
608             requisition->height += child_requisition.height;
609           else
610             requisition->width += child_requisition.width;
611         }
612       else
613         {
614           GtkStyleContext *context;
615           GtkStateFlags state;
616           GtkBorder padding;
617
618           context = gtk_widget_get_style_context (widget);
619           state = gtk_widget_get_state_flags (widget);
620           gtk_style_context_get_padding (context, state, &padding);
621
622           if (handle_position == GTK_POS_LEFT ||
623               handle_position == GTK_POS_RIGHT)
624             requisition->height += padding.top;
625           else
626             requisition->width += padding.left;
627         }
628     }
629   else
630     {
631       guint border_width;
632
633       border_width = gtk_container_get_border_width (GTK_CONTAINER (widget));
634       requisition->width += border_width * 2;
635       requisition->height += border_width * 2;
636       
637       if (child)
638         {
639           requisition->width += child_requisition.width;
640           requisition->height += child_requisition.height;
641         }
642       else
643         {
644           requisition->width += CHILDLESS_SIZE;
645           requisition->height += CHILDLESS_SIZE;
646         }
647     }
648 }
649
650 static void
651 gtk_handle_box_get_preferred_width (GtkWidget *widget,
652                                      gint      *minimum,
653                                      gint      *natural)
654 {
655   GtkRequisition requisition;
656
657   gtk_handle_box_size_request (widget, &requisition);
658
659   *minimum = *natural = requisition.width;
660 }
661
662 static void
663 gtk_handle_box_get_preferred_height (GtkWidget *widget,
664                                      gint      *minimum,
665                                      gint      *natural)
666 {
667   GtkRequisition requisition;
668
669   gtk_handle_box_size_request (widget, &requisition);
670
671   *minimum = *natural = requisition.height;
672 }
673
674
675 static void
676 gtk_handle_box_size_allocate (GtkWidget     *widget,
677                               GtkAllocation *allocation)
678 {
679   GtkBin *bin = GTK_BIN (widget);
680   GtkHandleBox *hb = GTK_HANDLE_BOX (widget);
681   GtkHandleBoxPrivate *priv = hb->priv;
682   GtkRequisition child_requisition;
683   GtkWidget *child;
684   gint handle_position;
685
686   handle_position = effective_handle_position (hb);
687
688   child = gtk_bin_get_child (bin);
689
690   if (child)
691     {
692       gtk_widget_get_preferred_size (child, &child_requisition, NULL);
693     }
694   else
695     {
696       child_requisition.width = 0;
697       child_requisition.height = 0;
698     }
699
700   gtk_widget_set_allocation (widget, allocation);
701
702   if (gtk_widget_get_realized (widget))
703     gdk_window_move_resize (gtk_widget_get_window (widget),
704                             allocation->x, allocation->y,
705                             allocation->width, allocation->height);
706
707   if (child != NULL && gtk_widget_get_visible (child))
708     {
709       GtkAllocation child_allocation;
710       guint border_width;
711
712       border_width = gtk_container_get_border_width (GTK_CONTAINER (widget));
713
714       child_allocation.x = border_width;
715       child_allocation.y = border_width;
716       if (handle_position == GTK_POS_LEFT)
717         child_allocation.x += DRAG_HANDLE_SIZE;
718       else if (handle_position == GTK_POS_TOP)
719         child_allocation.y += DRAG_HANDLE_SIZE;
720
721       if (priv->child_detached)
722         {
723           guint float_width;
724           guint float_height;
725           
726           child_allocation.width = child_requisition.width;
727           child_allocation.height = child_requisition.height;
728           
729           float_width = child_allocation.width + 2 * border_width;
730           float_height = child_allocation.height + 2 * border_width;
731           
732           if (handle_position == GTK_POS_LEFT ||
733               handle_position == GTK_POS_RIGHT)
734             float_width += DRAG_HANDLE_SIZE;
735           else
736             float_height += DRAG_HANDLE_SIZE;
737
738           if (gtk_widget_get_realized (widget))
739             {
740               gdk_window_resize (priv->float_window,
741                                  float_width,
742                                  float_height);
743               gdk_window_move_resize (priv->bin_window,
744                                       0,
745                                       0,
746                                       float_width,
747                                       float_height);
748             }
749         }
750       else
751         {
752           child_allocation.width = MAX (1, (gint) allocation->width - 2 * border_width);
753           child_allocation.height = MAX (1, (gint) allocation->height - 2 * border_width);
754
755           if (handle_position == GTK_POS_LEFT ||
756               handle_position == GTK_POS_RIGHT)
757             child_allocation.width -= DRAG_HANDLE_SIZE;
758           else
759             child_allocation.height -= DRAG_HANDLE_SIZE;
760           
761           if (gtk_widget_get_realized (widget))
762             gdk_window_move_resize (priv->bin_window,
763                                     0,
764                                     0,
765                                     allocation->width,
766                                     allocation->height);
767         }
768
769       gtk_widget_size_allocate (child, &child_allocation);
770     }
771 }
772
773 static void
774 gtk_handle_box_draw_ghost (GtkHandleBox *hb,
775                            cairo_t      *cr)
776 {
777   GtkWidget *widget = GTK_WIDGET (hb);
778   GtkStateFlags state;
779   GtkStyleContext *context;
780   guint x;
781   guint y;
782   guint width;
783   guint height;
784   gint allocation_width;
785   gint allocation_height;
786   gint handle_position;
787
788   handle_position = effective_handle_position (hb);
789   allocation_width = gtk_widget_get_allocated_width (widget);
790   allocation_height = gtk_widget_get_allocated_height (widget);
791
792   if (handle_position == GTK_POS_LEFT ||
793       handle_position == GTK_POS_RIGHT)
794     {
795       x = handle_position == GTK_POS_LEFT ? 0 : allocation_width - DRAG_HANDLE_SIZE;
796       y = 0;
797       width = DRAG_HANDLE_SIZE;
798       height = allocation_height;
799     }
800   else
801     {
802       x = 0;
803       y = handle_position == GTK_POS_TOP ? 0 : allocation_height - DRAG_HANDLE_SIZE;
804       width = allocation_width;
805       height = DRAG_HANDLE_SIZE;
806     }
807
808   context = gtk_widget_get_style_context (widget);
809   state = gtk_widget_get_state_flags (widget);
810
811   gtk_style_context_save (context);
812   gtk_style_context_set_state (context, state);
813
814   gtk_render_background (context, cr, x, y, width, height);
815   gtk_render_frame (context, cr, x, y, width, height);
816
817   if (handle_position == GTK_POS_LEFT ||
818       handle_position == GTK_POS_RIGHT)
819     gtk_render_line (context, cr,
820                      handle_position == GTK_POS_LEFT ? DRAG_HANDLE_SIZE : 0,
821                      allocation_height / 2,
822                      handle_position == GTK_POS_LEFT ? allocation_width : allocation_width - DRAG_HANDLE_SIZE,
823                      allocation_height / 2);
824   else
825     gtk_render_line (context, cr,
826                      allocation_width / 2,
827                      handle_position == GTK_POS_TOP ? DRAG_HANDLE_SIZE : 0,
828                      allocation_width / 2,
829                      handle_position == GTK_POS_TOP ? allocation_height : allocation_height - DRAG_HANDLE_SIZE);
830
831   gtk_style_context_restore (context);
832 }
833
834 void
835 gtk_handle_box_set_shadow_type (GtkHandleBox  *handle_box,
836                                 GtkShadowType  type)
837 {
838   GtkHandleBoxPrivate *priv;
839
840   g_return_if_fail (GTK_IS_HANDLE_BOX (handle_box));
841
842   priv = handle_box->priv;
843
844   if ((GtkShadowType) priv->shadow_type != type)
845     {
846       priv->shadow_type = type;
847       g_object_notify (G_OBJECT (handle_box), "shadow-type");
848       gtk_widget_queue_resize (GTK_WIDGET (handle_box));
849     }
850 }
851
852 /**
853  * gtk_handle_box_get_shadow_type:
854  * @handle_box: a #GtkHandleBox
855  * 
856  * Gets the type of shadow drawn around the handle box. See
857  * gtk_handle_box_set_shadow_type().
858  *
859  * Return value: the type of shadow currently drawn around the handle box.
860  **/
861 GtkShadowType
862 gtk_handle_box_get_shadow_type (GtkHandleBox *handle_box)
863 {
864   g_return_val_if_fail (GTK_IS_HANDLE_BOX (handle_box), GTK_SHADOW_ETCHED_OUT);
865
866   return handle_box->priv->shadow_type;
867 }
868
869 void        
870 gtk_handle_box_set_handle_position  (GtkHandleBox    *handle_box,
871                                      GtkPositionType  position)
872 {
873   GtkHandleBoxPrivate *priv;
874
875   g_return_if_fail (GTK_IS_HANDLE_BOX (handle_box));
876
877   priv = handle_box->priv;
878
879   if ((GtkPositionType) priv->handle_position != position)
880     {
881       priv->handle_position = position;
882       g_object_notify (G_OBJECT (handle_box), "handle-position");
883       gtk_widget_queue_resize (GTK_WIDGET (handle_box));
884     }
885 }
886
887 /**
888  * gtk_handle_box_get_handle_position:
889  * @handle_box: a #GtkHandleBox
890  *
891  * Gets the handle position of the handle box. See
892  * gtk_handle_box_set_handle_position().
893  *
894  * Return value: the current handle position.
895  **/
896 GtkPositionType
897 gtk_handle_box_get_handle_position (GtkHandleBox *handle_box)
898 {
899   g_return_val_if_fail (GTK_IS_HANDLE_BOX (handle_box), GTK_POS_LEFT);
900
901   return handle_box->priv->handle_position;
902 }
903
904 void        
905 gtk_handle_box_set_snap_edge        (GtkHandleBox    *handle_box,
906                                      GtkPositionType  edge)
907 {
908   GtkHandleBoxPrivate *priv;
909
910   g_return_if_fail (GTK_IS_HANDLE_BOX (handle_box));
911
912   priv = handle_box->priv;
913
914   if (priv->snap_edge != edge)
915     {
916       priv->snap_edge = edge;
917
918       g_object_freeze_notify (G_OBJECT (handle_box));
919       g_object_notify (G_OBJECT (handle_box), "snap-edge");
920       g_object_notify (G_OBJECT (handle_box), "snap-edge-set");
921       g_object_thaw_notify (G_OBJECT (handle_box));
922     }
923 }
924
925 /**
926  * gtk_handle_box_get_snap_edge:
927  * @handle_box: a #GtkHandleBox
928  * 
929  * Gets the edge used for determining reattachment of the handle box. See
930  * gtk_handle_box_set_snap_edge().
931  *
932  * Return value: the edge used for determining reattachment, or (GtkPositionType)-1 if this
933  *               is determined (as per default) from the handle position. 
934  **/
935 GtkPositionType
936 gtk_handle_box_get_snap_edge (GtkHandleBox *handle_box)
937 {
938   g_return_val_if_fail (GTK_IS_HANDLE_BOX (handle_box), (GtkPositionType)-1);
939
940   return handle_box->priv->snap_edge;
941 }
942
943 /**
944  * gtk_handle_box_get_child_detached:
945  * @handle_box: a #GtkHandleBox
946  *
947  * Whether the handlebox's child is currently detached.
948  *
949  * Return value: %TRUE if the child is currently detached, otherwise %FALSE
950  *
951  * Since: 2.14
952  **/
953 gboolean
954 gtk_handle_box_get_child_detached (GtkHandleBox *handle_box)
955 {
956   g_return_val_if_fail (GTK_IS_HANDLE_BOX (handle_box), FALSE);
957
958   return handle_box->priv->child_detached;
959 }
960
961 static void
962 gtk_handle_box_paint (GtkWidget *widget,
963                       cairo_t   *cr)
964 {
965   GtkHandleBox *hb = GTK_HANDLE_BOX (widget);
966   GtkHandleBoxPrivate *priv = hb->priv;
967   GtkBin *bin = GTK_BIN (widget);
968   GtkStyleContext *context;
969   GtkStateFlags state;
970   GtkWidget *child;
971   gint width, height;
972   GdkRectangle rect;
973   gint handle_position;
974
975   handle_position = effective_handle_position (hb);
976
977   width = gdk_window_get_width (priv->bin_window);
978   height = gdk_window_get_height (priv->bin_window);
979
980   context = gtk_widget_get_style_context (widget);
981   state = gtk_widget_get_state_flags (widget);
982
983   gtk_style_context_save (context);
984   gtk_style_context_set_state (context, state);
985
986   gtk_render_background (context, cr, 0, 0, width, height);
987   gtk_render_frame (context, cr, 0, 0, width, height);
988
989   switch (handle_position)
990     {
991     case GTK_POS_LEFT:
992       rect.x = 0;
993       rect.y = 0;
994       rect.width = DRAG_HANDLE_SIZE;
995       rect.height = height;
996       break;
997     case GTK_POS_RIGHT:
998       rect.x = width - DRAG_HANDLE_SIZE;
999       rect.y = 0;
1000       rect.width = DRAG_HANDLE_SIZE;
1001       rect.height = height;
1002       break;
1003     case GTK_POS_TOP:
1004       rect.x = 0;
1005       rect.y = 0;
1006       rect.width = width;
1007       rect.height = DRAG_HANDLE_SIZE;
1008       break;
1009     case GTK_POS_BOTTOM:
1010       rect.x = 0;
1011       rect.y = height - DRAG_HANDLE_SIZE;
1012       rect.width = width;
1013       rect.height = DRAG_HANDLE_SIZE;
1014       break;
1015     default:
1016       g_assert_not_reached ();
1017       break;
1018     }
1019
1020   gtk_render_handle (context, cr,
1021                      rect.x, rect.y, rect.width, rect.height);
1022
1023   child = gtk_bin_get_child (bin);
1024   if (child != NULL && gtk_widget_get_visible (child))
1025     GTK_WIDGET_CLASS (gtk_handle_box_parent_class)->draw (widget, cr);
1026
1027   gtk_style_context_restore (context);
1028 }
1029
1030 static gboolean
1031 gtk_handle_box_draw (GtkWidget *widget,
1032                      cairo_t   *cr)
1033 {
1034   GtkHandleBox *hb = GTK_HANDLE_BOX (widget);
1035   GtkHandleBoxPrivate *priv = hb->priv;
1036
1037   if (gtk_cairo_should_draw_window (cr, gtk_widget_get_window (widget)))
1038     {
1039       if (priv->child_detached)
1040         gtk_handle_box_draw_ghost (hb, cr);
1041     }
1042   else if (gtk_cairo_should_draw_window (cr, priv->bin_window))
1043     gtk_handle_box_paint (widget, cr);
1044   
1045   return FALSE;
1046 }
1047
1048 static GtkWidget *
1049 gtk_handle_box_get_invisible (void)
1050 {
1051   static GtkWidget *handle_box_invisible = NULL;
1052
1053   if (!handle_box_invisible)
1054     {
1055       handle_box_invisible = gtk_invisible_new ();
1056       gtk_widget_show (handle_box_invisible);
1057     }
1058   
1059   return handle_box_invisible;
1060 }
1061
1062 static gboolean
1063 gtk_handle_box_grab_event (GtkWidget    *widget,
1064                            GdkEvent     *event,
1065                            GtkHandleBox *hb)
1066 {
1067   GtkHandleBoxPrivate *priv = hb->priv;
1068
1069   switch (event->type)
1070     {
1071     case GDK_BUTTON_RELEASE:
1072       if (priv->in_drag)                /* sanity check */
1073         {
1074           gtk_handle_box_end_drag (hb, event->button.time);
1075           return TRUE;
1076         }
1077       break;
1078
1079     case GDK_MOTION_NOTIFY:
1080       return gtk_handle_box_motion (GTK_WIDGET (hb), (GdkEventMotion *)event);
1081       break;
1082
1083     default:
1084       break;
1085     }
1086
1087   return FALSE;
1088 }
1089
1090 static gboolean
1091 gtk_handle_box_button_press (GtkWidget      *widget,
1092                              GdkEventButton *event)
1093 {
1094   GtkHandleBox *hb = GTK_HANDLE_BOX (widget);
1095   GtkHandleBoxPrivate *priv = hb->priv;
1096   gboolean event_handled;
1097   GdkCursor *fleur;
1098   gint handle_position;
1099
1100   handle_position = effective_handle_position (hb);
1101
1102   event_handled = FALSE;
1103   if ((event->button == 1) && 
1104       (event->type == GDK_BUTTON_PRESS || event->type == GDK_2BUTTON_PRESS))
1105     {
1106       GtkWidget *child;
1107       gboolean in_handle;
1108       
1109       if (event->window != priv->bin_window)
1110         return FALSE;
1111
1112       child = gtk_bin_get_child (GTK_BIN (hb));
1113
1114       if (child)
1115         {
1116           GtkAllocation child_allocation;
1117           guint border_width;
1118
1119           gtk_widget_get_allocation (child, &child_allocation);
1120           border_width = gtk_container_get_border_width (GTK_CONTAINER (hb));
1121
1122           switch (handle_position)
1123             {
1124             case GTK_POS_LEFT:
1125               in_handle = event->x < DRAG_HANDLE_SIZE;
1126               break;
1127             case GTK_POS_TOP:
1128               in_handle = event->y < DRAG_HANDLE_SIZE;
1129               break;
1130             case GTK_POS_RIGHT:
1131               in_handle = event->x > 2 * border_width + child_allocation.width;
1132               break;
1133             case GTK_POS_BOTTOM:
1134               in_handle = event->y > 2 * border_width + child_allocation.height;
1135               break;
1136             default:
1137               in_handle = FALSE;
1138               break;
1139             }
1140         }
1141       else
1142         {
1143           in_handle = FALSE;
1144           event_handled = TRUE;
1145         }
1146       
1147       if (in_handle)
1148         {
1149           if (event->type == GDK_BUTTON_PRESS) /* Start a drag */
1150             {
1151               GtkWidget *invisible = gtk_handle_box_get_invisible ();
1152               GdkWindow *window;
1153               gint root_x, root_y;
1154
1155               gtk_invisible_set_screen (GTK_INVISIBLE (invisible),
1156                                         gtk_widget_get_screen (GTK_WIDGET (hb)));
1157               gdk_window_get_origin (priv->bin_window, &root_x, &root_y);
1158
1159               priv->orig_x = event->x_root;
1160               priv->orig_y = event->y_root;
1161
1162               priv->float_allocation.x = root_x - event->x_root;
1163               priv->float_allocation.y = root_y - event->y_root;
1164               priv->float_allocation.width = gdk_window_get_width (priv->bin_window);
1165               priv->float_allocation.height = gdk_window_get_height (priv->bin_window);
1166
1167               window = gtk_widget_get_window (widget);
1168               if (gdk_window_is_viewable (window))
1169                 {
1170                   gdk_window_get_origin (window, &root_x, &root_y);
1171
1172                   priv->attach_allocation.x = root_x;
1173                   priv->attach_allocation.y = root_y;
1174                   priv->attach_allocation.width = gdk_window_get_width (window);
1175                   priv->attach_allocation.height = gdk_window_get_height (window);
1176                 }
1177               else
1178                 {
1179                   priv->attach_allocation.x = -1;
1180                   priv->attach_allocation.y = -1;
1181                   priv->attach_allocation.width = 0;
1182                   priv->attach_allocation.height = 0;
1183                 }
1184               priv->in_drag = TRUE;
1185               priv->grab_device = event->device;
1186               fleur = gdk_cursor_new_for_display (gtk_widget_get_display (widget),
1187                                                   GDK_FLEUR);
1188               if (gdk_device_grab (event->device,
1189                                    gtk_widget_get_window (invisible),
1190                                    GDK_OWNERSHIP_WINDOW,
1191                                    FALSE,
1192                                    (GDK_BUTTON1_MOTION_MASK |
1193                                     GDK_POINTER_MOTION_HINT_MASK |
1194                                     GDK_BUTTON_RELEASE_MASK),
1195                                    fleur,
1196                                    event->time) != GDK_GRAB_SUCCESS)
1197                 {
1198                   priv->in_drag = FALSE;
1199                   priv->grab_device = NULL;
1200                 }
1201               else
1202                 {
1203                   gtk_device_grab_add (invisible, priv->grab_device, TRUE);
1204                   g_signal_connect (invisible, "event",
1205                                     G_CALLBACK (gtk_handle_box_grab_event), hb);
1206                 }
1207
1208               g_object_unref (fleur);
1209               event_handled = TRUE;
1210             }
1211           else if (priv->child_detached) /* Double click */
1212             {
1213               gtk_handle_box_reattach (hb);
1214             }
1215         }
1216     }
1217   
1218   return event_handled;
1219 }
1220
1221 static gboolean
1222 gtk_handle_box_motion (GtkWidget      *widget,
1223                        GdkEventMotion *event)
1224 {
1225   GtkHandleBox *hb = GTK_HANDLE_BOX (widget);
1226   GtkHandleBoxPrivate *priv = hb->priv;
1227   GtkWidget *child;
1228   gint new_x, new_y;
1229   gint snap_edge;
1230   gboolean is_snapped = FALSE;
1231   gint handle_position;
1232   GdkGeometry geometry;
1233   GdkScreen *screen, *pointer_screen;
1234
1235   if (!priv->in_drag)
1236     return FALSE;
1237   handle_position = effective_handle_position (hb);
1238
1239   /* Calculate the attachment point on the float, if the float
1240    * were detached
1241    */
1242   new_x = 0;
1243   new_y = 0;
1244   screen = gtk_widget_get_screen (widget);
1245   gdk_device_get_position (event->device,
1246                            &pointer_screen,
1247                            &new_x, &new_y);
1248   if (pointer_screen != screen)
1249     {
1250       new_x = priv->orig_x;
1251       new_y = priv->orig_y;
1252     }
1253
1254   new_x += priv->float_allocation.x;
1255   new_y += priv->float_allocation.y;
1256
1257   snap_edge = priv->snap_edge;
1258   if (snap_edge == -1)
1259     snap_edge = (handle_position == GTK_POS_LEFT ||
1260                  handle_position == GTK_POS_RIGHT) ?
1261       GTK_POS_TOP : GTK_POS_LEFT;
1262
1263   if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL) 
1264     switch (snap_edge) 
1265       {
1266       case GTK_POS_LEFT:
1267         snap_edge = GTK_POS_RIGHT;
1268         break;
1269       case GTK_POS_RIGHT:
1270         snap_edge = GTK_POS_LEFT;
1271         break;
1272       default:
1273         break;
1274       }
1275
1276   /* First, check if the snapped edge is aligned
1277    */
1278   switch (snap_edge)
1279     {
1280     case GTK_POS_TOP:
1281       is_snapped = abs (priv->attach_allocation.y - new_y) < TOLERANCE;
1282       break;
1283     case GTK_POS_BOTTOM:
1284       is_snapped = abs (priv->attach_allocation.y + (gint)priv->attach_allocation.height -
1285                         new_y - (gint)priv->float_allocation.height) < TOLERANCE;
1286       break;
1287     case GTK_POS_LEFT:
1288       is_snapped = abs (priv->attach_allocation.x - new_x) < TOLERANCE;
1289       break;
1290     case GTK_POS_RIGHT:
1291       is_snapped = abs (priv->attach_allocation.x + (gint)priv->attach_allocation.width -
1292                         new_x - (gint)priv->float_allocation.width) < TOLERANCE;
1293       break;
1294     }
1295
1296   /* Next, check if coordinates in the other direction are sufficiently
1297    * aligned
1298    */
1299   if (is_snapped)
1300     {
1301       gint float_pos1 = 0;      /* Initialize to suppress warnings */
1302       gint float_pos2 = 0;
1303       gint attach_pos1 = 0;
1304       gint attach_pos2 = 0;
1305       
1306       switch (snap_edge)
1307         {
1308         case GTK_POS_TOP:
1309         case GTK_POS_BOTTOM:
1310           attach_pos1 = priv->attach_allocation.x;
1311           attach_pos2 = priv->attach_allocation.x + priv->attach_allocation.width;
1312           float_pos1 = new_x;
1313           float_pos2 = new_x + priv->float_allocation.width;
1314           break;
1315         case GTK_POS_LEFT:
1316         case GTK_POS_RIGHT:
1317           attach_pos1 = priv->attach_allocation.y;
1318           attach_pos2 = priv->attach_allocation.y + priv->attach_allocation.height;
1319           float_pos1 = new_y;
1320           float_pos2 = new_y + priv->float_allocation.height;
1321           break;
1322         }
1323
1324       is_snapped = ((attach_pos1 - TOLERANCE < float_pos1) && 
1325                     (attach_pos2 + TOLERANCE > float_pos2)) ||
1326                    ((float_pos1 - TOLERANCE < attach_pos1) &&
1327                     (float_pos2 + TOLERANCE > attach_pos2));
1328     }
1329
1330   child = gtk_bin_get_child (GTK_BIN (hb));
1331
1332   if (is_snapped)
1333     {
1334       if (priv->child_detached)
1335         {
1336           priv->child_detached = FALSE;
1337           gdk_window_hide (priv->float_window);
1338           gdk_window_reparent (priv->bin_window, gtk_widget_get_window (widget), 0, 0);
1339           priv->float_window_mapped = FALSE;
1340           g_signal_emit (hb,
1341                          handle_box_signals[SIGNAL_CHILD_ATTACHED],
1342                          0,
1343                          child);
1344           
1345           gtk_widget_queue_resize (widget);
1346         }
1347     }
1348   else
1349     {
1350       gint width, height;
1351
1352       width = gdk_window_get_width (priv->float_window);
1353       height = gdk_window_get_height (priv->float_window);
1354
1355       switch (handle_position)
1356         {
1357         case GTK_POS_LEFT:
1358           new_y += ((gint)priv->float_allocation.height - height) / 2;
1359           break;
1360         case GTK_POS_RIGHT:
1361           new_x += (gint)priv->float_allocation.width - width;
1362           new_y += ((gint)priv->float_allocation.height - height) / 2;
1363           break;
1364         case GTK_POS_TOP:
1365           new_x += ((gint)priv->float_allocation.width - width) / 2;
1366           break;
1367         case GTK_POS_BOTTOM:
1368           new_x += ((gint)priv->float_allocation.width - width) / 2;
1369           new_y += (gint)priv->float_allocation.height - height;
1370           break;
1371         }
1372
1373       if (priv->child_detached)
1374         {
1375           gdk_window_move (priv->float_window, new_x, new_y);
1376           gdk_window_raise (priv->float_window);
1377         }
1378       else
1379         {
1380           guint border_width;
1381           GtkRequisition child_requisition;
1382
1383           priv->child_detached = TRUE;
1384
1385           if (child)
1386             {
1387               gtk_widget_get_preferred_size (child, &child_requisition, NULL);
1388             }
1389           else
1390             {
1391               child_requisition.width = 0;
1392               child_requisition.height = 0;
1393             }      
1394
1395           border_width = gtk_container_get_border_width (GTK_CONTAINER (hb));
1396           width = child_requisition.width + 2 * border_width;
1397           height = child_requisition.height + 2 * border_width;
1398
1399           if (handle_position == GTK_POS_LEFT || handle_position == GTK_POS_RIGHT)
1400             width += DRAG_HANDLE_SIZE;
1401           else
1402             height += DRAG_HANDLE_SIZE;
1403           
1404           gdk_window_move_resize (priv->float_window, new_x, new_y, width, height);
1405           gdk_window_reparent (priv->bin_window, priv->float_window, 0, 0);
1406           gdk_window_set_geometry_hints (priv->float_window, &geometry, GDK_HINT_POS);
1407           gdk_window_show (priv->float_window);
1408           priv->float_window_mapped = TRUE;
1409 #if     0
1410           /* this extra move is necessary if we use decorations, or our
1411            * window manager insists on decorations.
1412            */
1413           gdk_display_sync (gtk_widget_get_display (widget));
1414           gdk_window_move (priv->float_window, new_x, new_y);
1415           gdk_display_sync (gtk_widget_get_display (widget));
1416 #endif  /* 0 */
1417           g_signal_emit (hb,
1418                          handle_box_signals[SIGNAL_CHILD_DETACHED],
1419                          0,
1420                          child);
1421           
1422           gtk_widget_queue_resize (widget);
1423         }
1424     }
1425
1426   return TRUE;
1427 }
1428
1429 static void
1430 gtk_handle_box_add (GtkContainer *container,
1431                     GtkWidget    *widget)
1432 {
1433   GtkHandleBoxPrivate *priv = GTK_HANDLE_BOX (container)->priv;
1434
1435   gtk_widget_set_parent_window (widget, priv->bin_window);
1436   GTK_CONTAINER_CLASS (gtk_handle_box_parent_class)->add (container, widget);
1437 }
1438
1439 static void
1440 gtk_handle_box_remove (GtkContainer *container,
1441                        GtkWidget    *widget)
1442 {
1443   GTK_CONTAINER_CLASS (gtk_handle_box_parent_class)->remove (container, widget);
1444
1445   gtk_handle_box_reattach (GTK_HANDLE_BOX (container));
1446 }
1447
1448 static gint
1449 gtk_handle_box_delete_event (GtkWidget *widget,
1450                              GdkEventAny  *event)
1451 {
1452   GtkHandleBox *hb = GTK_HANDLE_BOX (widget);
1453   GtkHandleBoxPrivate *priv = hb->priv;
1454
1455   if (event->window == priv->float_window)
1456     {
1457       gtk_handle_box_reattach (hb);
1458       
1459       return TRUE;
1460     }
1461
1462   return FALSE;
1463 }
1464
1465 static void
1466 gtk_handle_box_reattach (GtkHandleBox *hb)
1467 {
1468   GtkHandleBoxPrivate *priv = hb->priv;
1469   GtkWidget *child;
1470   GtkWidget *widget = GTK_WIDGET (hb);
1471   
1472   if (priv->child_detached)
1473     {
1474       priv->child_detached = FALSE;
1475       if (gtk_widget_get_realized (widget))
1476         {
1477           gdk_window_hide (priv->float_window);
1478           gdk_window_reparent (priv->bin_window, gtk_widget_get_window (widget),
1479                                0, 0);
1480
1481           child = gtk_bin_get_child (GTK_BIN (hb));
1482           if (child)
1483             g_signal_emit (hb,
1484                            handle_box_signals[SIGNAL_CHILD_ATTACHED],
1485                            0,
1486                            child);
1487
1488         }
1489       priv->float_window_mapped = FALSE;
1490     }
1491   if (priv->in_drag)
1492     gtk_handle_box_end_drag (hb, GDK_CURRENT_TIME);
1493
1494   gtk_widget_queue_resize (GTK_WIDGET (hb));
1495 }
1496
1497 static void
1498 gtk_handle_box_end_drag (GtkHandleBox *hb,
1499                          guint32       time)
1500 {
1501   GtkHandleBoxPrivate *priv = hb->priv;
1502   GtkWidget *invisible = gtk_handle_box_get_invisible ();
1503
1504   priv->in_drag = FALSE;
1505
1506   gtk_device_grab_remove (invisible, priv->grab_device);
1507   gdk_device_ungrab (priv->grab_device, time);
1508   g_signal_handlers_disconnect_by_func (invisible,
1509                                         G_CALLBACK (gtk_handle_box_grab_event),
1510                                         hb);
1511
1512   priv->grab_device = NULL;
1513 }