]> Pileus Git - ~andy/gtk/blob - gtk/gtkhandlebox.c
implemented new flag shrink_on_detach (TRUE by default) which will cause
[~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   g_print ("size_request: width=%d height=%d\n", requisition->width, requisition->height);
419 }
420
421 static void
422 gtk_handle_box_size_allocate (GtkWidget     *widget,
423                               GtkAllocation *allocation)
424 {
425   GtkBin *bin;
426   GtkHandleBox *hb;
427   
428   g_return_if_fail (widget != NULL);
429   g_return_if_fail (GTK_IS_HANDLE_BOX (widget));
430   g_return_if_fail (allocation != NULL);
431   
432   bin = GTK_BIN (widget);
433   hb = GTK_HANDLE_BOX (widget);
434   
435   widget->allocation.x = allocation->x;
436
437   if (hb->child_detached)
438     {
439       guint max_req_height;
440       guint max_req_width;
441
442       max_req_height = MAX (widget->requisition.height,
443                             bin->child->requisition.height +
444                             2 * widget->style->klass->ythickness);
445       max_req_width = MAX (widget->requisition.width,
446                            bin->child->requisition.width +
447                            2 * widget->style->klass->xthickness);
448       
449       if (allocation->height > max_req_height)
450         widget->allocation.y = allocation->y +
451           (allocation->height - max_req_height) / 2;
452       else
453         widget->allocation.y = allocation->y;
454       
455       widget->allocation.height = MIN (allocation->height, max_req_height);
456       widget->allocation.width = MIN (allocation->width, max_req_width);
457     }
458   else
459     {
460       widget->allocation = *allocation;
461     }  
462
463   if (GTK_WIDGET_REALIZED (hb))
464     gdk_window_move_resize (widget->window,
465                             widget->allocation.x,
466                             widget->allocation.y,
467                             widget->allocation.width,
468                             widget->allocation.height);
469
470
471   if (bin->child && GTK_WIDGET_VISIBLE (bin->child))
472     {
473       GtkWidget *child;
474       GtkAllocation child_allocation;
475       guint border_width;
476
477       child = bin->child;
478       border_width = GTK_CONTAINER (widget)->border_width;
479
480       child_allocation.x = border_width;
481       child_allocation.y = border_width;
482       if (hb->handle_position == GTK_POS_LEFT)
483         child_allocation.x += DRAG_HANDLE_SIZE;
484       else if (hb->handle_position == GTK_POS_TOP)
485         child_allocation.y += DRAG_HANDLE_SIZE;
486
487       if (hb->child_detached)
488         {
489           guint float_width;
490           guint float_height;
491           
492           child_allocation.width = child->requisition.width;
493           child_allocation.height = child->requisition.height;
494           
495           float_width = child_allocation.width + 2 * border_width;
496           float_height = child_allocation.height + 2 * border_width;
497           
498           if (hb->handle_position == GTK_POS_LEFT ||
499               hb->handle_position == GTK_POS_RIGHT)
500             float_width += DRAG_HANDLE_SIZE;
501           else
502             float_height += DRAG_HANDLE_SIZE;
503
504           if (GTK_WIDGET_REALIZED (hb))
505             {
506               gdk_window_resize (hb->float_window,
507                                  float_width,
508                                  float_height);
509               gdk_window_move_resize (hb->bin_window,
510                                       0,
511                                       0,
512                                       float_width,
513                                       float_height);
514             }
515         }
516       else
517         {
518           child_allocation.width = widget->allocation.width - 2 * border_width;
519           child_allocation.height = widget->allocation.height - 2 * border_width;
520
521           if (hb->handle_position == GTK_POS_LEFT ||
522               hb->handle_position == GTK_POS_RIGHT)
523             child_allocation.width -= DRAG_HANDLE_SIZE;
524           else
525             child_allocation.height -= DRAG_HANDLE_SIZE;
526           
527           if (GTK_WIDGET_REALIZED (hb))
528             gdk_window_move_resize (hb->bin_window,
529                                     0,
530                                     0,
531                                     widget->allocation.width,
532                                     widget->allocation.height);
533         }
534
535       gtk_widget_size_allocate (bin->child, &child_allocation);
536     }
537 }
538
539 static void
540 gtk_handle_box_draw_ghost (GtkHandleBox *hb)
541 {
542   GtkWidget *widget;
543   guint x;
544   guint y;
545   guint width;
546   guint height;
547
548   widget = GTK_WIDGET (hb);
549
550   if (hb->handle_position == GTK_POS_LEFT ||
551       hb->handle_position == GTK_POS_RIGHT)
552     {
553       x = hb->handle_position == GTK_POS_LEFT ? 0 : widget->allocation.width;
554       y = 0;
555       width = DRAG_HANDLE_SIZE;
556       height = widget->allocation.height;
557     }
558   else
559     {
560       x = 0;
561       y = hb->handle_position == GTK_POS_TOP ? 0 : widget->allocation.height;
562       width = widget->allocation.width;
563       height = DRAG_HANDLE_SIZE;
564     }
565   gtk_draw_shadow (widget->style,
566                    widget->window,
567                    GTK_WIDGET_STATE (widget),
568                    GTK_SHADOW_ETCHED_IN,
569                    x,
570                    y,
571                    width,
572                    height);
573   /*
574   if (hb->handle_position == GTK_POS_LEFT ||
575       hb->handle_position == GTK_POS_RIGHT)
576     gtk_draw_hline (widget->style,
577                     widget->window,
578                     GTK_WIDGET_STATE (widget),
579                     hb->handle_position == GTK_POS_LEFT ? DRAG_HANDLE_SIZE : widget->allocation.width - DRAG_HANDLE_SIZE,
580                     widget->allocation.width - DRAG_HANDLE_SIZE,
581                     widget->allocation.height / 2);
582   else
583     gtk_draw_vline (widget->style,
584                     widget->window,
585                     GTK_WIDGET_STATE (widget),
586                     hb->handle_position == GTK_POS_TOP ? DRAG_HANDLE_SIZE : widget->allocation.height - DRAG_HANDLE_SIZE,
587                     widget->allocation.height - DRAG_HANDLE_SIZE,
588                     widget->allocation.width / 2);
589                     */
590 }
591
592 static void
593 gtk_handle_box_paint (GtkWidget      *widget,
594                       GdkEventExpose *event,
595                       GdkRectangle   *area)
596 {
597   GtkBin *bin;
598   GtkHandleBox *hb;
599   guint width;
600   guint height;
601   guint border_width;
602
603   bin = GTK_BIN (widget);
604   hb = GTK_HANDLE_BOX (widget);
605
606   border_width = GTK_CONTAINER (hb)->border_width;
607
608   if (hb->child_detached)
609     {
610       width = bin->child->allocation.width + 2 * border_width;
611       height = bin->child->allocation.height + 2 * border_width;
612     }
613   else if (hb->handle_position == GTK_POS_LEFT ||
614            hb->handle_position == GTK_POS_RIGHT)
615     {
616       width = widget->allocation.width - DRAG_HANDLE_SIZE;
617       height = widget->allocation.height;
618     }
619   else
620     {
621       width = widget->allocation.width;
622       height = widget->allocation.height - DRAG_HANDLE_SIZE;
623     }
624
625   gdk_window_clear (hb->bin_window);
626   gtk_draw_shadow (widget->style,
627                    hb->bin_window,
628                    GTK_WIDGET_STATE (widget),
629                    GTK_SHADOW_OUT,
630                    hb->handle_position == GTK_POS_LEFT ? DRAG_HANDLE_SIZE : 0,
631                    hb->handle_position == GTK_POS_TOP ? DRAG_HANDLE_SIZE : 0,
632                    width,
633                    height);
634
635   if (hb->handle_position == GTK_POS_LEFT ||
636       hb->handle_position == GTK_POS_RIGHT)
637     {
638       guint x;
639
640       for (x = 1; x < DRAG_HANDLE_SIZE; x += 3)
641         
642         gtk_draw_vline (widget->style,
643                         hb->bin_window,
644                         GTK_WIDGET_STATE (widget),
645                         widget->style->klass->ythickness,
646                         height + DRAG_HANDLE_SIZE - widget->style->klass->ythickness,
647                         hb->handle_position == GTK_POS_LEFT ? x : width + x);
648       gtk_draw_hline (widget->style,
649                       hb->bin_window,
650                       GTK_WIDGET_STATE (widget),
651                       hb->handle_position == GTK_POS_LEFT ? DRAG_HANDLE_SIZE : width,
652                       hb->handle_position == GTK_POS_LEFT ? 0 : width + DRAG_HANDLE_SIZE,
653                       0);
654       gtk_draw_hline (widget->style,
655                       hb->bin_window,
656                       GTK_WIDGET_STATE (widget),
657                       hb->handle_position == GTK_POS_LEFT ? DRAG_HANDLE_SIZE : width,
658                       hb->handle_position == GTK_POS_LEFT ? 0 : width + DRAG_HANDLE_SIZE,
659                       height - widget->style->klass->ythickness);
660     }
661   else
662     {
663       guint y;
664
665       for (y = 1; y < DRAG_HANDLE_SIZE; y += 3)
666         
667         gtk_draw_hline (widget->style,
668                         hb->bin_window,
669                         GTK_WIDGET_STATE (widget),
670                         widget->style->klass->xthickness,
671                         width + DRAG_HANDLE_SIZE - widget->style->klass->xthickness,
672                         hb->handle_position == GTK_POS_TOP ? y : height + y);
673       gtk_draw_vline (widget->style,
674                       hb->bin_window,
675                       GTK_WIDGET_STATE (widget),
676                       hb->handle_position == GTK_POS_TOP ? DRAG_HANDLE_SIZE : height,
677                       hb->handle_position == GTK_POS_TOP ? 0 : height + DRAG_HANDLE_SIZE,
678                       0);
679       gtk_draw_vline (widget->style,
680                       hb->bin_window,
681                       GTK_WIDGET_STATE (widget),
682                       hb->handle_position == GTK_POS_TOP ? DRAG_HANDLE_SIZE : height,
683                       hb->handle_position == GTK_POS_TOP ? 0 : height + DRAG_HANDLE_SIZE,
684                       width - widget->style->klass->xthickness);
685     }
686   
687
688   if (bin->child && GTK_WIDGET_VISIBLE (bin->child))
689     {
690       GdkRectangle child_area;
691       GdkEventExpose child_event;
692
693       if (!event) /* we were called from draw() */
694         {
695           if (gtk_widget_intersect (bin->child, area, &child_area))
696             gtk_widget_draw (bin->child, &child_area);
697         }
698       else /* we were called from expose() */
699         {
700           child_event = *event;
701           
702           if (GTK_WIDGET_NO_WINDOW (bin->child) &&
703               gtk_widget_intersect (bin->child, &event->area, &child_event.area))
704             gtk_widget_event (bin->child, (GdkEvent *) &child_event);
705         }
706     }
707 }
708
709 static void
710 gtk_handle_box_draw (GtkWidget    *widget,
711                      GdkRectangle *area)
712 {
713   GtkHandleBox *hb;
714
715   g_return_if_fail (widget != NULL);
716   g_return_if_fail (GTK_IS_HANDLE_BOX (widget));
717   g_return_if_fail (area != NULL);
718
719   hb = GTK_HANDLE_BOX (widget);
720
721   if (GTK_WIDGET_DRAWABLE (widget))
722     {
723       if (hb->child_detached)
724         {
725           /* The area parameter does not make sense in this case, so we
726            * repaint everything.
727            */
728
729           gtk_handle_box_draw_ghost (hb);
730
731           area->x = 0;
732           area->y = 0;
733           area->width = 2 * GTK_CONTAINER (hb)->border_width + DRAG_HANDLE_SIZE;
734           area->height = area->width + GTK_BIN (hb)->child->allocation.height;
735           area->width += GTK_BIN (hb)->child->allocation.width;
736
737           gtk_handle_box_paint (widget, NULL, area);
738         }
739       else
740         gtk_handle_box_paint (widget, NULL, area);
741     }
742 }
743
744 static gint
745 gtk_handle_box_expose (GtkWidget      *widget,
746                        GdkEventExpose *event)
747 {
748   GtkHandleBox *hb;
749
750   g_return_val_if_fail (widget != NULL, FALSE);
751   g_return_val_if_fail (GTK_IS_HANDLE_BOX (widget), FALSE);
752   g_return_val_if_fail (event != NULL, FALSE);
753
754   if (GTK_WIDGET_DRAWABLE (widget))
755     {
756       hb = GTK_HANDLE_BOX (widget);
757
758       if (event->window == widget->window)
759         {
760           if (hb->child_detached)
761             gtk_handle_box_draw_ghost (hb);
762         }
763       else
764         gtk_handle_box_paint (widget, event, NULL);
765     }
766   
767   return FALSE;
768 }
769
770 static gint
771 gtk_handle_box_button_changed (GtkWidget      *widget,
772                                GdkEventButton *event)
773 {
774   GtkHandleBox *hb;
775   gboolean event_handled;
776   
777   g_return_val_if_fail (widget != NULL, FALSE);
778   g_return_val_if_fail (GTK_IS_HANDLE_BOX (widget), FALSE);
779   g_return_val_if_fail (event != NULL, FALSE);
780
781   hb = GTK_HANDLE_BOX (widget);
782
783   event_handled = FALSE;
784   if (event->button == 1 &&
785       event->type == GDK_BUTTON_PRESS)
786     {
787       GtkWidget *child;
788       gboolean in_handle;
789       
790       child = GTK_BIN (hb)->child;
791       
792       switch (hb->handle_position)
793         {
794         case GTK_POS_LEFT:
795           in_handle = event->x < DRAG_HANDLE_SIZE;
796           break;
797         case GTK_POS_TOP:
798           in_handle = event->y < DRAG_HANDLE_SIZE;
799           break;
800         case GTK_POS_RIGHT:
801           in_handle = event->x > 2 * GTK_CONTAINER (hb)->border_width + child->allocation.width;
802           break;
803         case GTK_POS_BOTTOM:
804           in_handle = event->y > 2 * GTK_CONTAINER (hb)->border_width + child->allocation.height;
805           break;
806         default:
807           in_handle = FALSE;
808           break;
809         }
810
811       if (!child)
812         {
813           in_handle = FALSE;
814           event_handled = TRUE;
815         }
816       
817       if (in_handle)
818         {
819           hb->dragoff_x = event->x;
820           hb->dragoff_y = event->y;
821
822           gtk_grab_add (widget);
823           hb->in_drag = TRUE;
824           while (gdk_pointer_grab (hb->bin_window,
825                                    FALSE,
826                                    (GDK_BUTTON1_MOTION_MASK |
827                                     GDK_POINTER_MOTION_HINT_MASK |
828                                     GDK_BUTTON_RELEASE_MASK),
829                                    NULL,
830                                    hb->fleur_cursor,
831                                    GDK_CURRENT_TIME) != 0); /* wait for success */
832           event_handled = TRUE;
833         }
834     }
835   else if (event->type == GDK_BUTTON_RELEASE &&
836            hb->in_drag)
837     {
838       gdk_pointer_ungrab (GDK_CURRENT_TIME);
839       gtk_grab_remove (widget);
840       hb->in_drag = FALSE;
841       event_handled = TRUE;
842     }
843   
844   return event_handled;
845 }
846
847 static gint
848 gtk_handle_box_motion (GtkWidget      *widget,
849                        GdkEventMotion *event)
850 {
851   GtkHandleBox *hb;
852   gint new_x, new_y;
853   gint ox, oy;
854   gint snap_x, snap_y;
855   gboolean in_handle;
856
857   g_return_val_if_fail (widget != NULL, FALSE);
858   g_return_val_if_fail (GTK_IS_HANDLE_BOX (widget), FALSE);
859   g_return_val_if_fail (event != NULL, FALSE);
860
861   hb = GTK_HANDLE_BOX (widget);
862   if (!hb->in_drag)
863     return FALSE;
864
865   ox = 0;
866   oy = 0;
867   gdk_window_get_origin (hb->float_window, &ox, &oy);
868   new_x = 0;
869   new_y = 0;
870   gdk_window_get_pointer (hb->float_window, &new_x, &new_y, NULL);
871   new_x += ox;
872   new_y += oy;
873   new_x -= hb->dragoff_x;
874   new_y -= hb->dragoff_y;
875   snap_x = 0;
876   snap_y = 0;
877   gdk_window_get_pointer (widget->window, &snap_x, &snap_y, NULL);
878   
879   switch (hb->handle_position)
880     {
881     case GTK_POS_LEFT:
882       in_handle = (snap_x >= 0 && snap_x <= DRAG_HANDLE_SIZE &&
883                    snap_y >= 0 && snap_y <= widget->allocation.height);
884       break;
885     case GTK_POS_TOP:
886       in_handle = (snap_x >= 0 && snap_x <= widget->allocation.width &&
887                    snap_y >= 0 && snap_y <= DRAG_HANDLE_SIZE);
888       break;
889     case GTK_POS_RIGHT:
890       in_handle = (snap_x >= widget->allocation.width - DRAG_HANDLE_SIZE && snap_x <= widget->allocation.width &&
891                    snap_y >= 0 && snap_y <= widget->allocation.height);
892       break;
893     case GTK_POS_BOTTOM:
894       in_handle = (snap_x >= 0 && snap_x <= widget->allocation.width &&
895                    snap_y >= widget->allocation.height - DRAG_HANDLE_SIZE && snap_y <= widget->allocation.height);
896       break;
897     default:
898       in_handle = FALSE;
899       break;
900     }
901
902   if (in_handle)
903     {
904       if (hb->child_detached)
905         {
906           gdk_pointer_ungrab (GDK_CURRENT_TIME);
907           
908           hb->child_detached = FALSE;
909           gdk_window_hide (hb->float_window);
910           gdk_window_reparent (hb->bin_window, widget->window, 0, 0);
911           hb->float_window_mapped = FALSE;
912           gtk_signal_emit (GTK_OBJECT (hb),
913                            handle_box_signals[SIGNAL_CHILD_ATTACHED],
914                            GTK_BIN (hb)->child);
915           
916           while (gdk_pointer_grab (hb->bin_window,
917                                    FALSE,
918                                    (GDK_BUTTON1_MOTION_MASK |
919                                     GDK_POINTER_MOTION_HINT_MASK |
920                                     GDK_BUTTON_RELEASE_MASK),
921                                    NULL,
922                                    hb->fleur_cursor,
923                                    GDK_CURRENT_TIME) != 0); /* wait for success */
924           
925           gtk_widget_queue_resize (widget);
926         }
927     }
928   else
929     {
930       if (hb->child_detached)
931         {
932           gdk_window_move (hb->float_window, new_x, new_y);
933           gdk_window_raise (hb->float_window);
934         }
935       else
936         {
937           gint width;
938           gint height;
939
940           gdk_pointer_ungrab (GDK_CURRENT_TIME);
941
942           hb->child_detached = TRUE;
943           width = 0;
944           height = 0;
945           gdk_window_get_size (hb->bin_window, &width, &height);
946           gdk_window_move_resize (hb->float_window, new_x, new_y, width, height);
947           gdk_window_reparent (hb->bin_window, hb->float_window, 0, 0);
948           gdk_window_set_hints (hb->float_window, new_x, new_y, 0, 0, 0, 0, GDK_HINT_POS);
949           gdk_window_show (hb->float_window);
950           hb->float_window_mapped = TRUE;
951 #if     0
952           /* this extra move is neccessary if we use decorations, or our
953            * window manager insists on decorations.
954            */
955           gdk_flush ();
956           gdk_window_move (hb->float_window, new_x, new_y);
957           gdk_flush ();
958 #endif  /* 0 */
959           gtk_signal_emit (GTK_OBJECT (hb),
960                            handle_box_signals[SIGNAL_CHILD_DETACHED],
961                            GTK_BIN (hb)->child);
962           gtk_handle_box_draw_ghost (hb);
963           
964           while (gdk_pointer_grab (hb->bin_window,
965                                    FALSE,
966                                    (GDK_BUTTON1_MOTION_MASK |
967                                     GDK_POINTER_MOTION_HINT_MASK |
968                                     GDK_BUTTON_RELEASE_MASK),
969                                    NULL,
970                                    hb->fleur_cursor,
971                                    GDK_CURRENT_TIME) != 0); /* wait for success */
972           
973           gtk_widget_queue_resize (widget);
974         }
975     }
976
977   return TRUE;
978 }
979
980 static void
981 gtk_handle_box_add (GtkContainer *container,
982                     GtkWidget    *widget)
983 {
984   g_return_if_fail (GTK_IS_HANDLE_BOX (container));
985   g_return_if_fail (GTK_BIN (container)->child == NULL);
986   g_return_if_fail (widget->parent == NULL);
987
988   gtk_widget_set_parent_window (widget, GTK_HANDLE_BOX (container)->bin_window);
989   GTK_CONTAINER_CLASS (parent_class)->add (container, widget);
990 }
991
992 static void
993 gtk_handle_box_remove (GtkContainer *container,
994                        GtkWidget    *widget)
995 {
996   GtkHandleBox *hb;
997
998   g_return_if_fail (GTK_IS_HANDLE_BOX (container));
999   g_return_if_fail (GTK_BIN (container)->child == widget);
1000
1001   hb = GTK_HANDLE_BOX (container);
1002
1003   if (hb->child_detached)
1004     {
1005       hb->child_detached = FALSE;
1006       if (GTK_WIDGET_REALIZED (hb))
1007         {
1008           gdk_window_hide (hb->float_window);
1009           gdk_window_reparent (hb->bin_window, GTK_WIDGET (hb)->window, 0, 0);
1010         }
1011       hb->float_window_mapped = FALSE;
1012     }
1013   if (hb->in_drag)
1014     {
1015       gdk_pointer_ungrab (GDK_CURRENT_TIME);
1016       gtk_grab_remove (GTK_WIDGET (container));
1017       hb->in_drag = FALSE;
1018     }
1019   
1020   GTK_CONTAINER_CLASS (parent_class)->remove (container, widget);
1021 }
1022
1023 static gint
1024 gtk_handle_box_delete_event (GtkWidget *widget,
1025                              GdkEventAny  *event)
1026 {
1027   GtkHandleBox *hb;
1028
1029   g_return_val_if_fail (widget != NULL, FALSE);
1030   g_return_val_if_fail (GTK_IS_HANDLE_BOX (widget), FALSE);
1031   g_return_val_if_fail (event != NULL, FALSE);
1032
1033   hb = GTK_HANDLE_BOX (widget);
1034   g_return_val_if_fail (event->window == hb->float_window, FALSE);
1035   
1036   hb->child_detached = FALSE;
1037   gdk_window_hide (hb->float_window);
1038   gdk_window_reparent (hb->bin_window, widget->window, 0, 0);
1039   hb->float_window_mapped = FALSE;
1040   gtk_signal_emit (GTK_OBJECT (hb),
1041                    handle_box_signals[SIGNAL_CHILD_ATTACHED],
1042                    GTK_BIN (hb)->child);
1043   if (hb->in_drag)
1044     {
1045       gdk_pointer_ungrab (GDK_CURRENT_TIME);
1046       gtk_grab_remove (widget);
1047       hb->in_drag = FALSE;
1048     }
1049   
1050   gtk_widget_queue_resize (GTK_WIDGET (hb));
1051
1052   return TRUE;
1053 }