]> Pileus Git - ~andy/gtk/blob - gtk/gtkhandlebox.c
Integrate Erwann Chenede's multihead changes for the gtk/ directory.
[~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 <stdlib.h>
29 #include "gtkhandlebox.h"
30 #include "gtkmain.h"
31 #include "gtkmarshalers.h"
32 #include "gtksignal.h"
33 #include "gtkwindow.h"
34 #include "gtkintl.h"
35
36 enum {
37   PROP_0,
38   PROP_SHADOW,
39   PROP_SHADOW_TYPE,
40   PROP_HANDLE_POSITION,
41   PROP_SNAP_EDGE
42 };
43
44 #define DRAG_HANDLE_SIZE 10
45 #define CHILDLESS_SIZE  25
46 #define GHOST_HEIGHT 3
47 #define TOLERANCE 5
48
49 enum {
50   SIGNAL_CHILD_ATTACHED,
51   SIGNAL_CHILD_DETACHED,
52   SIGNAL_LAST
53 };
54
55 /* The algorithm for docking and redocking implemented here
56  * has a couple of nice properties:
57  *
58  * 1) During a single drag, docking always occurs at the
59  *    the same cursor position. This means that the users
60  *    motions are reversible, and that you won't
61  *    undock/dock oscillations.
62  *
63  * 2) Docking generally occurs at user-visible features.
64  *    The user, once they figure out to redock, will
65  *    have useful information about doing it again in
66  *    the future.
67  *
68  * Please try to preserve these properties if you
69  * change the algorithm. (And the current algorithm
70  * is far from ideal). Briefly, the current algorithm
71  * for deciding whether the handlebox is docked or not:
72  *
73  * 1) The decision is done by comparing two rectangles - the
74  *    allocation if the widget at the start of the drag,
75  *    and the boundary of hb->bin_window at the start of
76  *    of the drag offset by the distance that the cursor
77  *    has moved.
78  *
79  * 2) These rectangles must have one edge, the "snap_edge"
80  *    of the handlebox, aligned within TOLERANCE.
81  * 
82  * 3) On the other dimension, the extents of one rectangle
83  *    must be contained in the extents of the other,
84  *    extended by tolerance. That is, either we can have:
85  *
86  * <-TOLERANCE-|--------bin_window--------------|-TOLERANCE->
87  *         <--------float_window-------------------->
88  *
89  * or we can have:
90  *
91  * <-TOLERANCE-|------float_window--------------|-TOLERANCE->
92  *          <--------bin_window-------------------->
93  */
94
95 static void gtk_handle_box_class_init     (GtkHandleBoxClass *klass);
96 static void gtk_handle_box_init           (GtkHandleBox      *handle_box);
97 static void gtk_handle_box_set_property   (GObject      *object,
98                                            guint         param_id,
99                                            const GValue *value,
100                                            GParamSpec   *pspec);
101 static void gtk_handle_box_get_property   (GObject     *object,
102                                            guint        param_id,
103                                            GValue      *value,
104                                            GParamSpec  *pspec);
105 static void gtk_handle_box_destroy        (GtkObject         *object);
106 static void gtk_handle_box_map            (GtkWidget         *widget);
107 static void gtk_handle_box_unmap          (GtkWidget         *widget);
108 static void gtk_handle_box_realize        (GtkWidget         *widget);
109 static void gtk_handle_box_unrealize      (GtkWidget         *widget);
110 static void gtk_handle_box_style_set      (GtkWidget         *widget,
111                                            GtkStyle          *previous_style);
112 static void gtk_handle_box_size_request   (GtkWidget         *widget,
113                                            GtkRequisition    *requisition);
114 static void gtk_handle_box_size_allocate  (GtkWidget         *widget,
115                                            GtkAllocation     *real_allocation);
116 static void gtk_handle_box_add            (GtkContainer      *container,
117                                            GtkWidget         *widget);
118 static void gtk_handle_box_remove         (GtkContainer      *container,
119                                            GtkWidget         *widget);
120 static void gtk_handle_box_draw_ghost     (GtkHandleBox      *hb);
121 static void gtk_handle_box_paint          (GtkWidget         *widget,
122                                            GdkEventExpose    *event,
123                                            GdkRectangle      *area);
124 static gint gtk_handle_box_expose         (GtkWidget         *widget,
125                                            GdkEventExpose    *event);
126 static gint gtk_handle_box_button_changed (GtkWidget         *widget,
127                                            GdkEventButton    *event);
128 static gint gtk_handle_box_motion         (GtkWidget         *widget,
129                                            GdkEventMotion    *event);
130 static gint gtk_handle_box_delete_event   (GtkWidget         *widget,
131                                            GdkEventAny       *event);
132 static void gtk_handle_box_reattach       (GtkHandleBox      *hb);
133
134
135 static GtkBinClass *parent_class;
136 static guint        handle_box_signals[SIGNAL_LAST] = { 0 };
137
138
139 GtkType
140 gtk_handle_box_get_type (void)
141 {
142   static GtkType handle_box_type = 0;
143
144   if (!handle_box_type)
145     {
146       static const GtkTypeInfo handle_box_info =
147       {
148         "GtkHandleBox",
149         sizeof (GtkHandleBox),
150         sizeof (GtkHandleBoxClass),
151         (GtkClassInitFunc) gtk_handle_box_class_init,
152         (GtkObjectInitFunc) gtk_handle_box_init,
153         /* reserved_1 */ NULL,
154         /* reserved_2 */ NULL,
155         (GtkClassInitFunc) NULL,
156       };
157
158       handle_box_type = gtk_type_unique (GTK_TYPE_BIN, &handle_box_info);
159     }
160
161   return handle_box_type;
162 }
163
164 static void
165 gtk_handle_box_class_init (GtkHandleBoxClass *class)
166 {
167   GObjectClass *gobject_class;
168   GtkObjectClass *object_class;
169   GtkWidgetClass *widget_class;
170   GtkContainerClass *container_class;
171
172   gobject_class = (GObjectClass *) class;
173   object_class = (GtkObjectClass *) class;
174   widget_class = (GtkWidgetClass *) class;
175   container_class = (GtkContainerClass *) class;
176
177   parent_class = gtk_type_class (GTK_TYPE_BIN);
178
179   gobject_class->set_property = gtk_handle_box_set_property;
180   gobject_class->get_property = gtk_handle_box_get_property;
181   
182   g_object_class_install_property (gobject_class,
183                                    PROP_SHADOW,
184                                    g_param_spec_enum ("shadow", NULL,
185                                                       _("Deprecated property, use shadow_type instead."),
186                                                       GTK_TYPE_SHADOW_TYPE,
187                                                       GTK_SHADOW_ETCHED_OUT,
188                                                       G_PARAM_READABLE | G_PARAM_WRITABLE));
189   g_object_class_install_property (gobject_class,
190                                    PROP_SHADOW_TYPE,
191                                    g_param_spec_enum ("shadow_type",
192                                                       _("Shadow type"),
193                                                       _("Appearance of the shadow that surrounds the container."),
194                                                       GTK_TYPE_SHADOW_TYPE,
195                                                       GTK_SHADOW_ETCHED_OUT,
196                                                       G_PARAM_READABLE | G_PARAM_WRITABLE));
197   
198   g_object_class_install_property (gobject_class,
199                                    PROP_HANDLE_POSITION,
200                                    g_param_spec_enum ("handle_position",
201                                                       _("Handle position"),
202                                                       _("Position of the handle relative to the child widget."),
203                                                       GTK_TYPE_POSITION_TYPE,
204                                                       GTK_POS_LEFT,
205                                                       G_PARAM_READABLE | G_PARAM_WRITABLE));
206   
207   g_object_class_install_property (gobject_class,
208                                    PROP_SNAP_EDGE,
209                                    g_param_spec_enum ("snap_edge",
210                                                       _("Snap edge"),
211                                                       _("Side of the handlebox that's lined up with the docking point to dock the handlebox."),
212                                                       GTK_TYPE_POSITION_TYPE,
213                                                       GTK_POS_TOP,
214                                                       G_PARAM_READABLE | G_PARAM_WRITABLE));
215   object_class->destroy = gtk_handle_box_destroy;
216
217   widget_class->map = gtk_handle_box_map;
218   widget_class->unmap = gtk_handle_box_unmap;
219   widget_class->realize = gtk_handle_box_realize;
220   widget_class->unrealize = gtk_handle_box_unrealize;
221   widget_class->style_set = gtk_handle_box_style_set;
222   widget_class->size_request = gtk_handle_box_size_request;
223   widget_class->size_allocate = gtk_handle_box_size_allocate;
224   widget_class->expose_event = gtk_handle_box_expose;
225   widget_class->button_press_event = gtk_handle_box_button_changed;
226   widget_class->button_release_event = gtk_handle_box_button_changed;
227   widget_class->motion_notify_event = gtk_handle_box_motion;
228   widget_class->delete_event = gtk_handle_box_delete_event;
229
230   container_class->add = gtk_handle_box_add;
231   container_class->remove = gtk_handle_box_remove;
232
233   class->child_attached = NULL;
234   class->child_detached = NULL;
235
236   handle_box_signals[SIGNAL_CHILD_ATTACHED] =
237     gtk_signal_new ("child_attached",
238                     GTK_RUN_FIRST,
239                     GTK_CLASS_TYPE (object_class),
240                     GTK_SIGNAL_OFFSET (GtkHandleBoxClass, child_attached),
241                     _gtk_marshal_VOID__OBJECT,
242                     GTK_TYPE_NONE, 1,
243                     GTK_TYPE_WIDGET);
244   handle_box_signals[SIGNAL_CHILD_DETACHED] =
245     gtk_signal_new ("child_detached",
246                     GTK_RUN_FIRST,
247                     GTK_CLASS_TYPE (object_class),
248                     GTK_SIGNAL_OFFSET (GtkHandleBoxClass, child_detached),
249                     _gtk_marshal_VOID__OBJECT,
250                     GTK_TYPE_NONE, 1,
251                     GTK_TYPE_WIDGET);
252 }
253
254 static void
255 gtk_handle_box_init (GtkHandleBox *handle_box)
256 {
257   GTK_WIDGET_UNSET_FLAGS (handle_box, GTK_NO_WINDOW);
258
259   handle_box->bin_window = NULL;
260   handle_box->float_window = NULL;
261   handle_box->shadow_type = GTK_SHADOW_OUT;
262   handle_box->handle_position = GTK_POS_LEFT;
263   handle_box->float_window_mapped = FALSE;
264   handle_box->child_detached = FALSE;
265   handle_box->in_drag = FALSE;
266   handle_box->shrink_on_detach = TRUE;
267   handle_box->snap_edge = -1;
268 }
269
270 static void 
271 gtk_handle_box_set_property (GObject         *object,
272                              guint            prop_id,
273                              const GValue    *value,
274                              GParamSpec      *pspec)
275 {
276   GtkHandleBox *handle_box = GTK_HANDLE_BOX (object);
277
278   switch (prop_id)
279     {
280     case PROP_SHADOW:
281     case PROP_SHADOW_TYPE:
282       gtk_handle_box_set_shadow_type (handle_box, g_value_get_enum (value));
283       break;
284     case PROP_HANDLE_POSITION:
285       gtk_handle_box_set_handle_position (handle_box, g_value_get_enum (value));
286       break;
287     case PROP_SNAP_EDGE:
288       gtk_handle_box_set_snap_edge (handle_box, g_value_get_enum (value));
289       break;
290     default:
291       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
292       break;
293     }
294 }
295
296 static void 
297 gtk_handle_box_get_property (GObject         *object,
298                              guint            prop_id,
299                              GValue          *value,
300                              GParamSpec      *pspec)
301 {
302   GtkHandleBox *handle_box = GTK_HANDLE_BOX (object);
303   
304   switch (prop_id)
305     {
306     case PROP_SHADOW:
307     case PROP_SHADOW_TYPE:
308       g_value_set_enum (value, handle_box->shadow_type);
309       break;
310     case PROP_HANDLE_POSITION:
311       g_value_set_enum (value, handle_box->handle_position);
312       break;
313     case PROP_SNAP_EDGE:
314       g_value_set_enum (value, handle_box->snap_edge);
315       break;
316     default:
317       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
318       break;
319     }
320 }
321  
322 GtkWidget*
323 gtk_handle_box_new (void)
324 {
325   return GTK_WIDGET (gtk_type_new (gtk_handle_box_get_type ()));
326 }
327
328 static void
329 gtk_handle_box_destroy (GtkObject *object)
330 {
331   if (GTK_OBJECT_CLASS (parent_class)->destroy)
332     (* GTK_OBJECT_CLASS (parent_class)->destroy) (object);
333 }
334
335 static void
336 gtk_handle_box_map (GtkWidget *widget)
337 {
338   GtkBin *bin;
339   GtkHandleBox *hb;
340
341   GTK_WIDGET_SET_FLAGS (widget, GTK_MAPPED);
342
343   bin = GTK_BIN (widget);
344   hb = GTK_HANDLE_BOX (widget);
345
346   if (bin->child &&
347       GTK_WIDGET_VISIBLE (bin->child) &&
348       !GTK_WIDGET_MAPPED (bin->child))
349     gtk_widget_map (bin->child);
350
351   if (hb->child_detached && !hb->float_window_mapped)
352     {
353       gdk_window_show (hb->float_window);
354       hb->float_window_mapped = TRUE;
355     }
356
357   gdk_window_show (hb->bin_window);
358   gdk_window_show (widget->window);
359 }
360
361 static void
362 gtk_handle_box_unmap (GtkWidget *widget)
363 {
364   GtkHandleBox *hb;
365
366   GTK_WIDGET_UNSET_FLAGS (widget, GTK_MAPPED);
367
368   hb = GTK_HANDLE_BOX (widget);
369
370   gdk_window_hide (widget->window);
371   if (hb->float_window_mapped)
372     {
373       gdk_window_hide (hb->float_window);
374       hb->float_window_mapped = FALSE;
375     }
376 }
377
378 static void
379 gtk_handle_box_realize (GtkWidget *widget)
380 {
381   GdkWindowAttr attributes;
382   gint attributes_mask;
383   GtkHandleBox *hb;
384
385   hb = GTK_HANDLE_BOX (widget);
386
387   GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
388
389   attributes.x = widget->allocation.x;
390   attributes.y = widget->allocation.y;
391   attributes.width = widget->allocation.width;
392   attributes.height = widget->allocation.height;
393   attributes.window_type = GDK_WINDOW_CHILD;
394   attributes.wclass = GDK_INPUT_OUTPUT;
395   attributes.visual = gtk_widget_get_visual (widget);
396   attributes.colormap = gtk_widget_get_colormap (widget);
397   attributes.event_mask = (gtk_widget_get_events (widget)
398                            | GDK_EXPOSURE_MASK);
399   attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
400   widget->window = gdk_window_new (gtk_widget_get_parent_window (widget), &attributes, attributes_mask);
401   gdk_window_set_user_data (widget->window, widget);
402
403   attributes.x = 0;
404   attributes.y = 0;
405   attributes.width = widget->allocation.width;
406   attributes.height = widget->allocation.height;
407   attributes.window_type = GDK_WINDOW_CHILD;
408   attributes.event_mask = (gtk_widget_get_events (widget) |
409                            GDK_EXPOSURE_MASK |
410                            GDK_BUTTON1_MOTION_MASK |
411                            GDK_POINTER_MOTION_HINT_MASK |
412                            GDK_BUTTON_PRESS_MASK |
413                             GDK_BUTTON_RELEASE_MASK);
414   attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
415   hb->bin_window = gdk_window_new (widget->window, &attributes, attributes_mask);
416   gdk_window_set_user_data (hb->bin_window, widget);
417   if (GTK_BIN (hb)->child)
418     gtk_widget_set_parent_window (GTK_BIN (hb)->child, hb->bin_window);
419   
420   attributes.x = 0;
421   attributes.y = 0;
422   attributes.width = widget->requisition.width;
423   attributes.height = widget->requisition.height;
424   attributes.window_type = GDK_WINDOW_TOPLEVEL;
425   attributes.wclass = GDK_INPUT_OUTPUT;
426   attributes.visual = gtk_widget_get_visual (widget);
427   attributes.colormap = gtk_widget_get_colormap (widget);
428   attributes.event_mask = (gtk_widget_get_events (widget) |
429                            GDK_KEY_PRESS_MASK |
430                            GDK_ENTER_NOTIFY_MASK |
431                            GDK_LEAVE_NOTIFY_MASK |
432                            GDK_FOCUS_CHANGE_MASK |
433                            GDK_STRUCTURE_MASK);
434   attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
435   hb->float_window = gdk_window_new (gtk_widget_get_root_window (widget),
436                                      &attributes, attributes_mask);
437   gdk_window_set_user_data (hb->float_window, widget);
438   gdk_window_set_decorations (hb->float_window, 0);
439   gdk_window_set_type_hint (hb->float_window, GDK_WINDOW_TYPE_HINT_TOOLBAR);
440   
441   widget->style = gtk_style_attach (widget->style, widget->window);
442   gtk_style_set_background (widget->style, widget->window, GTK_WIDGET_STATE (hb));
443   gtk_style_set_background (widget->style, hb->bin_window, GTK_WIDGET_STATE (hb));
444   gtk_style_set_background (widget->style, hb->float_window, GTK_WIDGET_STATE (hb));
445   gdk_window_set_back_pixmap (widget->window, NULL, TRUE);
446 }
447
448 static void
449 gtk_handle_box_unrealize (GtkWidget *widget)
450 {
451   GtkHandleBox *hb = GTK_HANDLE_BOX (widget);
452
453   gdk_window_set_user_data (hb->bin_window, NULL);
454   gdk_window_destroy (hb->bin_window);
455   hb->bin_window = NULL;
456   gdk_window_set_user_data (hb->float_window, NULL);
457   gdk_window_destroy (hb->float_window);
458   hb->float_window = NULL;
459
460   if (GTK_WIDGET_CLASS (parent_class)->unrealize)
461     (* GTK_WIDGET_CLASS (parent_class)->unrealize) (widget);
462 }
463
464 static void
465 gtk_handle_box_style_set (GtkWidget *widget,
466                           GtkStyle  *previous_style)
467 {
468   GtkHandleBox *hb = GTK_HANDLE_BOX (widget);
469
470   if (GTK_WIDGET_REALIZED (widget) &&
471       !GTK_WIDGET_NO_WINDOW (widget))
472     {
473       gtk_style_set_background (widget->style, widget->window,
474                                 widget->state);
475       gtk_style_set_background (widget->style, hb->bin_window, widget->state);
476       gtk_style_set_background (widget->style, hb->float_window, widget->state);
477     }
478 }
479
480 static void
481 gtk_handle_box_size_request (GtkWidget      *widget,
482                              GtkRequisition *requisition)
483 {
484   GtkBin *bin;
485   GtkHandleBox *hb;
486   GtkRequisition child_requisition;
487
488   bin = GTK_BIN (widget);
489   hb = GTK_HANDLE_BOX (widget);
490
491   if (hb->handle_position == GTK_POS_LEFT ||
492       hb->handle_position == GTK_POS_RIGHT)
493     {
494       requisition->width = DRAG_HANDLE_SIZE;
495       requisition->height = 0;
496     }
497   else
498     {
499       requisition->width = 0;
500       requisition->height = DRAG_HANDLE_SIZE;
501     }
502
503   /* if our child is not visible, we still request its size, since we
504    * won't have any usefull hint for our size otherwise.
505    */
506   if (bin->child)
507     gtk_widget_size_request (bin->child, &child_requisition);
508   else
509     {
510       child_requisition.width = 0;
511       child_requisition.height = 0;
512     }      
513
514   if (hb->child_detached)
515     {
516       /* FIXME: This doesn't work currently */
517       if (!hb->shrink_on_detach)
518         {
519           if (hb->handle_position == GTK_POS_LEFT ||
520               hb->handle_position == GTK_POS_RIGHT)
521             requisition->height += child_requisition.height;
522           else
523             requisition->width += child_requisition.width;
524         }
525       else
526         {
527           if (hb->handle_position == GTK_POS_LEFT ||
528               hb->handle_position == GTK_POS_RIGHT)
529             requisition->height += widget->style->ythickness;
530           else
531             requisition->width += widget->style->xthickness;
532         }
533     }
534   else
535     {
536       requisition->width += GTK_CONTAINER (widget)->border_width * 2;
537       requisition->height += GTK_CONTAINER (widget)->border_width * 2;
538       
539       if (bin->child)
540         {
541           requisition->width += child_requisition.width;
542           requisition->height += child_requisition.height;
543         }
544       else
545         {
546           requisition->width += CHILDLESS_SIZE;
547           requisition->height += CHILDLESS_SIZE;
548         }
549     }
550 }
551
552 static void
553 gtk_handle_box_size_allocate (GtkWidget     *widget,
554                               GtkAllocation *allocation)
555 {
556   GtkBin *bin;
557   GtkHandleBox *hb;
558   GtkRequisition child_requisition;
559   
560   bin = GTK_BIN (widget);
561   hb = GTK_HANDLE_BOX (widget);
562   
563   if (bin->child)
564     gtk_widget_get_child_requisition (bin->child, &child_requisition);
565   else
566     {
567       child_requisition.width = 0;
568       child_requisition.height = 0;
569     }      
570       
571   widget->allocation = *allocation;
572
573   if (GTK_WIDGET_REALIZED (hb))
574     gdk_window_move_resize (widget->window,
575                             widget->allocation.x,
576                             widget->allocation.y,
577                             widget->allocation.width,
578                             widget->allocation.height);
579
580
581   if (bin->child && GTK_WIDGET_VISIBLE (bin->child))
582     {
583       GtkWidget *child;
584       GtkAllocation child_allocation;
585       guint border_width;
586
587       child = bin->child;
588       border_width = GTK_CONTAINER (widget)->border_width;
589
590       child_allocation.x = border_width;
591       child_allocation.y = border_width;
592       if (hb->handle_position == GTK_POS_LEFT)
593         child_allocation.x += DRAG_HANDLE_SIZE;
594       else if (hb->handle_position == GTK_POS_TOP)
595         child_allocation.y += DRAG_HANDLE_SIZE;
596
597       if (hb->child_detached)
598         {
599           guint float_width;
600           guint float_height;
601           
602           child_allocation.width = child_requisition.width;
603           child_allocation.height = child_requisition.height;
604           
605           float_width = child_allocation.width + 2 * border_width;
606           float_height = child_allocation.height + 2 * border_width;
607           
608           if (hb->handle_position == GTK_POS_LEFT ||
609               hb->handle_position == GTK_POS_RIGHT)
610             float_width += DRAG_HANDLE_SIZE;
611           else
612             float_height += DRAG_HANDLE_SIZE;
613
614           if (GTK_WIDGET_REALIZED (hb))
615             {
616               gdk_window_resize (hb->float_window,
617                                  float_width,
618                                  float_height);
619               gdk_window_move_resize (hb->bin_window,
620                                       0,
621                                       0,
622                                       float_width,
623                                       float_height);
624             }
625         }
626       else
627         {
628           child_allocation.width = MAX (1, (gint)widget->allocation.width - 2 * border_width);
629           child_allocation.height = MAX (1, (gint)widget->allocation.height - 2 * border_width);
630
631           if (hb->handle_position == GTK_POS_LEFT ||
632               hb->handle_position == GTK_POS_RIGHT)
633             child_allocation.width -= DRAG_HANDLE_SIZE;
634           else
635             child_allocation.height -= DRAG_HANDLE_SIZE;
636           
637           if (GTK_WIDGET_REALIZED (hb))
638             gdk_window_move_resize (hb->bin_window,
639                                     0,
640                                     0,
641                                     widget->allocation.width,
642                                     widget->allocation.height);
643         }
644
645       gtk_widget_size_allocate (bin->child, &child_allocation);
646     }
647 }
648
649 static void
650 gtk_handle_box_draw_ghost (GtkHandleBox *hb)
651 {
652   GtkWidget *widget;
653   guint x;
654   guint y;
655   guint width;
656   guint height;
657
658   widget = GTK_WIDGET (hb);
659
660   if (hb->handle_position == GTK_POS_LEFT ||
661       hb->handle_position == GTK_POS_RIGHT)
662     {
663       x = hb->handle_position == GTK_POS_LEFT ? 0 : widget->allocation.width - DRAG_HANDLE_SIZE;
664       y = 0;
665       width = DRAG_HANDLE_SIZE;
666       height = widget->allocation.height;
667     }
668   else
669     {
670       x = 0;
671       y = hb->handle_position == GTK_POS_TOP ? 0 : widget->allocation.height - DRAG_HANDLE_SIZE;
672       width = widget->allocation.width;
673       height = DRAG_HANDLE_SIZE;
674     }
675   gtk_paint_shadow (widget->style,
676                     widget->window,
677                     GTK_WIDGET_STATE (widget),
678                     GTK_SHADOW_ETCHED_IN,
679                     NULL, widget, "handle",
680                     x,
681                     y,
682                     width,
683                     height);
684    if (hb->handle_position == GTK_POS_LEFT ||
685        hb->handle_position == GTK_POS_RIGHT)
686      gtk_paint_hline (widget->style,
687                       widget->window,
688                       GTK_WIDGET_STATE (widget),
689                       NULL, widget, "handlebox",
690                       hb->handle_position == GTK_POS_LEFT ? DRAG_HANDLE_SIZE : 0,
691                       hb->handle_position == GTK_POS_LEFT ? widget->allocation.width : widget->allocation.width - DRAG_HANDLE_SIZE,
692                       widget->allocation.height / 2);
693    else
694      gtk_paint_vline (widget->style,
695                       widget->window,
696                       GTK_WIDGET_STATE (widget),
697                       NULL, widget, "handlebox",
698                       hb->handle_position == GTK_POS_TOP ? DRAG_HANDLE_SIZE : 0,
699                       hb->handle_position == GTK_POS_TOP ? widget->allocation.height : widget->allocation.height - DRAG_HANDLE_SIZE,
700                       widget->allocation.width / 2);
701 }
702
703 static void
704 draw_textured_frame (GtkWidget *widget, GdkWindow *window, GdkRectangle *rect, GtkShadowType shadow,
705                      GdkRectangle *clip)
706 {
707    gtk_paint_handle (widget->style, window, GTK_STATE_NORMAL, shadow,
708                      clip, widget, "handlebox",
709                      rect->x, rect->y, rect->width, rect->height, 
710                      GTK_ORIENTATION_VERTICAL);
711 }
712
713 void
714 gtk_handle_box_set_shadow_type (GtkHandleBox  *handle_box,
715                                 GtkShadowType  type)
716 {
717   g_return_if_fail (GTK_IS_HANDLE_BOX (handle_box));
718
719   if ((GtkShadowType) handle_box->shadow_type != type)
720     {
721       handle_box->shadow_type = type;
722       g_object_notify (G_OBJECT (handle_box), "shadow_type");
723       gtk_widget_queue_resize (GTK_WIDGET (handle_box));
724     }
725 }
726
727 /**
728  * gtk_handle_box_get_shadow_type:
729  * @handle_box: a #GtkHandleBox
730  * 
731  * Gets the type of shadow drawn around the handle box. See
732  * gtk_handle_box_set_shadow_type().
733  *
734  * Return value: the type of shadow currently drawn around the handle box.
735  **/
736 GtkShadowType
737 gtk_handle_box_get_shadow_type (GtkHandleBox *handle_box)
738 {
739   g_return_val_if_fail (GTK_IS_HANDLE_BOX (handle_box), GTK_SHADOW_ETCHED_OUT);
740
741   return handle_box->shadow_type;
742 }
743
744 void        
745 gtk_handle_box_set_handle_position  (GtkHandleBox    *handle_box,
746                                      GtkPositionType  position)
747 {
748   g_return_if_fail (GTK_IS_HANDLE_BOX (handle_box));
749
750   if ((GtkPositionType) handle_box->handle_position != position)
751     {
752       handle_box->handle_position = position;
753       g_object_notify (G_OBJECT (handle_box), "handle_position");
754       gtk_widget_queue_resize (GTK_WIDGET (handle_box));
755     }
756 }
757
758 /**
759  * gtk_handle_box_get_handle_position:
760  * @handle_box: a #GtkHandleBox
761  *
762  * Gets the handle position of the handle box. See
763  * gtk_handle_box_set_handle_position().
764  *
765  * Return value: the current handle position.
766  **/
767 GtkPositionType
768 gtk_handle_box_get_handle_position (GtkHandleBox *handle_box)
769 {
770   g_return_val_if_fail (GTK_IS_HANDLE_BOX (handle_box), GTK_POS_LEFT);
771
772   return handle_box->handle_position;
773 }
774
775 void        
776 gtk_handle_box_set_snap_edge        (GtkHandleBox    *handle_box,
777                                      GtkPositionType  edge)
778 {
779   g_return_if_fail (GTK_IS_HANDLE_BOX (handle_box));
780
781   if (handle_box->snap_edge != edge)
782     {
783       handle_box->snap_edge = edge;
784       g_object_notify (G_OBJECT (handle_box), "snap_edge");
785     }
786 }
787
788 /**
789  * gtk_handle_box_get_snap_edge:
790  * @handle_box: a #GtkHandleBox
791  * 
792  * Gets the edge used for determining reattachment of the handle box. See
793  * gtk_handle_box_set_snap_edge().
794  *
795  * Return value: the edge used for determining reattachment, or (GtkPositionType)-1 if this
796  *               is determined (as per default) from the handle position. 
797  **/
798 GtkPositionType
799 gtk_handle_box_get_snap_edge (GtkHandleBox *handle_box)
800 {
801   g_return_val_if_fail (GTK_IS_HANDLE_BOX (handle_box), (GtkPositionType)-1);
802
803   return handle_box->snap_edge;
804 }
805
806 static void
807 gtk_handle_box_paint (GtkWidget      *widget,
808                       GdkEventExpose *event,
809                       GdkRectangle   *area)
810 {
811   GtkBin *bin;
812   GtkHandleBox *hb;
813   guint width;
814   guint height;
815   GdkRectangle rect;
816   GdkRectangle dest;
817
818   bin = GTK_BIN (widget);
819   hb = GTK_HANDLE_BOX (widget);
820
821   gdk_window_get_size (hb->bin_window, &width, &height);
822   
823   if (!event)
824     gtk_paint_box (widget->style,
825                    hb->bin_window,
826                    GTK_WIDGET_STATE (widget),
827                    hb->shadow_type,
828                    area, widget, "handlebox_bin",
829                    0, 0, -1, -1);
830   else
831    gtk_paint_box (widget->style,
832                   hb->bin_window,
833                   GTK_WIDGET_STATE (widget),
834                   hb->shadow_type,
835                   &event->area, widget, "handlebox_bin",
836                   0, 0, -1, -1);
837
838 /* We currently draw the handle _above_ the relief of the handlebox.
839  * it could also be drawn on the same level...
840
841                  hb->handle_position == GTK_POS_LEFT ? DRAG_HANDLE_SIZE : 0,
842                  hb->handle_position == GTK_POS_TOP ? DRAG_HANDLE_SIZE : 0,
843                  width,
844                  height);*/
845
846   switch (hb->handle_position)
847     {
848     case GTK_POS_LEFT:
849       rect.x = 0;
850       rect.y = 0; 
851       rect.width = DRAG_HANDLE_SIZE;
852       rect.height = height;
853       break;
854     case GTK_POS_RIGHT:
855       rect.x = width - DRAG_HANDLE_SIZE; 
856       rect.y = 0;
857       rect.width = DRAG_HANDLE_SIZE;
858       rect.height = height;
859       break;
860     case GTK_POS_TOP:
861       rect.x = 0;
862       rect.y = 0; 
863       rect.width = width;
864       rect.height = DRAG_HANDLE_SIZE;
865       break;
866     case GTK_POS_BOTTOM:
867       rect.x = 0;
868       rect.y = height - DRAG_HANDLE_SIZE;
869       rect.width = width;
870       rect.height = DRAG_HANDLE_SIZE;
871       break;
872     }
873
874   if (gdk_rectangle_intersect (event ? &event->area : area, &rect, &dest))
875     draw_textured_frame (widget, hb->bin_window, &rect,
876                          GTK_SHADOW_OUT,
877                          event ? &event->area : area);
878
879   if (bin->child && GTK_WIDGET_VISIBLE (bin->child))
880     {
881       GdkRectangle child_area;
882
883       if (!event) /* we were called from draw() */
884         {
885           if (gtk_widget_intersect (bin->child, area, &child_area))
886             gtk_widget_draw (bin->child, &child_area);
887         }
888       else /* we were called from expose() */
889         (* GTK_WIDGET_CLASS (parent_class)->expose_event) (widget, event);
890     }
891 }
892
893 static gint
894 gtk_handle_box_expose (GtkWidget      *widget,
895                        GdkEventExpose *event)
896 {
897   GtkHandleBox *hb;
898
899   if (GTK_WIDGET_DRAWABLE (widget))
900     {
901       hb = GTK_HANDLE_BOX (widget);
902
903       if (event->window == widget->window)
904         {
905           if (hb->child_detached)
906             gtk_handle_box_draw_ghost (hb);
907         }
908       else
909         gtk_handle_box_paint (widget, event, NULL);
910     }
911   
912   return FALSE;
913 }
914
915 static gint
916 gtk_handle_box_button_changed (GtkWidget      *widget,
917                                GdkEventButton *event)
918 {
919   GtkHandleBox *hb;
920   gboolean event_handled;
921   GdkCursor *fleur;
922
923   hb = GTK_HANDLE_BOX (widget);
924
925   event_handled = FALSE;
926   if ((event->button == 1) && 
927       (event->type == GDK_BUTTON_PRESS || event->type == GDK_2BUTTON_PRESS))
928     {
929       GtkWidget *child;
930       gboolean in_handle;
931       
932       if (event->window != hb->bin_window)
933         return FALSE;
934
935       child = GTK_BIN (hb)->child;
936
937       if (child)
938         {
939           switch (hb->handle_position)
940             {
941             case GTK_POS_LEFT:
942               in_handle = event->x < DRAG_HANDLE_SIZE;
943               break;
944             case GTK_POS_TOP:
945               in_handle = event->y < DRAG_HANDLE_SIZE;
946               break;
947             case GTK_POS_RIGHT:
948               in_handle = event->x > 2 * GTK_CONTAINER (hb)->border_width + child->allocation.width;
949               break;
950             case GTK_POS_BOTTOM:
951               in_handle = event->y > 2 * GTK_CONTAINER (hb)->border_width + child->allocation.height;
952               break;
953             default:
954               in_handle = FALSE;
955               break;
956             }
957         }
958       else
959         {
960           in_handle = FALSE;
961           event_handled = TRUE;
962         }
963       
964       if (in_handle)
965         {
966           if (event->type == GDK_BUTTON_PRESS) /* Start a drag */
967             {
968               gint desk_x, desk_y;
969               gint root_x, root_y;
970               gint width, height;
971               
972               gdk_window_get_deskrelative_origin (hb->bin_window, &desk_x, &desk_y);
973               gdk_window_get_origin (hb->bin_window, &root_x, &root_y);
974               gdk_window_get_size (hb->bin_window, &width, &height);
975               
976               hb->float_allocation.x = root_x - event->x_root;
977               hb->float_allocation.y = root_y - event->y_root;
978               hb->float_allocation.width = width;
979               hb->float_allocation.height = height;
980               
981               hb->deskoff_x = desk_x - root_x;
982               hb->deskoff_y = desk_y - root_y;
983               
984               gdk_window_get_origin (widget->window, &root_x, &root_y);
985               gdk_window_get_size (widget->window, &width, &height);
986               
987               hb->attach_allocation.x = root_x;
988               hb->attach_allocation.y = root_y;
989               hb->attach_allocation.width = width;
990               hb->attach_allocation.height = height;
991
992               hb->in_drag = TRUE;
993               fleur = gdk_cursor_new_for_screen (gdk_drawable_get_screen (widget->window),
994                                                  GDK_FLEUR);
995               if (gdk_pointer_grab (widget->window,
996                                     FALSE,
997                                     (GDK_BUTTON1_MOTION_MASK |
998                                      GDK_POINTER_MOTION_HINT_MASK |
999                                      GDK_BUTTON_RELEASE_MASK),
1000                                     NULL,
1001                                     fleur,
1002                                     GDK_CURRENT_TIME) != 0)
1003                 {
1004                   hb->in_drag = FALSE;
1005                 }
1006               
1007               gdk_cursor_destroy (fleur);
1008               event_handled = TRUE;
1009             }
1010           else if (hb->child_detached) /* Double click */
1011             {
1012               gtk_handle_box_reattach (hb);
1013             }
1014         }
1015     }
1016   else if (event->type == GDK_BUTTON_RELEASE &&
1017            hb->in_drag)
1018     {
1019       if (event->window != widget->window)
1020         return FALSE;
1021       
1022       gdk_display_pointer_ungrab (gtk_widget_get_display (widget),
1023                                   GDK_CURRENT_TIME);
1024       hb->in_drag = FALSE;
1025       event_handled = TRUE;
1026     }
1027   
1028   return event_handled;
1029 }
1030
1031 static gint
1032 gtk_handle_box_motion (GtkWidget      *widget,
1033                        GdkEventMotion *event)
1034 {
1035   GtkHandleBox *hb;
1036   gint new_x, new_y;
1037   gint snap_edge;
1038   gboolean is_snapped = FALSE;
1039
1040   hb = GTK_HANDLE_BOX (widget);
1041   if (!hb->in_drag)
1042     return FALSE;
1043
1044   if (!hb->in_drag || (event->window != widget->window))
1045     return FALSE;
1046   
1047   /* Calculate the attachment point on the float, if the float
1048    * were detached
1049    */
1050   new_x = 0;
1051   new_y = 0;
1052   gdk_window_get_pointer (gtk_widget_get_root_window (widget), 
1053                           &new_x, &new_y, NULL);
1054   new_x += hb->float_allocation.x;
1055   new_y += hb->float_allocation.y;
1056
1057   snap_edge = hb->snap_edge;
1058   if (snap_edge == -1)
1059     snap_edge = (hb->handle_position == GTK_POS_LEFT ||
1060                   hb->handle_position == GTK_POS_RIGHT) ?
1061       GTK_POS_TOP : GTK_POS_LEFT;
1062
1063   /* First, check if the snapped edge is aligned
1064    */
1065   switch (snap_edge)
1066     {
1067     case GTK_POS_TOP:
1068       is_snapped = abs (hb->attach_allocation.y - new_y) < TOLERANCE;
1069       break;
1070     case GTK_POS_BOTTOM:
1071       is_snapped = abs (hb->attach_allocation.y + (gint)hb->attach_allocation.height -
1072                         new_y - (gint)hb->float_allocation.height) < TOLERANCE;
1073       break;
1074     case GTK_POS_LEFT:
1075       is_snapped = abs (hb->attach_allocation.x - new_x) < TOLERANCE;
1076       break;
1077     case GTK_POS_RIGHT:
1078       is_snapped = abs (hb->attach_allocation.x + (gint)hb->attach_allocation.width -
1079                         new_x - (gint)hb->float_allocation.width) < TOLERANCE;
1080       break;
1081     }
1082
1083   /* Next, check if coordinates in the other direction are sufficiently
1084    * aligned
1085    */
1086   if (is_snapped)
1087     {
1088       gint float_pos1 = 0;      /* Initialize to suppress warnings */
1089       gint float_pos2 = 0;
1090       gint attach_pos1 = 0;
1091       gint attach_pos2 = 0;
1092       
1093       switch (snap_edge)
1094         {
1095         case GTK_POS_TOP:
1096         case GTK_POS_BOTTOM:
1097           attach_pos1 = hb->attach_allocation.x;
1098           attach_pos2 = hb->attach_allocation.x + hb->attach_allocation.width;
1099           float_pos1 = new_x;
1100           float_pos2 = new_x + hb->float_allocation.width;
1101           break;
1102         case GTK_POS_LEFT:
1103         case GTK_POS_RIGHT:
1104           attach_pos1 = hb->attach_allocation.y;
1105           attach_pos2 = hb->attach_allocation.y + hb->attach_allocation.height;
1106           float_pos1 = new_y;
1107           float_pos2 = new_y + hb->float_allocation.height;
1108           break;
1109         }
1110
1111       is_snapped = ((attach_pos1 - TOLERANCE < float_pos1) && 
1112                     (attach_pos2 + TOLERANCE > float_pos2)) ||
1113                    ((float_pos1 - TOLERANCE < attach_pos1) &&
1114                     (float_pos2 + TOLERANCE > attach_pos2));
1115     }
1116
1117   if (is_snapped)
1118     {
1119       if (hb->child_detached)
1120         {
1121           hb->child_detached = FALSE;
1122           gdk_window_hide (hb->float_window);
1123           gdk_window_reparent (hb->bin_window, widget->window, 0, 0);
1124           hb->float_window_mapped = FALSE;
1125           gtk_signal_emit (GTK_OBJECT (hb),
1126                            handle_box_signals[SIGNAL_CHILD_ATTACHED],
1127                            GTK_BIN (hb)->child);
1128           
1129           gtk_widget_queue_resize (widget);
1130         }
1131     }
1132   else
1133     {
1134       gint width, height;
1135
1136       gdk_window_get_size (hb->float_window, &width, &height);
1137       new_x += hb->deskoff_x;
1138       new_y += hb->deskoff_y;
1139
1140       switch (hb->handle_position)
1141         {
1142         case GTK_POS_LEFT:
1143           new_y += ((gint)hb->float_allocation.height - height) / 2;
1144           break;
1145         case GTK_POS_RIGHT:
1146           new_x += (gint)hb->float_allocation.width - width;
1147           new_y += ((gint)hb->float_allocation.height - height) / 2;
1148           break;
1149         case GTK_POS_TOP:
1150           new_x += ((gint)hb->float_allocation.width - width) / 2;
1151           break;
1152         case GTK_POS_BOTTOM:
1153           new_x += ((gint)hb->float_allocation.width - width) / 2;
1154           new_y += (gint)hb->float_allocation.height - height;
1155           break;
1156         }
1157
1158       if (hb->child_detached)
1159         {
1160           gdk_window_move (hb->float_window, new_x, new_y);
1161           gdk_window_raise (hb->float_window);
1162         }
1163       else
1164         {
1165           gint width;
1166           gint height;
1167           GtkRequisition child_requisition;
1168
1169           hb->child_detached = TRUE;
1170
1171           if (GTK_BIN (hb)->child)
1172             gtk_widget_get_child_requisition (GTK_BIN (hb)->child, &child_requisition);
1173           else
1174             {
1175               child_requisition.width = 0;
1176               child_requisition.height = 0;
1177             }      
1178
1179           width = child_requisition.width + 2 * GTK_CONTAINER (hb)->border_width;
1180           height = child_requisition.height + 2 * GTK_CONTAINER (hb)->border_width;
1181
1182           if (hb->handle_position == GTK_POS_LEFT || hb->handle_position == GTK_POS_RIGHT)
1183             width += DRAG_HANDLE_SIZE;
1184           else
1185             height += DRAG_HANDLE_SIZE;
1186           
1187           gdk_window_move_resize (hb->float_window, new_x, new_y, width, height);
1188           gdk_window_reparent (hb->bin_window, hb->float_window, 0, 0);
1189           gdk_window_set_hints (hb->float_window, new_x, new_y, 0, 0, 0, 0, GDK_HINT_POS);
1190           gdk_window_show (hb->float_window);
1191           hb->float_window_mapped = TRUE;
1192 #if     0
1193           /* this extra move is neccessary if we use decorations, or our
1194            * window manager insists on decorations.
1195            */
1196           gdk_display_sync (gtk_widget_get_display (widget));
1197           gdk_window_move (hb->float_window, new_x, new_y);
1198           gdk_display_sync (gtk_widget_get_display (widget));
1199 #endif  /* 0 */
1200           gtk_signal_emit (GTK_OBJECT (hb),
1201                            handle_box_signals[SIGNAL_CHILD_DETACHED],
1202                            GTK_BIN (hb)->child);
1203           gtk_handle_box_draw_ghost (hb);
1204           
1205           gtk_widget_queue_resize (widget);
1206         }
1207     }
1208
1209   return TRUE;
1210 }
1211
1212 static void
1213 gtk_handle_box_add (GtkContainer *container,
1214                     GtkWidget    *widget)
1215 {
1216   gtk_widget_set_parent_window (widget, GTK_HANDLE_BOX (container)->bin_window);
1217   GTK_CONTAINER_CLASS (parent_class)->add (container, widget);
1218 }
1219
1220 static void
1221 gtk_handle_box_remove (GtkContainer *container,
1222                        GtkWidget    *widget)
1223 {
1224   GTK_CONTAINER_CLASS (parent_class)->remove (container, widget);
1225
1226   gtk_handle_box_reattach (GTK_HANDLE_BOX (container));
1227 }
1228
1229 static gint
1230 gtk_handle_box_delete_event (GtkWidget *widget,
1231                              GdkEventAny  *event)
1232 {
1233   GtkHandleBox *hb = GTK_HANDLE_BOX (widget);
1234
1235   if (event->window == hb->float_window)
1236     {
1237       gtk_handle_box_reattach (hb);
1238       
1239       return TRUE;
1240     }
1241
1242   return FALSE;
1243 }
1244
1245 static void
1246 gtk_handle_box_reattach (GtkHandleBox *hb)
1247 {
1248   GtkWidget *widget = GTK_WIDGET (hb);
1249   
1250   if (hb->child_detached)
1251     {
1252       hb->child_detached = FALSE;
1253       if (GTK_WIDGET_REALIZED (hb))
1254         {
1255           gdk_window_hide (hb->float_window);
1256           gdk_window_reparent (hb->bin_window, widget->window, 0, 0);
1257
1258           if (GTK_BIN (hb)->child)
1259             gtk_signal_emit (GTK_OBJECT (hb),
1260                              handle_box_signals[SIGNAL_CHILD_ATTACHED],
1261                              GTK_BIN (hb)->child);
1262
1263         }
1264       hb->float_window_mapped = FALSE;
1265     }
1266   if (hb->in_drag)
1267     {
1268       gdk_display_pointer_ungrab (gtk_widget_get_display (GTK_WIDGET (hb)),
1269                                   GDK_CURRENT_TIME);
1270       hb->in_drag = FALSE;
1271     }
1272
1273   gtk_widget_queue_resize (GTK_WIDGET (hb));
1274 }