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