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