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