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