]> Pileus Git - ~andy/gtk/blob - gtk/gtkhandlebox.c
major overhaul of the code, fixed all known bugs (hopefully ;).
[~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->fleur_cursor = gdk_cursor_new (GDK_FLEUR);
182   handle_box->dragoff_x = 0;
183   handle_box->dragoff_y = 0;
184 }
185
186 GtkWidget*
187 gtk_handle_box_new (void)
188 {
189   return GTK_WIDGET (gtk_type_new (gtk_handle_box_get_type ()));
190 }
191
192 static void
193 gtk_handle_box_destroy (GtkObject *object)
194 {
195   GtkHandleBox *hb;
196
197   g_return_if_fail (object != NULL);
198   g_return_if_fail (GTK_IS_HANDLE_BOX (object));
199
200   hb = GTK_HANDLE_BOX (object);
201
202   if (GTK_OBJECT_CLASS (parent_class)->destroy)
203     (* GTK_OBJECT_CLASS (parent_class)->destroy) (object);
204 }
205
206 static void
207 gtk_handle_box_map (GtkWidget *widget)
208 {
209   GtkBin *bin;
210   GtkHandleBox *hb;
211
212   g_return_if_fail (widget != NULL);
213   g_return_if_fail (GTK_IS_HANDLE_BOX (widget));
214
215   GTK_WIDGET_SET_FLAGS (widget, GTK_MAPPED);
216
217   bin = GTK_BIN (widget);
218   hb = GTK_HANDLE_BOX (widget);
219
220   gdk_window_show (hb->bin_window);
221   gdk_window_show (widget->window);
222
223   if (hb->child_detached && !hb->float_window_mapped)
224     {
225       gdk_window_show (hb->float_window);
226       hb->float_window_mapped = TRUE;
227     }
228
229   if (bin->child &&
230       GTK_WIDGET_VISIBLE (bin->child) &&
231       !GTK_WIDGET_MAPPED (bin->child))
232     gtk_widget_map (bin->child);
233 }
234
235 static void
236 gtk_handle_box_unmap (GtkWidget *widget)
237 {
238   GtkHandleBox *hb;
239
240   g_return_if_fail (widget != NULL);
241   g_return_if_fail (GTK_IS_HANDLE_BOX (widget));
242
243   GTK_WIDGET_UNSET_FLAGS (widget, GTK_MAPPED);
244
245   hb = GTK_HANDLE_BOX (widget);
246
247   gdk_window_hide (widget->window);
248   if (hb->float_window_mapped)
249     {
250       gdk_window_hide (hb->float_window);
251       hb->float_window_mapped = FALSE;
252     }
253 }
254
255 static void
256 gtk_handle_box_realize (GtkWidget *widget)
257 {
258   GdkWindowAttr attributes;
259   gint attributes_mask;
260   GtkHandleBox *hb;
261
262   g_return_if_fail (widget != NULL);
263   g_return_if_fail (GTK_IS_HANDLE_BOX (widget));
264
265   hb = GTK_HANDLE_BOX (widget);
266
267   GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
268
269   attributes.x = widget->allocation.x;
270   attributes.y = widget->allocation.y;
271   attributes.width = widget->allocation.width;
272   attributes.height = widget->allocation.height;
273   attributes.window_type = GDK_WINDOW_CHILD;
274   attributes.wclass = GDK_INPUT_OUTPUT;
275   attributes.visual = gtk_widget_get_visual (widget);
276   attributes.colormap = gtk_widget_get_colormap (widget);
277   attributes.event_mask = (gtk_widget_get_events (widget)
278                            | GDK_EXPOSURE_MASK);
279   attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
280   widget->window = gdk_window_new (gtk_widget_get_parent_window (widget), &attributes, attributes_mask);
281   gdk_window_set_user_data (widget->window, widget);
282
283   attributes.x = 0;
284   attributes.y = 0;
285   attributes.width = widget->allocation.width;
286   attributes.height = widget->allocation.height;
287   attributes.window_type = GDK_WINDOW_CHILD;
288   attributes.event_mask |= (gtk_widget_get_events (widget) |
289                             GDK_EXPOSURE_MASK |
290                             GDK_BUTTON1_MOTION_MASK |
291                             GDK_POINTER_MOTION_HINT_MASK |
292                             GDK_BUTTON_PRESS_MASK |
293                             GDK_BUTTON_RELEASE_MASK);
294   attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
295   hb->bin_window = gdk_window_new (widget->window, &attributes, attributes_mask);
296   gdk_window_set_user_data (hb->bin_window, widget);
297   if (GTK_BIN (hb)->child)
298     gtk_widget_set_parent_window (GTK_BIN (hb)->child, hb->bin_window);
299   
300   attributes.x = 0;
301   attributes.y = 0;
302   attributes.width = widget->requisition.width;
303   attributes.height = widget->requisition.height;
304   attributes.window_type = GDK_WINDOW_TOPLEVEL;
305   attributes.wclass = GDK_INPUT_OUTPUT;
306   attributes.visual = gtk_widget_get_visual (widget);
307   attributes.colormap = gtk_widget_get_colormap (widget);
308   attributes.event_mask = (gtk_widget_get_events (widget) |
309                            GDK_KEY_PRESS_MASK |
310                            GDK_ENTER_NOTIFY_MASK |
311                            GDK_LEAVE_NOTIFY_MASK |
312                            GDK_FOCUS_CHANGE_MASK |
313                            GDK_STRUCTURE_MASK);
314   attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
315   hb->float_window = gdk_window_new (NULL, &attributes, attributes_mask);
316   gdk_window_set_user_data (hb->float_window, widget);
317   gdk_window_set_decorations (hb->float_window, 0);
318   
319
320   widget->style = gtk_style_attach (widget->style, widget->window);
321   gtk_style_set_background (widget->style, widget->window, GTK_WIDGET_STATE (hb));
322   gtk_style_set_background (widget->style, hb->bin_window, GTK_WIDGET_STATE (hb));
323   gtk_style_set_background (widget->style, hb->float_window, GTK_WIDGET_STATE (hb));
324 }
325
326 static void
327 gtk_handle_box_unrealize (GtkWidget *widget)
328 {
329   GtkHandleBox *hb;
330
331   g_return_if_fail (widget != NULL);
332   g_return_if_fail (GTK_IS_HANDLE_BOX (widget));
333
334   hb = GTK_HANDLE_BOX (widget);
335
336   gdk_window_set_user_data (hb->bin_window, NULL);
337   gdk_window_destroy (hb->bin_window);
338   hb->bin_window = NULL;
339   gdk_window_set_user_data (hb->float_window, NULL);
340   gdk_window_destroy (hb->float_window);
341   hb->float_window = NULL;
342
343   gdk_cursor_destroy (hb->fleur_cursor);
344   hb->fleur_cursor = NULL;
345
346   if (GTK_WIDGET_CLASS (parent_class)->unrealize)
347     (* GTK_WIDGET_CLASS (parent_class)->unrealize) (widget);
348 }
349
350 static void
351 gtk_handle_box_size_request (GtkWidget      *widget,
352                              GtkRequisition *requisition)
353 {
354   GtkBin *bin;
355   GtkHandleBox *hb;
356
357   g_return_if_fail (widget != NULL);
358   g_return_if_fail (GTK_IS_HANDLE_BOX (widget));
359   g_return_if_fail (requisition != NULL);
360
361   bin = GTK_BIN (widget);
362   hb = GTK_HANDLE_BOX (widget);
363
364   if (hb->handle_position == GTK_POS_LEFT ||
365       hb->handle_position == GTK_POS_RIGHT)
366     {
367       requisition->width = DRAG_HANDLE_SIZE;
368       requisition->height = 0;
369     }
370   else
371     {
372       requisition->width = 0;
373       requisition->height = DRAG_HANDLE_SIZE;
374     }
375   requisition->width += GTK_CONTAINER (widget)->border_width * 2;
376   requisition->height += GTK_CONTAINER (widget)->border_width * 2;
377
378   /* if our child is not visible, we still request its size, since we
379    * won't have any usefull hint for our size otherwise.
380    */
381   if (bin->child)
382     gtk_widget_size_request (bin->child, &bin->child->requisition);
383
384   if (hb->child_detached)
385     {
386       if (hb->handle_position == GTK_POS_LEFT ||
387           hb->handle_position == GTK_POS_RIGHT)
388         requisition->height += bin->child->requisition.height;
389       else
390         requisition->width += bin->child->requisition.width;
391     }
392   else if (bin->child)
393     {
394       requisition->width += bin->child->requisition.width;
395       requisition->height += bin->child->requisition.height;
396     }
397   else
398     {
399       requisition->width += CHILDLESS_SIZE;
400       requisition->height += CHILDLESS_SIZE;
401     }
402 }
403
404 static void
405 gtk_handle_box_size_allocate (GtkWidget     *widget,
406                               GtkAllocation *allocation)
407 {
408   GtkBin *bin;
409   GtkHandleBox *hb;
410   
411   g_return_if_fail (widget != NULL);
412   g_return_if_fail (GTK_IS_HANDLE_BOX (widget));
413   g_return_if_fail (allocation != NULL);
414   
415   bin = GTK_BIN (widget);
416   hb = GTK_HANDLE_BOX (widget);
417   
418   widget->allocation.x = allocation->x;
419   if (allocation->height > widget->requisition.height)
420     widget->allocation.y = allocation->y + (allocation->height - widget->requisition.height) / 2;
421   else
422     widget->allocation.y = allocation->y;
423   widget->allocation.height = MIN (allocation->height, widget->requisition.height);
424   widget->allocation.width = MIN (allocation->width, widget->requisition.width);
425   
426   if (GTK_WIDGET_REALIZED (hb))
427     gdk_window_move_resize (widget->window,
428                             widget->allocation.x,
429                             widget->allocation.y,
430                             widget->allocation.width,
431                             widget->allocation.height);
432
433
434   if (bin->child && GTK_WIDGET_VISIBLE (bin->child))
435     {
436       GtkWidget *child;
437       GtkAllocation child_allocation;
438       guint border_width;
439
440       child = bin->child;
441       border_width = GTK_CONTAINER (widget)->border_width;
442
443       child_allocation.x = border_width;
444       child_allocation.y = border_width;
445       if (hb->handle_position == GTK_POS_LEFT)
446         child_allocation.x += DRAG_HANDLE_SIZE;
447       else if (hb->handle_position == GTK_POS_TOP)
448         child_allocation.y += DRAG_HANDLE_SIZE;
449
450       if (hb->child_detached)
451         {
452           guint float_width;
453           guint float_height;
454           
455           child_allocation.width = child->requisition.width;
456           child_allocation.height = child->requisition.height;
457           
458           float_width = child_allocation.width + 2 * border_width;
459           float_height = child_allocation.height + 2 * border_width;
460           
461           if (hb->handle_position == GTK_POS_LEFT ||
462               hb->handle_position == GTK_POS_RIGHT)
463             float_width += DRAG_HANDLE_SIZE;
464           else
465             float_height += DRAG_HANDLE_SIZE;
466
467           if (GTK_WIDGET_REALIZED (hb))
468             {
469               gdk_window_resize (hb->float_window,
470                                  float_width,
471                                  float_height);
472               gdk_window_move_resize (hb->bin_window,
473                                       0,
474                                       0,
475                                       float_width,
476                                       float_height);
477             }
478         }
479       else
480         {
481           child_allocation.width = widget->allocation.width - 2 * border_width;
482           child_allocation.height = widget->allocation.height - 2 * border_width;
483
484           if (hb->handle_position == GTK_POS_LEFT ||
485               hb->handle_position == GTK_POS_RIGHT)
486             child_allocation.width -= DRAG_HANDLE_SIZE;
487           else
488             child_allocation.height -= DRAG_HANDLE_SIZE;
489           
490           if (GTK_WIDGET_REALIZED (hb))
491             gdk_window_move_resize (hb->bin_window,
492                                     0,
493                                     0,
494                                     widget->allocation.width,
495                                     widget->allocation.height);
496         }
497
498       gtk_widget_size_allocate (bin->child, &child_allocation);
499     }
500 }
501
502 static void
503 gtk_handle_box_draw_ghost (GtkHandleBox *hb)
504 {
505   GtkWidget *widget;
506   guint x;
507   guint y;
508   guint width;
509   guint height;
510
511   widget = GTK_WIDGET (hb);
512
513   if (hb->handle_position == GTK_POS_LEFT ||
514       hb->handle_position == GTK_POS_RIGHT)
515     {
516       x = hb->handle_position == GTK_POS_LEFT ? 0 : widget->allocation.width;
517       y = 0;
518       width = DRAG_HANDLE_SIZE;
519       height = widget->allocation.height;
520     }
521   else
522     {
523       x = 0;
524       y = hb->handle_position == GTK_POS_TOP ? 0 : widget->allocation.height;
525       width = widget->allocation.width;
526       height = DRAG_HANDLE_SIZE;
527     }
528   gtk_draw_shadow (widget->style,
529                    widget->window,
530                    GTK_WIDGET_STATE (widget),
531                    GTK_SHADOW_ETCHED_IN,
532                    x,
533                    y,
534                    width,
535                    height);
536   /*
537   if (hb->handle_position == GTK_POS_LEFT ||
538       hb->handle_position == GTK_POS_RIGHT)
539     gtk_draw_hline (widget->style,
540                     widget->window,
541                     GTK_WIDGET_STATE (widget),
542                     hb->handle_position == GTK_POS_LEFT ? DRAG_HANDLE_SIZE : widget->allocation.width - DRAG_HANDLE_SIZE,
543                     widget->allocation.width - DRAG_HANDLE_SIZE,
544                     widget->allocation.height / 2);
545   else
546     gtk_draw_vline (widget->style,
547                     widget->window,
548                     GTK_WIDGET_STATE (widget),
549                     hb->handle_position == GTK_POS_TOP ? DRAG_HANDLE_SIZE : widget->allocation.height - DRAG_HANDLE_SIZE,
550                     widget->allocation.height - DRAG_HANDLE_SIZE,
551                     widget->allocation.width / 2);
552                     */
553 }
554
555 static void
556 gtk_handle_box_paint (GtkWidget      *widget,
557                       GdkEventExpose *event,
558                       GdkRectangle   *area)
559 {
560   GtkBin *bin;
561   GtkHandleBox *hb;
562   guint width;
563   guint height;
564   guint border_width;
565
566   bin = GTK_BIN (widget);
567   hb = GTK_HANDLE_BOX (widget);
568
569   border_width = GTK_CONTAINER (hb)->border_width;
570
571   if (hb->child_detached)
572     {
573       width = bin->child->allocation.width + 2 * border_width;
574       height = bin->child->allocation.height + 2 * border_width;
575     }
576   else if (hb->handle_position == GTK_POS_LEFT ||
577            hb->handle_position == GTK_POS_RIGHT)
578     {
579       width = widget->allocation.width - DRAG_HANDLE_SIZE;
580       height = widget->allocation.height;
581     }
582   else
583     {
584       width = widget->allocation.width;
585       height = widget->allocation.height - DRAG_HANDLE_SIZE;
586     }
587
588   gdk_window_clear (hb->bin_window);
589   gtk_draw_shadow (widget->style,
590                    hb->bin_window,
591                    GTK_WIDGET_STATE (widget),
592                    GTK_SHADOW_OUT,
593                    hb->handle_position == GTK_POS_LEFT ? DRAG_HANDLE_SIZE : 0,
594                    hb->handle_position == GTK_POS_TOP ? DRAG_HANDLE_SIZE : 0,
595                    width,
596                    height);
597
598   if (hb->handle_position == GTK_POS_LEFT ||
599       hb->handle_position == GTK_POS_RIGHT)
600     {
601       guint x;
602
603       for (x = 1; x < DRAG_HANDLE_SIZE; x += 3)
604         
605         gtk_draw_vline (widget->style,
606                         hb->bin_window,
607                         GTK_WIDGET_STATE (widget),
608                         widget->style->klass->ythickness,
609                         height + DRAG_HANDLE_SIZE - widget->style->klass->ythickness,
610                         hb->handle_position == GTK_POS_LEFT ? x : width + x);
611       gtk_draw_hline (widget->style,
612                       hb->bin_window,
613                       GTK_WIDGET_STATE (widget),
614                       hb->handle_position == GTK_POS_LEFT ? DRAG_HANDLE_SIZE : width,
615                       hb->handle_position == GTK_POS_LEFT ? 0 : width + DRAG_HANDLE_SIZE,
616                       0);
617       gtk_draw_hline (widget->style,
618                       hb->bin_window,
619                       GTK_WIDGET_STATE (widget),
620                       hb->handle_position == GTK_POS_LEFT ? DRAG_HANDLE_SIZE : width,
621                       hb->handle_position == GTK_POS_LEFT ? 0 : width + DRAG_HANDLE_SIZE,
622                       height - widget->style->klass->ythickness);
623     }
624   else
625     {
626       guint y;
627
628       for (y = 1; y < DRAG_HANDLE_SIZE; y += 3)
629         
630         gtk_draw_hline (widget->style,
631                         hb->bin_window,
632                         GTK_WIDGET_STATE (widget),
633                         widget->style->klass->xthickness,
634                         width + DRAG_HANDLE_SIZE - widget->style->klass->xthickness,
635                         hb->handle_position == GTK_POS_TOP ? y : height + y);
636       gtk_draw_vline (widget->style,
637                       hb->bin_window,
638                       GTK_WIDGET_STATE (widget),
639                       hb->handle_position == GTK_POS_TOP ? DRAG_HANDLE_SIZE : height,
640                       hb->handle_position == GTK_POS_TOP ? 0 : height + DRAG_HANDLE_SIZE,
641                       0);
642       gtk_draw_vline (widget->style,
643                       hb->bin_window,
644                       GTK_WIDGET_STATE (widget),
645                       hb->handle_position == GTK_POS_TOP ? DRAG_HANDLE_SIZE : height,
646                       hb->handle_position == GTK_POS_TOP ? 0 : height + DRAG_HANDLE_SIZE,
647                       width - widget->style->klass->xthickness);
648     }
649   
650
651   if (bin->child && GTK_WIDGET_VISIBLE (bin->child))
652     {
653       GdkRectangle child_area;
654       GdkEventExpose child_event;
655
656       if (!event) /* we were called from draw() */
657         {
658           if (gtk_widget_intersect (bin->child, area, &child_area))
659             gtk_widget_draw (bin->child, &child_area);
660         }
661       else /* we were called from expose() */
662         {
663           child_event = *event;
664           
665           if (GTK_WIDGET_NO_WINDOW (bin->child) &&
666               gtk_widget_intersect (bin->child, &event->area, &child_event.area))
667             gtk_widget_event (bin->child, (GdkEvent *) &child_event);
668         }
669     }
670 }
671
672 static void
673 gtk_handle_box_draw (GtkWidget    *widget,
674                      GdkRectangle *area)
675 {
676   GtkHandleBox *hb;
677
678   g_return_if_fail (widget != NULL);
679   g_return_if_fail (GTK_IS_HANDLE_BOX (widget));
680   g_return_if_fail (area != NULL);
681
682   hb = GTK_HANDLE_BOX (widget);
683
684   if (GTK_WIDGET_DRAWABLE (widget))
685     {
686       if (hb->child_detached)
687         {
688           /* The area parameter does not make sense in this case, so we
689            * repaint everything.
690            */
691
692           gtk_handle_box_draw_ghost (hb);
693
694           area->x = 0;
695           area->y = 0;
696           area->width = 2 * GTK_CONTAINER (hb)->border_width + DRAG_HANDLE_SIZE;
697           area->height = area->width + GTK_BIN (hb)->child->allocation.height;
698           area->width += GTK_BIN (hb)->child->allocation.width;
699
700           gtk_handle_box_paint (widget, NULL, area);
701         }
702       else
703         gtk_handle_box_paint (widget, NULL, area);
704     }
705 }
706
707 static gint
708 gtk_handle_box_expose (GtkWidget      *widget,
709                        GdkEventExpose *event)
710 {
711   GtkHandleBox *hb;
712
713   g_return_val_if_fail (widget != NULL, FALSE);
714   g_return_val_if_fail (GTK_IS_HANDLE_BOX (widget), FALSE);
715   g_return_val_if_fail (event != NULL, FALSE);
716
717   if (GTK_WIDGET_DRAWABLE (widget))
718     {
719       hb = GTK_HANDLE_BOX (widget);
720
721       if (event->window == widget->window)
722         {
723           if (hb->child_detached)
724             gtk_handle_box_draw_ghost (hb);
725         }
726       else
727         gtk_handle_box_paint (widget, event, NULL);
728     }
729   
730   return FALSE;
731 }
732
733 static gint
734 gtk_handle_box_button_changed (GtkWidget      *widget,
735                                GdkEventButton *event)
736 {
737   GtkHandleBox *hb;
738   gboolean event_handled;
739   
740   g_return_val_if_fail (widget != NULL, FALSE);
741   g_return_val_if_fail (GTK_IS_HANDLE_BOX (widget), FALSE);
742   g_return_val_if_fail (event != NULL, FALSE);
743
744   hb = GTK_HANDLE_BOX (widget);
745
746   event_handled = FALSE;
747   if (event->button == 1 &&
748       event->type == GDK_BUTTON_PRESS)
749     {
750       GtkWidget *child;
751       gboolean in_handle;
752       
753       child = GTK_BIN (hb)->child;
754       
755       switch (hb->handle_position)
756         {
757         case GTK_POS_LEFT:
758           in_handle = event->x < DRAG_HANDLE_SIZE;
759           break;
760         case GTK_POS_TOP:
761           in_handle = event->y < DRAG_HANDLE_SIZE;
762           break;
763         case GTK_POS_RIGHT:
764           in_handle = event->x > 2 * GTK_CONTAINER (hb)->border_width + child->allocation.width;
765           break;
766         case GTK_POS_BOTTOM:
767           in_handle = event->y > 2 * GTK_CONTAINER (hb)->border_width + child->allocation.height;
768           break;
769         default:
770           in_handle = FALSE;
771           break;
772         }
773
774       if (!child)
775         {
776           in_handle = FALSE;
777           event_handled = TRUE;
778         }
779       
780       if (in_handle)
781         {
782           hb->dragoff_x = event->x;
783           hb->dragoff_y = event->y;
784
785           gtk_grab_add (widget);
786           hb->in_drag = TRUE;
787           while (gdk_pointer_grab (hb->bin_window,
788                                    FALSE,
789                                    (GDK_BUTTON1_MOTION_MASK |
790                                     GDK_POINTER_MOTION_HINT_MASK |
791                                     GDK_BUTTON_RELEASE_MASK),
792                                    NULL,
793                                    hb->fleur_cursor,
794                                    GDK_CURRENT_TIME) != 0); /* wait for success */
795           event_handled = TRUE;
796         }
797     }
798   else if (event->type == GDK_BUTTON_RELEASE &&
799            hb->in_drag)
800     {
801       gdk_pointer_ungrab (GDK_CURRENT_TIME);
802       gtk_grab_remove (widget);
803       hb->in_drag = FALSE;
804       event_handled = TRUE;
805     }
806   
807   return event_handled;
808 }
809
810 static gint
811 gtk_handle_box_motion (GtkWidget      *widget,
812                        GdkEventMotion *event)
813 {
814   GtkHandleBox *hb;
815   gint new_x, new_y;
816   gint ox, oy;
817   gint snap_x, snap_y;
818   gboolean in_handle;
819
820   g_return_val_if_fail (widget != NULL, FALSE);
821   g_return_val_if_fail (GTK_IS_HANDLE_BOX (widget), FALSE);
822   g_return_val_if_fail (event != NULL, FALSE);
823
824   hb = GTK_HANDLE_BOX (widget);
825   if (!hb->in_drag)
826     return FALSE;
827
828   ox = 0;
829   oy = 0;
830   gdk_window_get_origin (hb->float_window, &ox, &oy);
831   new_x = 0;
832   new_y = 0;
833   gdk_window_get_pointer (hb->float_window, &new_x, &new_y, NULL);
834   new_x += ox;
835   new_y += oy;
836   new_x -= hb->dragoff_x;
837   new_y -= hb->dragoff_y;
838   snap_x = 0;
839   snap_y = 0;
840   gdk_window_get_pointer (widget->window, &snap_x, &snap_y, NULL);
841   
842   switch (hb->handle_position)
843     {
844     case GTK_POS_LEFT:
845       in_handle = (snap_x >= 0 && snap_x <= DRAG_HANDLE_SIZE &&
846                    snap_y >= 0 && snap_y <= widget->allocation.height);
847       break;
848     case GTK_POS_TOP:
849       in_handle = (snap_x >= 0 && snap_x <= widget->allocation.width &&
850                    snap_y >= 0 && snap_y <= DRAG_HANDLE_SIZE);
851       break;
852     case GTK_POS_RIGHT:
853       in_handle = (snap_x >= widget->allocation.width - DRAG_HANDLE_SIZE && snap_x <= widget->allocation.width &&
854                    snap_y >= 0 && snap_y <= widget->allocation.height);
855       break;
856     case GTK_POS_BOTTOM:
857       in_handle = (snap_x >= 0 && snap_x <= widget->allocation.width &&
858                    snap_y >= widget->allocation.height - DRAG_HANDLE_SIZE && snap_y <= widget->allocation.height);
859       break;
860     default:
861       in_handle = FALSE;
862       break;
863     }
864
865   if (in_handle)
866     {
867       if (hb->child_detached)
868         {
869           gdk_pointer_ungrab (GDK_CURRENT_TIME);
870           
871           hb->child_detached = FALSE;
872           gdk_window_hide (hb->float_window);
873           gdk_window_reparent (hb->bin_window, widget->window, 0, 0);
874           hb->float_window_mapped = FALSE;
875           gtk_signal_emit (GTK_OBJECT (hb),
876                            handle_box_signals[SIGNAL_CHILD_ATTACHED],
877                            GTK_BIN (hb)->child);
878           
879           while (gdk_pointer_grab (hb->bin_window,
880                                    FALSE,
881                                    (GDK_BUTTON1_MOTION_MASK |
882                                     GDK_POINTER_MOTION_HINT_MASK |
883                                     GDK_BUTTON_RELEASE_MASK),
884                                    NULL,
885                                    hb->fleur_cursor,
886                                    GDK_CURRENT_TIME) != 0); /* wait for success */
887           
888           gtk_widget_queue_resize (widget);
889         }
890     }
891   else
892     {
893       if (hb->child_detached)
894         {
895           gdk_window_move (hb->float_window, new_x, new_y);
896           gdk_window_raise (hb->float_window);
897         }
898       else
899         {
900           gint width;
901           gint height;
902
903           gdk_pointer_ungrab (GDK_CURRENT_TIME);
904
905           hb->child_detached = TRUE;
906           width = 0;
907           height = 0;
908           gdk_window_get_size (hb->bin_window, &width, &height);
909           gdk_window_move_resize (hb->float_window, new_x, new_y, width, height);
910           gdk_window_reparent (hb->bin_window, hb->float_window, 0, 0);
911           gdk_window_set_hints (hb->float_window, new_x, new_y, 0, 0, 0, 0, GDK_HINT_POS);
912           gdk_window_show (hb->float_window);
913           hb->float_window_mapped = TRUE;
914 #if     0
915           /* this extra move is neccessary if we use decorations, or our
916            * window manager insists on decorations.
917            */
918           gdk_flush ();
919           gdk_window_move (hb->float_window, new_x, new_y);
920           gdk_flush ();
921 #endif  /* 0 */
922           gtk_signal_emit (GTK_OBJECT (hb),
923                            handle_box_signals[SIGNAL_CHILD_DETACHED],
924                            GTK_BIN (hb)->child);
925           gtk_handle_box_draw_ghost (hb);
926           
927           while (gdk_pointer_grab (hb->bin_window,
928                                    FALSE,
929                                    (GDK_BUTTON1_MOTION_MASK |
930                                     GDK_POINTER_MOTION_HINT_MASK |
931                                     GDK_BUTTON_RELEASE_MASK),
932                                    NULL,
933                                    hb->fleur_cursor,
934                                    GDK_CURRENT_TIME) != 0); /* wait for success */
935           
936           gtk_widget_queue_resize (widget);
937         }
938     }
939
940   return TRUE;
941 }
942
943 static void
944 gtk_handle_box_add (GtkContainer *container,
945                     GtkWidget    *widget)
946 {
947   g_return_if_fail (GTK_IS_HANDLE_BOX (container));
948   g_return_if_fail (GTK_BIN (container)->child == NULL);
949   g_return_if_fail (widget->parent == NULL);
950
951   gtk_widget_set_parent_window (widget, GTK_HANDLE_BOX (container)->bin_window);
952   GTK_CONTAINER_CLASS (parent_class)->add (container, widget);
953 }
954
955 static void
956 gtk_handle_box_remove (GtkContainer *container,
957                        GtkWidget    *widget)
958 {
959   GtkHandleBox *hb;
960
961   g_return_if_fail (GTK_IS_HANDLE_BOX (container));
962   g_return_if_fail (GTK_BIN (container)->child == widget);
963
964   hb = GTK_HANDLE_BOX (container);
965
966   if (hb->child_detached)
967     {
968       hb->child_detached = FALSE;
969       if (GTK_WIDGET_REALIZED (hb))
970         {
971           gdk_window_hide (hb->float_window);
972           gdk_window_reparent (hb->bin_window, GTK_WIDGET (hb)->window, 0, 0);
973         }
974       hb->float_window_mapped = FALSE;
975     }
976   if (hb->in_drag)
977     {
978       gdk_pointer_ungrab (GDK_CURRENT_TIME);
979       gtk_grab_remove (GTK_WIDGET (container));
980       hb->in_drag = FALSE;
981     }
982   
983   GTK_CONTAINER_CLASS (parent_class)->remove (container, widget);
984 }
985
986 static gint
987 gtk_handle_box_delete_event (GtkWidget *widget,
988                              GdkEventAny  *event)
989 {
990   GtkHandleBox *hb;
991
992   g_return_val_if_fail (widget != NULL, FALSE);
993   g_return_val_if_fail (GTK_IS_HANDLE_BOX (widget), FALSE);
994   g_return_val_if_fail (event != NULL, FALSE);
995
996   hb = GTK_HANDLE_BOX (widget);
997   g_return_val_if_fail (event->window == hb->float_window, FALSE);
998   
999   hb->child_detached = FALSE;
1000   gdk_window_hide (hb->float_window);
1001   gdk_window_reparent (hb->bin_window, widget->window, 0, 0);
1002   hb->float_window_mapped = FALSE;
1003   gtk_signal_emit (GTK_OBJECT (hb),
1004                    handle_box_signals[SIGNAL_CHILD_ATTACHED],
1005                    GTK_BIN (hb)->child);
1006   if (hb->in_drag)
1007     {
1008       gdk_pointer_ungrab (GDK_CURRENT_TIME);
1009       gtk_grab_remove (widget);
1010       hb->in_drag = FALSE;
1011     }
1012   
1013   gtk_widget_queue_resize (GTK_WIDGET (hb));
1014
1015   return TRUE;
1016 }