]> Pileus Git - ~andy/gtk/blob - gtk/gtkhandlebox.c
removed a leftover g_print() call
[~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 Library 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  * Library General Public License for more details.
14  *
15  * You should have received a copy of the GNU Library General Public
16  * License along with this library; if not, write to the Free
17  * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18  */
19
20
21 #include <stdlib.h>
22 #include "gdk/gdkx.h"
23 #include "gtkhandlebox.h"
24 #include "gtkmain.h"
25 #include "gtksignal.h"
26 #include "gtkwindow.h"
27
28
29 #define DRAG_HANDLE_SIZE 10
30 #define CHILDLESS_SIZE  25
31 #define GHOST_HEIGHT 3
32
33 enum
34 {
35   SIGNAL_CHILD_ATTACHED,
36   SIGNAL_CHILD_DETACHED,
37   SIGNAL_LAST
38 };
39
40 typedef void    (*SignalChildAttached)          (GtkObject      *object,
41                                                  GtkWidget      *widget,
42                                                  gpointer        func_data);
43
44 static void gtk_handle_box_class_init     (GtkHandleBoxClass *klass);
45 static void gtk_handle_box_init           (GtkHandleBox      *handle_box);
46 static void gtk_handle_box_destroy        (GtkObject         *object);
47 static void gtk_handle_box_map            (GtkWidget         *widget);
48 static void gtk_handle_box_unmap          (GtkWidget         *widget);
49 static void gtk_handle_box_realize        (GtkWidget         *widget);
50 static void gtk_handle_box_unrealize      (GtkWidget         *widget);
51 static void gtk_handle_box_size_request   (GtkWidget         *widget,
52                                            GtkRequisition    *requisition);
53 static void gtk_handle_box_size_allocate  (GtkWidget         *widget,
54                                            GtkAllocation     *real_allocation);
55 static void gtk_handle_box_add            (GtkContainer      *container,
56                                            GtkWidget         *widget);
57 static void gtk_handle_box_remove         (GtkContainer      *container,
58                                            GtkWidget         *widget);
59 static void gtk_handle_box_draw_ghost     (GtkHandleBox      *hb);
60 static void gtk_handle_box_paint          (GtkWidget         *widget,
61                                            GdkEventExpose    *event,
62                                            GdkRectangle      *area);
63 static void gtk_handle_box_draw           (GtkWidget         *widget,
64                                            GdkRectangle      *area);
65 static gint gtk_handle_box_expose         (GtkWidget         *widget,
66                                            GdkEventExpose    *event);
67 static gint gtk_handle_box_button_changed (GtkWidget         *widget,
68                                            GdkEventButton    *event);
69 static gint gtk_handle_box_motion         (GtkWidget         *widget,
70                                            GdkEventMotion    *event);
71 static gint gtk_handle_box_delete_event   (GtkWidget         *widget,
72                                            GdkEventAny       *event);
73
74
75 static GtkBinClass *parent_class;
76 static guint        handle_box_signals[SIGNAL_LAST] = { 0 };
77
78
79 guint
80 gtk_handle_box_get_type (void)
81 {
82   static guint handle_box_type = 0;
83
84   if (!handle_box_type)
85     {
86       GtkTypeInfo handle_box_info =
87       {
88         "GtkHandleBox",
89         sizeof (GtkHandleBox),
90         sizeof (GtkHandleBoxClass),
91         (GtkClassInitFunc) gtk_handle_box_class_init,
92         (GtkObjectInitFunc) gtk_handle_box_init,
93         (GtkArgSetFunc) NULL,
94         (GtkArgGetFunc) NULL,
95       };
96
97       handle_box_type = gtk_type_unique (gtk_bin_get_type (), &handle_box_info);
98     }
99
100   return handle_box_type;
101 }
102
103 static void
104 gtk_handle_box_marshal_child_attached (GtkObject      *object,
105                                        GtkSignalFunc  func,
106                                        gpointer       func_data,
107                                        GtkArg         *args)
108 {
109   SignalChildAttached sfunc = (SignalChildAttached) func;
110
111   (* sfunc) (object,
112              (GtkWidget*) GTK_VALUE_OBJECT (args[0]),
113              func_data);
114 }
115
116 static void
117 gtk_handle_box_class_init (GtkHandleBoxClass *class)
118 {
119   GtkObjectClass *object_class;
120   GtkWidgetClass *widget_class;
121   GtkContainerClass *container_class;
122
123   object_class = (GtkObjectClass *) class;
124   widget_class = (GtkWidgetClass *) class;
125   container_class = (GtkContainerClass *) class;
126
127   parent_class = gtk_type_class (gtk_bin_get_type ());
128
129   handle_box_signals[SIGNAL_CHILD_ATTACHED] =
130     gtk_signal_new ("child_attached",
131                     GTK_RUN_FIRST,
132                     object_class->type,
133                     GTK_SIGNAL_OFFSET (GtkHandleBoxClass, child_attached),
134                     gtk_handle_box_marshal_child_attached,
135                     GTK_TYPE_NONE, 1,
136                     GTK_TYPE_WIDGET);
137   handle_box_signals[SIGNAL_CHILD_DETACHED] =
138     gtk_signal_new ("child_detached",
139                     GTK_RUN_FIRST,
140                     object_class->type,
141                     GTK_SIGNAL_OFFSET (GtkHandleBoxClass, child_detached),
142                     gtk_handle_box_marshal_child_attached,
143                     GTK_TYPE_NONE, 1,
144                     GTK_TYPE_WIDGET);
145   gtk_object_class_add_signals (object_class, handle_box_signals, SIGNAL_LAST);
146   
147   object_class->destroy = gtk_handle_box_destroy;
148
149   widget_class->map = gtk_handle_box_map;
150   widget_class->unmap = gtk_handle_box_unmap;
151   widget_class->realize = gtk_handle_box_realize;
152   widget_class->unrealize = gtk_handle_box_unrealize;
153   widget_class->size_request = gtk_handle_box_size_request;
154   widget_class->size_allocate = gtk_handle_box_size_allocate;
155   widget_class->draw = gtk_handle_box_draw;
156   widget_class->expose_event = gtk_handle_box_expose;
157   widget_class->button_press_event = gtk_handle_box_button_changed;
158   widget_class->button_release_event = gtk_handle_box_button_changed;
159   widget_class->motion_notify_event = gtk_handle_box_motion;
160   widget_class->delete_event = gtk_handle_box_delete_event;
161
162   container_class->add = gtk_handle_box_add;
163   container_class->remove = gtk_handle_box_remove;
164
165   class->child_attached = NULL;
166   class->child_detached = NULL;
167 }
168
169 static void
170 gtk_handle_box_init (GtkHandleBox *handle_box)
171 {
172   GTK_WIDGET_UNSET_FLAGS (handle_box, GTK_NO_WINDOW);
173   GTK_WIDGET_SET_FLAGS (handle_box, GTK_BASIC); /* FIXME: are we really a basic widget? */
174
175   handle_box->bin_window = NULL;
176   handle_box->float_window = NULL;
177   handle_box->handle_position = GTK_POS_LEFT;
178   handle_box->float_window_mapped = FALSE;
179   handle_box->child_detached = FALSE;
180   handle_box->in_drag = FALSE;
181   handle_box->shrink_on_detach = TRUE;
182   handle_box->fleur_cursor = gdk_cursor_new (GDK_FLEUR);
183   handle_box->dragoff_x = 0;
184   handle_box->dragoff_y = 0;
185 }
186
187 GtkWidget*
188 gtk_handle_box_new (void)
189 {
190   return GTK_WIDGET (gtk_type_new (gtk_handle_box_get_type ()));
191 }
192
193 static void
194 gtk_handle_box_destroy (GtkObject *object)
195 {
196   GtkHandleBox *hb;
197
198   g_return_if_fail (object != NULL);
199   g_return_if_fail (GTK_IS_HANDLE_BOX (object));
200
201   hb = GTK_HANDLE_BOX (object);
202
203   if (GTK_OBJECT_CLASS (parent_class)->destroy)
204     (* GTK_OBJECT_CLASS (parent_class)->destroy) (object);
205 }
206
207 static void
208 gtk_handle_box_map (GtkWidget *widget)
209 {
210   GtkBin *bin;
211   GtkHandleBox *hb;
212
213   g_return_if_fail (widget != NULL);
214   g_return_if_fail (GTK_IS_HANDLE_BOX (widget));
215
216   GTK_WIDGET_SET_FLAGS (widget, GTK_MAPPED);
217
218   bin = GTK_BIN (widget);
219   hb = GTK_HANDLE_BOX (widget);
220
221   gdk_window_show (hb->bin_window);
222   gdk_window_show (widget->window);
223
224   if (hb->child_detached && !hb->float_window_mapped)
225     {
226       gdk_window_show (hb->float_window);
227       hb->float_window_mapped = TRUE;
228     }
229
230   if (bin->child &&
231       GTK_WIDGET_VISIBLE (bin->child) &&
232       !GTK_WIDGET_MAPPED (bin->child))
233     gtk_widget_map (bin->child);
234 }
235
236 static void
237 gtk_handle_box_unmap (GtkWidget *widget)
238 {
239   GtkHandleBox *hb;
240
241   g_return_if_fail (widget != NULL);
242   g_return_if_fail (GTK_IS_HANDLE_BOX (widget));
243
244   GTK_WIDGET_UNSET_FLAGS (widget, GTK_MAPPED);
245
246   hb = GTK_HANDLE_BOX (widget);
247
248   gdk_window_hide (widget->window);
249   if (hb->float_window_mapped)
250     {
251       gdk_window_hide (hb->float_window);
252       hb->float_window_mapped = FALSE;
253     }
254 }
255
256 static void
257 gtk_handle_box_realize (GtkWidget *widget)
258 {
259   GdkWindowAttr attributes;
260   gint attributes_mask;
261   GtkHandleBox *hb;
262
263   g_return_if_fail (widget != NULL);
264   g_return_if_fail (GTK_IS_HANDLE_BOX (widget));
265
266   hb = GTK_HANDLE_BOX (widget);
267
268   GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
269
270   attributes.x = widget->allocation.x;
271   attributes.y = widget->allocation.y;
272   attributes.width = widget->allocation.width;
273   attributes.height = widget->allocation.height;
274   attributes.window_type = GDK_WINDOW_CHILD;
275   attributes.wclass = GDK_INPUT_OUTPUT;
276   attributes.visual = gtk_widget_get_visual (widget);
277   attributes.colormap = gtk_widget_get_colormap (widget);
278   attributes.event_mask = (gtk_widget_get_events (widget)
279                            | GDK_EXPOSURE_MASK);
280   attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
281   widget->window = gdk_window_new (gtk_widget_get_parent_window (widget), &attributes, attributes_mask);
282   gdk_window_set_user_data (widget->window, widget);
283
284   attributes.x = 0;
285   attributes.y = 0;
286   attributes.width = widget->allocation.width;
287   attributes.height = widget->allocation.height;
288   attributes.window_type = GDK_WINDOW_CHILD;
289   attributes.event_mask |= (gtk_widget_get_events (widget) |
290                             GDK_EXPOSURE_MASK |
291                             GDK_BUTTON1_MOTION_MASK |
292                             GDK_POINTER_MOTION_HINT_MASK |
293                             GDK_BUTTON_PRESS_MASK |
294                             GDK_BUTTON_RELEASE_MASK);
295   attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
296   hb->bin_window = gdk_window_new (widget->window, &attributes, attributes_mask);
297   gdk_window_set_user_data (hb->bin_window, widget);
298   if (GTK_BIN (hb)->child)
299     gtk_widget_set_parent_window (GTK_BIN (hb)->child, hb->bin_window);
300   
301   attributes.x = 0;
302   attributes.y = 0;
303   attributes.width = widget->requisition.width;
304   attributes.height = widget->requisition.height;
305   attributes.window_type = GDK_WINDOW_TOPLEVEL;
306   attributes.wclass = GDK_INPUT_OUTPUT;
307   attributes.visual = gtk_widget_get_visual (widget);
308   attributes.colormap = gtk_widget_get_colormap (widget);
309   attributes.event_mask = (gtk_widget_get_events (widget) |
310                            GDK_KEY_PRESS_MASK |
311                            GDK_ENTER_NOTIFY_MASK |
312                            GDK_LEAVE_NOTIFY_MASK |
313                            GDK_FOCUS_CHANGE_MASK |
314                            GDK_STRUCTURE_MASK);
315   attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
316   hb->float_window = gdk_window_new (NULL, &attributes, attributes_mask);
317   gdk_window_set_user_data (hb->float_window, widget);
318   gdk_window_set_decorations (hb->float_window, 0);
319   
320
321   widget->style = gtk_style_attach (widget->style, widget->window);
322   gtk_style_set_background (widget->style, widget->window, GTK_WIDGET_STATE (hb));
323   gtk_style_set_background (widget->style, hb->bin_window, GTK_WIDGET_STATE (hb));
324   gtk_style_set_background (widget->style, hb->float_window, GTK_WIDGET_STATE (hb));
325 }
326
327 static void
328 gtk_handle_box_unrealize (GtkWidget *widget)
329 {
330   GtkHandleBox *hb;
331
332   g_return_if_fail (widget != NULL);
333   g_return_if_fail (GTK_IS_HANDLE_BOX (widget));
334
335   hb = GTK_HANDLE_BOX (widget);
336
337   gdk_window_set_user_data (hb->bin_window, NULL);
338   gdk_window_destroy (hb->bin_window);
339   hb->bin_window = NULL;
340   gdk_window_set_user_data (hb->float_window, NULL);
341   gdk_window_destroy (hb->float_window);
342   hb->float_window = NULL;
343
344   gdk_cursor_destroy (hb->fleur_cursor);
345   hb->fleur_cursor = NULL;
346
347   if (GTK_WIDGET_CLASS (parent_class)->unrealize)
348     (* GTK_WIDGET_CLASS (parent_class)->unrealize) (widget);
349 }
350
351 static void
352 gtk_handle_box_size_request (GtkWidget      *widget,
353                              GtkRequisition *requisition)
354 {
355   GtkBin *bin;
356   GtkHandleBox *hb;
357
358   g_return_if_fail (widget != NULL);
359   g_return_if_fail (GTK_IS_HANDLE_BOX (widget));
360   g_return_if_fail (requisition != NULL);
361
362   bin = GTK_BIN (widget);
363   hb = GTK_HANDLE_BOX (widget);
364
365   if (hb->handle_position == GTK_POS_LEFT ||
366       hb->handle_position == GTK_POS_RIGHT)
367     {
368       requisition->width = DRAG_HANDLE_SIZE;
369       requisition->height = 0;
370     }
371   else
372     {
373       requisition->width = 0;
374       requisition->height = DRAG_HANDLE_SIZE;
375     }
376
377   /* if our child is not visible, we still request its size, since we
378    * won't have any usefull hint for our size otherwise.
379    */
380   if (bin->child)
381     gtk_widget_size_request (bin->child, &bin->child->requisition);
382
383   if (hb->child_detached)
384     {
385       if (!hb->shrink_on_detach)
386         {
387           if (hb->handle_position == GTK_POS_LEFT ||
388               hb->handle_position == GTK_POS_RIGHT)
389             requisition->height += bin->child->requisition.height;
390           else
391             requisition->width += bin->child->requisition.width;
392         }
393       else
394         {
395           if (hb->handle_position == GTK_POS_LEFT ||
396               hb->handle_position == GTK_POS_RIGHT)
397             requisition->height += widget->style->klass->ythickness;
398           else
399             requisition->width += widget->style->klass->xthickness;
400         }
401     }
402   else
403     {
404       requisition->width += GTK_CONTAINER (widget)->border_width * 2;
405       requisition->height += GTK_CONTAINER (widget)->border_width * 2;
406       
407       if (bin->child)
408         {
409           requisition->width += bin->child->requisition.width;
410           requisition->height += bin->child->requisition.height;
411         }
412       else
413         {
414           requisition->width += CHILDLESS_SIZE;
415           requisition->height += CHILDLESS_SIZE;
416         }
417     }
418 }
419
420 static void
421 gtk_handle_box_size_allocate (GtkWidget     *widget,
422                               GtkAllocation *allocation)
423 {
424   GtkBin *bin;
425   GtkHandleBox *hb;
426   
427   g_return_if_fail (widget != NULL);
428   g_return_if_fail (GTK_IS_HANDLE_BOX (widget));
429   g_return_if_fail (allocation != NULL);
430   
431   bin = GTK_BIN (widget);
432   hb = GTK_HANDLE_BOX (widget);
433   
434   widget->allocation.x = allocation->x;
435
436   if (hb->child_detached)
437     {
438       guint max_req_height;
439       guint max_req_width;
440
441       max_req_height = MAX (widget->requisition.height,
442                             bin->child->requisition.height +
443                             2 * widget->style->klass->ythickness);
444       max_req_width = MAX (widget->requisition.width,
445                            bin->child->requisition.width +
446                            2 * widget->style->klass->xthickness);
447       
448       if (allocation->height > max_req_height)
449         widget->allocation.y = allocation->y +
450           (allocation->height - max_req_height) / 2;
451       else
452         widget->allocation.y = allocation->y;
453       
454       widget->allocation.height = MIN (allocation->height, max_req_height);
455       widget->allocation.width = MIN (allocation->width, max_req_width);
456     }
457   else
458     {
459       widget->allocation = *allocation;
460     }  
461
462   if (GTK_WIDGET_REALIZED (hb))
463     gdk_window_move_resize (widget->window,
464                             widget->allocation.x,
465                             widget->allocation.y,
466                             widget->allocation.width,
467                             widget->allocation.height);
468
469
470   if (bin->child && GTK_WIDGET_VISIBLE (bin->child))
471     {
472       GtkWidget *child;
473       GtkAllocation child_allocation;
474       guint border_width;
475
476       child = bin->child;
477       border_width = GTK_CONTAINER (widget)->border_width;
478
479       child_allocation.x = border_width;
480       child_allocation.y = border_width;
481       if (hb->handle_position == GTK_POS_LEFT)
482         child_allocation.x += DRAG_HANDLE_SIZE;
483       else if (hb->handle_position == GTK_POS_TOP)
484         child_allocation.y += DRAG_HANDLE_SIZE;
485
486       if (hb->child_detached)
487         {
488           guint float_width;
489           guint float_height;
490           
491           child_allocation.width = child->requisition.width;
492           child_allocation.height = child->requisition.height;
493           
494           float_width = child_allocation.width + 2 * border_width;
495           float_height = child_allocation.height + 2 * border_width;
496           
497           if (hb->handle_position == GTK_POS_LEFT ||
498               hb->handle_position == GTK_POS_RIGHT)
499             float_width += DRAG_HANDLE_SIZE;
500           else
501             float_height += DRAG_HANDLE_SIZE;
502
503           if (GTK_WIDGET_REALIZED (hb))
504             {
505               gdk_window_resize (hb->float_window,
506                                  float_width,
507                                  float_height);
508               gdk_window_move_resize (hb->bin_window,
509                                       0,
510                                       0,
511                                       float_width,
512                                       float_height);
513             }
514         }
515       else
516         {
517           child_allocation.width = widget->allocation.width - 2 * border_width;
518           child_allocation.height = widget->allocation.height - 2 * border_width;
519
520           if (hb->handle_position == GTK_POS_LEFT ||
521               hb->handle_position == GTK_POS_RIGHT)
522             child_allocation.width -= DRAG_HANDLE_SIZE;
523           else
524             child_allocation.height -= DRAG_HANDLE_SIZE;
525           
526           if (GTK_WIDGET_REALIZED (hb))
527             gdk_window_move_resize (hb->bin_window,
528                                     0,
529                                     0,
530                                     widget->allocation.width,
531                                     widget->allocation.height);
532         }
533
534       gtk_widget_size_allocate (bin->child, &child_allocation);
535     }
536 }
537
538 static void
539 gtk_handle_box_draw_ghost (GtkHandleBox *hb)
540 {
541   GtkWidget *widget;
542   guint x;
543   guint y;
544   guint width;
545   guint height;
546
547   widget = GTK_WIDGET (hb);
548
549   if (hb->handle_position == GTK_POS_LEFT ||
550       hb->handle_position == GTK_POS_RIGHT)
551     {
552       x = hb->handle_position == GTK_POS_LEFT ? 0 : widget->allocation.width;
553       y = 0;
554       width = DRAG_HANDLE_SIZE;
555       height = widget->allocation.height;
556     }
557   else
558     {
559       x = 0;
560       y = hb->handle_position == GTK_POS_TOP ? 0 : widget->allocation.height;
561       width = widget->allocation.width;
562       height = DRAG_HANDLE_SIZE;
563     }
564   gtk_draw_shadow (widget->style,
565                    widget->window,
566                    GTK_WIDGET_STATE (widget),
567                    GTK_SHADOW_ETCHED_IN,
568                    x,
569                    y,
570                    width,
571                    height);
572   /*
573   if (hb->handle_position == GTK_POS_LEFT ||
574       hb->handle_position == GTK_POS_RIGHT)
575     gtk_draw_hline (widget->style,
576                     widget->window,
577                     GTK_WIDGET_STATE (widget),
578                     hb->handle_position == GTK_POS_LEFT ? DRAG_HANDLE_SIZE : widget->allocation.width - DRAG_HANDLE_SIZE,
579                     widget->allocation.width - DRAG_HANDLE_SIZE,
580                     widget->allocation.height / 2);
581   else
582     gtk_draw_vline (widget->style,
583                     widget->window,
584                     GTK_WIDGET_STATE (widget),
585                     hb->handle_position == GTK_POS_TOP ? DRAG_HANDLE_SIZE : widget->allocation.height - DRAG_HANDLE_SIZE,
586                     widget->allocation.height - DRAG_HANDLE_SIZE,
587                     widget->allocation.width / 2);
588                     */
589 }
590
591 static void
592 gtk_handle_box_paint (GtkWidget      *widget,
593                       GdkEventExpose *event,
594                       GdkRectangle   *area)
595 {
596   GtkBin *bin;
597   GtkHandleBox *hb;
598   guint width;
599   guint height;
600   guint border_width;
601
602   bin = GTK_BIN (widget);
603   hb = GTK_HANDLE_BOX (widget);
604
605   border_width = GTK_CONTAINER (hb)->border_width;
606
607   if (hb->child_detached)
608     {
609       width = bin->child->allocation.width + 2 * border_width;
610       height = bin->child->allocation.height + 2 * border_width;
611     }
612   else if (hb->handle_position == GTK_POS_LEFT ||
613            hb->handle_position == GTK_POS_RIGHT)
614     {
615       width = widget->allocation.width - DRAG_HANDLE_SIZE;
616       height = widget->allocation.height;
617     }
618   else
619     {
620       width = widget->allocation.width;
621       height = widget->allocation.height - DRAG_HANDLE_SIZE;
622     }
623
624   gdk_window_clear (hb->bin_window);
625   gtk_draw_shadow (widget->style,
626                    hb->bin_window,
627                    GTK_WIDGET_STATE (widget),
628                    GTK_SHADOW_OUT,
629                    hb->handle_position == GTK_POS_LEFT ? DRAG_HANDLE_SIZE : 0,
630                    hb->handle_position == GTK_POS_TOP ? DRAG_HANDLE_SIZE : 0,
631                    width,
632                    height);
633
634   if (hb->handle_position == GTK_POS_LEFT ||
635       hb->handle_position == GTK_POS_RIGHT)
636     {
637       guint x;
638
639       for (x = 1; x < DRAG_HANDLE_SIZE; x += 3)
640         
641         gtk_draw_vline (widget->style,
642                         hb->bin_window,
643                         GTK_WIDGET_STATE (widget),
644                         widget->style->klass->ythickness,
645                         height + DRAG_HANDLE_SIZE - widget->style->klass->ythickness,
646                         hb->handle_position == GTK_POS_LEFT ? x : width + x);
647       gtk_draw_hline (widget->style,
648                       hb->bin_window,
649                       GTK_WIDGET_STATE (widget),
650                       hb->handle_position == GTK_POS_LEFT ? DRAG_HANDLE_SIZE : width,
651                       hb->handle_position == GTK_POS_LEFT ? 0 : width + DRAG_HANDLE_SIZE,
652                       0);
653       gtk_draw_hline (widget->style,
654                       hb->bin_window,
655                       GTK_WIDGET_STATE (widget),
656                       hb->handle_position == GTK_POS_LEFT ? DRAG_HANDLE_SIZE : width,
657                       hb->handle_position == GTK_POS_LEFT ? 0 : width + DRAG_HANDLE_SIZE,
658                       height - widget->style->klass->ythickness);
659     }
660   else
661     {
662       guint y;
663
664       for (y = 1; y < DRAG_HANDLE_SIZE; y += 3)
665         
666         gtk_draw_hline (widget->style,
667                         hb->bin_window,
668                         GTK_WIDGET_STATE (widget),
669                         widget->style->klass->xthickness,
670                         width + DRAG_HANDLE_SIZE - widget->style->klass->xthickness,
671                         hb->handle_position == GTK_POS_TOP ? y : height + y);
672       gtk_draw_vline (widget->style,
673                       hb->bin_window,
674                       GTK_WIDGET_STATE (widget),
675                       hb->handle_position == GTK_POS_TOP ? DRAG_HANDLE_SIZE : height,
676                       hb->handle_position == GTK_POS_TOP ? 0 : height + DRAG_HANDLE_SIZE,
677                       0);
678       gtk_draw_vline (widget->style,
679                       hb->bin_window,
680                       GTK_WIDGET_STATE (widget),
681                       hb->handle_position == GTK_POS_TOP ? DRAG_HANDLE_SIZE : height,
682                       hb->handle_position == GTK_POS_TOP ? 0 : height + DRAG_HANDLE_SIZE,
683                       width - widget->style->klass->xthickness);
684     }
685   
686
687   if (bin->child && GTK_WIDGET_VISIBLE (bin->child))
688     {
689       GdkRectangle child_area;
690       GdkEventExpose child_event;
691
692       if (!event) /* we were called from draw() */
693         {
694           if (gtk_widget_intersect (bin->child, area, &child_area))
695             gtk_widget_draw (bin->child, &child_area);
696         }
697       else /* we were called from expose() */
698         {
699           child_event = *event;
700           
701           if (GTK_WIDGET_NO_WINDOW (bin->child) &&
702               gtk_widget_intersect (bin->child, &event->area, &child_event.area))
703             gtk_widget_event (bin->child, (GdkEvent *) &child_event);
704         }
705     }
706 }
707
708 static void
709 gtk_handle_box_draw (GtkWidget    *widget,
710                      GdkRectangle *area)
711 {
712   GtkHandleBox *hb;
713
714   g_return_if_fail (widget != NULL);
715   g_return_if_fail (GTK_IS_HANDLE_BOX (widget));
716   g_return_if_fail (area != NULL);
717
718   hb = GTK_HANDLE_BOX (widget);
719
720   if (GTK_WIDGET_DRAWABLE (widget))
721     {
722       if (hb->child_detached)
723         {
724           /* The area parameter does not make sense in this case, so we
725            * repaint everything.
726            */
727
728           gtk_handle_box_draw_ghost (hb);
729
730           area->x = 0;
731           area->y = 0;
732           area->width = 2 * GTK_CONTAINER (hb)->border_width + DRAG_HANDLE_SIZE;
733           area->height = area->width + GTK_BIN (hb)->child->allocation.height;
734           area->width += GTK_BIN (hb)->child->allocation.width;
735
736           gtk_handle_box_paint (widget, NULL, area);
737         }
738       else
739         gtk_handle_box_paint (widget, NULL, area);
740     }
741 }
742
743 static gint
744 gtk_handle_box_expose (GtkWidget      *widget,
745                        GdkEventExpose *event)
746 {
747   GtkHandleBox *hb;
748
749   g_return_val_if_fail (widget != NULL, FALSE);
750   g_return_val_if_fail (GTK_IS_HANDLE_BOX (widget), FALSE);
751   g_return_val_if_fail (event != NULL, FALSE);
752
753   if (GTK_WIDGET_DRAWABLE (widget))
754     {
755       hb = GTK_HANDLE_BOX (widget);
756
757       if (event->window == widget->window)
758         {
759           if (hb->child_detached)
760             gtk_handle_box_draw_ghost (hb);
761         }
762       else
763         gtk_handle_box_paint (widget, event, NULL);
764     }
765   
766   return FALSE;
767 }
768
769 static gint
770 gtk_handle_box_button_changed (GtkWidget      *widget,
771                                GdkEventButton *event)
772 {
773   GtkHandleBox *hb;
774   gboolean event_handled;
775   
776   g_return_val_if_fail (widget != NULL, FALSE);
777   g_return_val_if_fail (GTK_IS_HANDLE_BOX (widget), FALSE);
778   g_return_val_if_fail (event != NULL, FALSE);
779
780   hb = GTK_HANDLE_BOX (widget);
781
782   event_handled = FALSE;
783   if (event->button == 1 &&
784       event->type == GDK_BUTTON_PRESS)
785     {
786       GtkWidget *child;
787       gboolean in_handle;
788       
789       child = GTK_BIN (hb)->child;
790       
791       switch (hb->handle_position)
792         {
793         case GTK_POS_LEFT:
794           in_handle = event->x < DRAG_HANDLE_SIZE;
795           break;
796         case GTK_POS_TOP:
797           in_handle = event->y < DRAG_HANDLE_SIZE;
798           break;
799         case GTK_POS_RIGHT:
800           in_handle = event->x > 2 * GTK_CONTAINER (hb)->border_width + child->allocation.width;
801           break;
802         case GTK_POS_BOTTOM:
803           in_handle = event->y > 2 * GTK_CONTAINER (hb)->border_width + child->allocation.height;
804           break;
805         default:
806           in_handle = FALSE;
807           break;
808         }
809
810       if (!child)
811         {
812           in_handle = FALSE;
813           event_handled = TRUE;
814         }
815       
816       if (in_handle)
817         {
818           hb->dragoff_x = event->x;
819           hb->dragoff_y = event->y;
820
821           gtk_grab_add (widget);
822           hb->in_drag = TRUE;
823           while (gdk_pointer_grab (hb->bin_window,
824                                    FALSE,
825                                    (GDK_BUTTON1_MOTION_MASK |
826                                     GDK_POINTER_MOTION_HINT_MASK |
827                                     GDK_BUTTON_RELEASE_MASK),
828                                    NULL,
829                                    hb->fleur_cursor,
830                                    GDK_CURRENT_TIME) != 0); /* wait for success */
831           event_handled = TRUE;
832         }
833     }
834   else if (event->type == GDK_BUTTON_RELEASE &&
835            hb->in_drag)
836     {
837       gdk_pointer_ungrab (GDK_CURRENT_TIME);
838       gtk_grab_remove (widget);
839       hb->in_drag = FALSE;
840       event_handled = TRUE;
841     }
842   
843   return event_handled;
844 }
845
846 static gint
847 gtk_handle_box_motion (GtkWidget      *widget,
848                        GdkEventMotion *event)
849 {
850   GtkHandleBox *hb;
851   gint new_x, new_y;
852   gint ox, oy;
853   gint snap_x, snap_y;
854   gboolean in_handle;
855
856   g_return_val_if_fail (widget != NULL, FALSE);
857   g_return_val_if_fail (GTK_IS_HANDLE_BOX (widget), FALSE);
858   g_return_val_if_fail (event != NULL, FALSE);
859
860   hb = GTK_HANDLE_BOX (widget);
861   if (!hb->in_drag)
862     return FALSE;
863
864   ox = 0;
865   oy = 0;
866   gdk_window_get_origin (hb->float_window, &ox, &oy);
867   new_x = 0;
868   new_y = 0;
869   gdk_window_get_pointer (hb->float_window, &new_x, &new_y, NULL);
870   new_x += ox;
871   new_y += oy;
872   new_x -= hb->dragoff_x;
873   new_y -= hb->dragoff_y;
874   snap_x = 0;
875   snap_y = 0;
876   gdk_window_get_pointer (widget->window, &snap_x, &snap_y, NULL);
877   
878   switch (hb->handle_position)
879     {
880     case GTK_POS_LEFT:
881       in_handle = (snap_x >= 0 && snap_x <= DRAG_HANDLE_SIZE &&
882                    snap_y >= 0 && snap_y <= widget->allocation.height);
883       break;
884     case GTK_POS_TOP:
885       in_handle = (snap_x >= 0 && snap_x <= widget->allocation.width &&
886                    snap_y >= 0 && snap_y <= DRAG_HANDLE_SIZE);
887       break;
888     case GTK_POS_RIGHT:
889       in_handle = (snap_x >= widget->allocation.width - DRAG_HANDLE_SIZE && snap_x <= widget->allocation.width &&
890                    snap_y >= 0 && snap_y <= widget->allocation.height);
891       break;
892     case GTK_POS_BOTTOM:
893       in_handle = (snap_x >= 0 && snap_x <= widget->allocation.width &&
894                    snap_y >= widget->allocation.height - DRAG_HANDLE_SIZE && snap_y <= widget->allocation.height);
895       break;
896     default:
897       in_handle = FALSE;
898       break;
899     }
900
901   if (in_handle)
902     {
903       if (hb->child_detached)
904         {
905           gdk_pointer_ungrab (GDK_CURRENT_TIME);
906           
907           hb->child_detached = FALSE;
908           gdk_window_hide (hb->float_window);
909           gdk_window_reparent (hb->bin_window, widget->window, 0, 0);
910           hb->float_window_mapped = FALSE;
911           gtk_signal_emit (GTK_OBJECT (hb),
912                            handle_box_signals[SIGNAL_CHILD_ATTACHED],
913                            GTK_BIN (hb)->child);
914           
915           while (gdk_pointer_grab (hb->bin_window,
916                                    FALSE,
917                                    (GDK_BUTTON1_MOTION_MASK |
918                                     GDK_POINTER_MOTION_HINT_MASK |
919                                     GDK_BUTTON_RELEASE_MASK),
920                                    NULL,
921                                    hb->fleur_cursor,
922                                    GDK_CURRENT_TIME) != 0); /* wait for success */
923           
924           gtk_widget_queue_resize (widget);
925         }
926     }
927   else
928     {
929       if (hb->child_detached)
930         {
931           gdk_window_move (hb->float_window, new_x, new_y);
932           gdk_window_raise (hb->float_window);
933         }
934       else
935         {
936           gint width;
937           gint height;
938
939           gdk_pointer_ungrab (GDK_CURRENT_TIME);
940
941           hb->child_detached = TRUE;
942           width = 0;
943           height = 0;
944           gdk_window_get_size (hb->bin_window, &width, &height);
945           gdk_window_move_resize (hb->float_window, new_x, new_y, width, height);
946           gdk_window_reparent (hb->bin_window, hb->float_window, 0, 0);
947           gdk_window_set_hints (hb->float_window, new_x, new_y, 0, 0, 0, 0, GDK_HINT_POS);
948           gdk_window_show (hb->float_window);
949           hb->float_window_mapped = TRUE;
950 #if     0
951           /* this extra move is neccessary if we use decorations, or our
952            * window manager insists on decorations.
953            */
954           gdk_flush ();
955           gdk_window_move (hb->float_window, new_x, new_y);
956           gdk_flush ();
957 #endif  /* 0 */
958           gtk_signal_emit (GTK_OBJECT (hb),
959                            handle_box_signals[SIGNAL_CHILD_DETACHED],
960                            GTK_BIN (hb)->child);
961           gtk_handle_box_draw_ghost (hb);
962           
963           while (gdk_pointer_grab (hb->bin_window,
964                                    FALSE,
965                                    (GDK_BUTTON1_MOTION_MASK |
966                                     GDK_POINTER_MOTION_HINT_MASK |
967                                     GDK_BUTTON_RELEASE_MASK),
968                                    NULL,
969                                    hb->fleur_cursor,
970                                    GDK_CURRENT_TIME) != 0); /* wait for success */
971           
972           gtk_widget_queue_resize (widget);
973         }
974     }
975
976   return TRUE;
977 }
978
979 static void
980 gtk_handle_box_add (GtkContainer *container,
981                     GtkWidget    *widget)
982 {
983   g_return_if_fail (GTK_IS_HANDLE_BOX (container));
984   g_return_if_fail (GTK_BIN (container)->child == NULL);
985   g_return_if_fail (widget->parent == NULL);
986
987   gtk_widget_set_parent_window (widget, GTK_HANDLE_BOX (container)->bin_window);
988   GTK_CONTAINER_CLASS (parent_class)->add (container, widget);
989 }
990
991 static void
992 gtk_handle_box_remove (GtkContainer *container,
993                        GtkWidget    *widget)
994 {
995   GtkHandleBox *hb;
996
997   g_return_if_fail (GTK_IS_HANDLE_BOX (container));
998   g_return_if_fail (GTK_BIN (container)->child == widget);
999
1000   hb = GTK_HANDLE_BOX (container);
1001
1002   if (hb->child_detached)
1003     {
1004       hb->child_detached = FALSE;
1005       if (GTK_WIDGET_REALIZED (hb))
1006         {
1007           gdk_window_hide (hb->float_window);
1008           gdk_window_reparent (hb->bin_window, GTK_WIDGET (hb)->window, 0, 0);
1009         }
1010       hb->float_window_mapped = FALSE;
1011     }
1012   if (hb->in_drag)
1013     {
1014       gdk_pointer_ungrab (GDK_CURRENT_TIME);
1015       gtk_grab_remove (GTK_WIDGET (container));
1016       hb->in_drag = FALSE;
1017     }
1018   
1019   GTK_CONTAINER_CLASS (parent_class)->remove (container, widget);
1020 }
1021
1022 static gint
1023 gtk_handle_box_delete_event (GtkWidget *widget,
1024                              GdkEventAny  *event)
1025 {
1026   GtkHandleBox *hb;
1027
1028   g_return_val_if_fail (widget != NULL, FALSE);
1029   g_return_val_if_fail (GTK_IS_HANDLE_BOX (widget), FALSE);
1030   g_return_val_if_fail (event != NULL, FALSE);
1031
1032   hb = GTK_HANDLE_BOX (widget);
1033   g_return_val_if_fail (event->window == hb->float_window, FALSE);
1034   
1035   hb->child_detached = FALSE;
1036   gdk_window_hide (hb->float_window);
1037   gdk_window_reparent (hb->bin_window, widget->window, 0, 0);
1038   hb->float_window_mapped = FALSE;
1039   gtk_signal_emit (GTK_OBJECT (hb),
1040                    handle_box_signals[SIGNAL_CHILD_ATTACHED],
1041                    GTK_BIN (hb)->child);
1042   if (hb->in_drag)
1043     {
1044       gdk_pointer_ungrab (GDK_CURRENT_TIME);
1045       gtk_grab_remove (widget);
1046       hb->in_drag = FALSE;
1047     }
1048   
1049   gtk_widget_queue_resize (GTK_WIDGET (hb));
1050
1051   return TRUE;
1052 }