]> Pileus Git - ~andy/gtk/blob - gtk/gtkhandlebox.c
Minor indent fix on gtk_handle_box_get_arg proto
[~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       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   GTK_WIDGET_SET_FLAGS (handle_box, GTK_BASIC); /* FIXME: are we really a basic widget? */
177
178   handle_box->bin_window = NULL;
179   handle_box->float_window = NULL;
180   handle_box->shadow_type = GTK_SHADOW_OUT;
181   handle_box->handle_position = GTK_POS_LEFT;
182   handle_box->float_window_mapped = FALSE;
183   handle_box->child_detached = FALSE;
184   handle_box->in_drag = FALSE;
185   handle_box->shrink_on_detach = TRUE;
186   handle_box->dragoff_x = 0;
187   handle_box->dragoff_y = 0;
188 }
189
190 static void
191 gtk_handle_box_set_arg (GtkObject      *object,
192                         GtkArg         *arg,
193                         guint           arg_id)
194 {
195   GtkHandleBox *handle_box;
196
197   handle_box = GTK_HANDLE_BOX (object);
198
199   switch (arg_id)
200     {
201     case ARG_SHADOW:
202       gtk_handle_box_set_shadow_type (handle_box, GTK_VALUE_ENUM (*arg));
203       break;
204     default:
205       break;
206     }
207 }
208
209 static void
210 gtk_handle_box_get_arg (GtkObject      *object,
211                         GtkArg         *arg,
212                         guint           arg_id)
213 {
214   GtkHandleBox *handle_box;
215
216   handle_box = GTK_HANDLE_BOX (object);
217
218   switch (arg_id)
219     {
220     case ARG_SHADOW:
221       GTK_VALUE_ENUM (*arg) = handle_box->shadow_type;
222       break;
223     default:
224       arg->type = GTK_TYPE_INVALID;
225       break;
226     }
227 }
228  
229 GtkWidget*
230 gtk_handle_box_new (void)
231 {
232   return GTK_WIDGET (gtk_type_new (gtk_handle_box_get_type ()));
233 }
234
235 static void
236 gtk_handle_box_destroy (GtkObject *object)
237 {
238   GtkHandleBox *hb;
239
240   g_return_if_fail (object != NULL);
241   g_return_if_fail (GTK_IS_HANDLE_BOX (object));
242
243   hb = GTK_HANDLE_BOX (object);
244
245   if (GTK_OBJECT_CLASS (parent_class)->destroy)
246     (* GTK_OBJECT_CLASS (parent_class)->destroy) (object);
247 }
248
249 static void
250 gtk_handle_box_map (GtkWidget *widget)
251 {
252   GtkBin *bin;
253   GtkHandleBox *hb;
254
255   g_return_if_fail (widget != NULL);
256   g_return_if_fail (GTK_IS_HANDLE_BOX (widget));
257
258   GTK_WIDGET_SET_FLAGS (widget, GTK_MAPPED);
259
260   bin = GTK_BIN (widget);
261   hb = GTK_HANDLE_BOX (widget);
262
263   gdk_window_show (hb->bin_window);
264   gdk_window_show (widget->window);
265
266   if (hb->child_detached && !hb->float_window_mapped)
267     {
268       gdk_window_show (hb->float_window);
269       hb->float_window_mapped = TRUE;
270     }
271
272   if (bin->child &&
273       GTK_WIDGET_VISIBLE (bin->child) &&
274       !GTK_WIDGET_MAPPED (bin->child))
275     gtk_widget_map (bin->child);
276 }
277
278 static void
279 gtk_handle_box_unmap (GtkWidget *widget)
280 {
281   GtkHandleBox *hb;
282
283   g_return_if_fail (widget != NULL);
284   g_return_if_fail (GTK_IS_HANDLE_BOX (widget));
285
286   GTK_WIDGET_UNSET_FLAGS (widget, GTK_MAPPED);
287
288   hb = GTK_HANDLE_BOX (widget);
289
290   gdk_window_hide (widget->window);
291   if (hb->float_window_mapped)
292     {
293       gdk_window_hide (hb->float_window);
294       hb->float_window_mapped = FALSE;
295     }
296 }
297
298 static void
299 gtk_handle_box_realize (GtkWidget *widget)
300 {
301   GdkWindowAttr attributes;
302   gint attributes_mask;
303   GtkHandleBox *hb;
304
305   g_return_if_fail (widget != NULL);
306   g_return_if_fail (GTK_IS_HANDLE_BOX (widget));
307
308   hb = GTK_HANDLE_BOX (widget);
309
310   GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
311
312   attributes.x = widget->allocation.x;
313   attributes.y = widget->allocation.y;
314   attributes.width = widget->allocation.width;
315   attributes.height = widget->allocation.height;
316   attributes.window_type = GDK_WINDOW_CHILD;
317   attributes.wclass = GDK_INPUT_OUTPUT;
318   attributes.visual = gtk_widget_get_visual (widget);
319   attributes.colormap = gtk_widget_get_colormap (widget);
320   attributes.event_mask = (gtk_widget_get_events (widget)
321                            | GDK_EXPOSURE_MASK);
322   attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
323   widget->window = gdk_window_new (gtk_widget_get_parent_window (widget), &attributes, attributes_mask);
324   gdk_window_set_user_data (widget->window, widget);
325
326   attributes.x = 0;
327   attributes.y = 0;
328   attributes.width = widget->allocation.width;
329   attributes.height = widget->allocation.height;
330   attributes.window_type = GDK_WINDOW_CHILD;
331   attributes.event_mask |= (gtk_widget_get_events (widget) |
332                             GDK_EXPOSURE_MASK |
333                             GDK_BUTTON1_MOTION_MASK |
334                             GDK_POINTER_MOTION_HINT_MASK |
335                             GDK_BUTTON_PRESS_MASK |
336                             GDK_BUTTON_RELEASE_MASK);
337   attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
338   hb->bin_window = gdk_window_new (widget->window, &attributes, attributes_mask);
339   gdk_window_set_user_data (hb->bin_window, widget);
340   if (GTK_BIN (hb)->child)
341     gtk_widget_set_parent_window (GTK_BIN (hb)->child, hb->bin_window);
342   
343   attributes.x = 0;
344   attributes.y = 0;
345   attributes.width = widget->requisition.width;
346   attributes.height = widget->requisition.height;
347   attributes.window_type = GDK_WINDOW_TOPLEVEL;
348   attributes.wclass = GDK_INPUT_OUTPUT;
349   attributes.visual = gtk_widget_get_visual (widget);
350   attributes.colormap = gtk_widget_get_colormap (widget);
351   attributes.event_mask = (gtk_widget_get_events (widget) |
352                            GDK_KEY_PRESS_MASK |
353                            GDK_ENTER_NOTIFY_MASK |
354                            GDK_LEAVE_NOTIFY_MASK |
355                            GDK_FOCUS_CHANGE_MASK |
356                            GDK_STRUCTURE_MASK);
357   attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
358   hb->float_window = gdk_window_new (NULL, &attributes, attributes_mask);
359   gdk_window_set_user_data (hb->float_window, widget);
360   gdk_window_set_decorations (hb->float_window, 0);
361   
362   widget->style = gtk_style_attach (widget->style, widget->window);
363   gtk_style_set_background (widget->style, widget->window, GTK_WIDGET_STATE (hb));
364   gtk_style_set_background (widget->style, hb->bin_window, GTK_WIDGET_STATE (hb));
365   gtk_style_set_background (widget->style, hb->float_window, GTK_WIDGET_STATE (hb));
366   gdk_window_set_back_pixmap (widget->window, NULL, TRUE);
367 }
368
369 static void
370 gtk_handle_box_unrealize (GtkWidget *widget)
371 {
372   GtkHandleBox *hb;
373
374   g_return_if_fail (widget != NULL);
375   g_return_if_fail (GTK_IS_HANDLE_BOX (widget));
376
377   hb = GTK_HANDLE_BOX (widget);
378
379   gdk_window_set_user_data (hb->bin_window, NULL);
380   gdk_window_destroy (hb->bin_window);
381   hb->bin_window = NULL;
382   gdk_window_set_user_data (hb->float_window, NULL);
383   gdk_window_destroy (hb->float_window);
384   hb->float_window = NULL;
385
386   if (GTK_WIDGET_CLASS (parent_class)->unrealize)
387     (* GTK_WIDGET_CLASS (parent_class)->unrealize) (widget);
388 }
389
390 static void
391 gtk_handle_box_style_set (GtkWidget *widget,
392                           GtkStyle  *previous_style)
393 {
394   GtkHandleBox *hb;
395
396   g_return_if_fail (widget != NULL);
397   g_return_if_fail (GTK_IS_HANDLE_BOX (widget));
398
399   hb = GTK_HANDLE_BOX (widget);
400
401   if (GTK_WIDGET_REALIZED (widget) &&
402       !GTK_WIDGET_NO_WINDOW (widget))
403     {
404       gtk_style_set_background (widget->style, widget->window,
405 widget->state);
406       gtk_style_set_background (widget->style, hb->bin_window, widget->state);
407       gtk_style_set_background (widget->style, hb->float_window, widget->state);
408       if (GTK_WIDGET_DRAWABLE (widget))
409         gdk_window_clear (widget->window);
410     }
411 }
412
413 static void
414 gtk_handle_box_size_request (GtkWidget      *widget,
415                              GtkRequisition *requisition)
416 {
417   GtkBin *bin;
418   GtkHandleBox *hb;
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, &bin->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 += bin->child->requisition.height;
452           else
453             requisition->width += bin->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 += bin->child->requisition.width;
472           requisition->height += bin->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   
489   g_return_if_fail (widget != NULL);
490   g_return_if_fail (GTK_IS_HANDLE_BOX (widget));
491   g_return_if_fail (allocation != NULL);
492   
493   bin = GTK_BIN (widget);
494   hb = GTK_HANDLE_BOX (widget);
495   
496   widget->allocation.x = allocation->x;
497
498   if (hb->child_detached)
499     {
500       guint max_req_height;
501       guint max_req_width;
502
503       max_req_height = MAX (widget->requisition.height,
504                             bin->child->requisition.height +
505                             2 * widget->style->klass->ythickness);
506       max_req_width = MAX (widget->requisition.width,
507                            bin->child->requisition.width +
508                            2 * widget->style->klass->xthickness);
509       
510       if (allocation->height > max_req_height)
511         widget->allocation.y = allocation->y +
512           (allocation->height - max_req_height) / 2;
513       else
514         widget->allocation.y = allocation->y;
515       
516       widget->allocation.height = MIN (allocation->height, max_req_height);
517       widget->allocation.width = MIN (allocation->width, max_req_width);
518     }
519   else
520     {
521       widget->allocation = *allocation;
522     }  
523
524   if (GTK_WIDGET_REALIZED (hb))
525     gdk_window_move_resize (widget->window,
526                             widget->allocation.x,
527                             widget->allocation.y,
528                             widget->allocation.width,
529                             widget->allocation.height);
530
531
532   if (bin->child && GTK_WIDGET_VISIBLE (bin->child))
533     {
534       GtkWidget *child;
535       GtkAllocation child_allocation;
536       guint border_width;
537
538       child = bin->child;
539       border_width = GTK_CONTAINER (widget)->border_width;
540
541       child_allocation.x = border_width;
542       child_allocation.y = border_width;
543       if (hb->handle_position == GTK_POS_LEFT)
544         child_allocation.x += DRAG_HANDLE_SIZE;
545       else if (hb->handle_position == GTK_POS_TOP)
546         child_allocation.y += DRAG_HANDLE_SIZE;
547
548       if (hb->child_detached)
549         {
550           guint float_width;
551           guint float_height;
552           
553           child_allocation.width = child->requisition.width;
554           child_allocation.height = child->requisition.height;
555           
556           float_width = child_allocation.width + 2 * border_width;
557           float_height = child_allocation.height + 2 * border_width;
558           
559           if (hb->handle_position == GTK_POS_LEFT ||
560               hb->handle_position == GTK_POS_RIGHT)
561             float_width += DRAG_HANDLE_SIZE;
562           else
563             float_height += DRAG_HANDLE_SIZE;
564
565           if (GTK_WIDGET_REALIZED (hb))
566             {
567               gdk_window_resize (hb->float_window,
568                                  float_width,
569                                  float_height);
570               gdk_window_move_resize (hb->bin_window,
571                                       0,
572                                       0,
573                                       float_width,
574                                       float_height);
575             }
576         }
577       else
578         {
579           child_allocation.width = MAX (1, widget->allocation.width - 2 * border_width);
580           child_allocation.height = MAX (1, widget->allocation.height - 2 * border_width);
581
582           if (hb->handle_position == GTK_POS_LEFT ||
583               hb->handle_position == GTK_POS_RIGHT)
584             child_allocation.width -= DRAG_HANDLE_SIZE;
585           else
586             child_allocation.height -= DRAG_HANDLE_SIZE;
587           
588           if (GTK_WIDGET_REALIZED (hb))
589             gdk_window_move_resize (hb->bin_window,
590                                     0,
591                                     0,
592                                     widget->allocation.width,
593                                     widget->allocation.height);
594         }
595
596       gtk_widget_size_allocate (bin->child, &child_allocation);
597     }
598 }
599
600 static void
601 gtk_handle_box_draw_ghost (GtkHandleBox *hb)
602 {
603   GtkWidget *widget;
604   guint x;
605   guint y;
606   guint width;
607   guint height;
608
609   widget = GTK_WIDGET (hb);
610
611   if (hb->handle_position == GTK_POS_LEFT ||
612       hb->handle_position == GTK_POS_RIGHT)
613     {
614       x = hb->handle_position == GTK_POS_LEFT ? 0 : widget->allocation.width;
615       y = 0;
616       width = DRAG_HANDLE_SIZE;
617       height = widget->allocation.height;
618     }
619   else
620     {
621       x = 0;
622       y = hb->handle_position == GTK_POS_TOP ? 0 : widget->allocation.height;
623       width = widget->allocation.width;
624       height = DRAG_HANDLE_SIZE;
625     }
626   gtk_paint_shadow (widget->style,
627                     widget->window,
628                     GTK_WIDGET_STATE (widget),
629                     GTK_SHADOW_ETCHED_IN,
630                     NULL, widget, "handle",
631                     x,
632                     y,
633                     width,
634                     height);
635    if (hb->handle_position == GTK_POS_LEFT ||
636        hb->handle_position == GTK_POS_RIGHT)
637      gtk_paint_hline (widget->style,
638                       widget->window,
639                       GTK_WIDGET_STATE (widget),
640                       NULL, widget, "handlebox",
641                       hb->handle_position == GTK_POS_LEFT ? DRAG_HANDLE_SIZE : widget->allocation.width - DRAG_HANDLE_SIZE,
642                       widget->allocation.width - DRAG_HANDLE_SIZE,
643                       widget->allocation.height / 2);
644    else
645      gtk_paint_vline (widget->style,
646                       widget->window,
647                       GTK_WIDGET_STATE (widget),
648                       NULL, widget, "handlebox",
649                       hb->handle_position == GTK_POS_TOP ? DRAG_HANDLE_SIZE : widget->allocation.height - DRAG_HANDLE_SIZE,
650                       widget->allocation.height - DRAG_HANDLE_SIZE,
651                       widget->allocation.width / 2);
652 }
653
654 static void
655 draw_textured_frame (GtkWidget *widget, GdkWindow *window, GdkRectangle *rect, GtkShadowType shadow,
656                      GdkRectangle *clip)
657 {
658    gtk_paint_handle(widget->style, window, GTK_STATE_NORMAL, shadow,
659                     NULL, widget, "handlebox",
660                     rect->x, rect->y, rect->width, rect->height, 
661                     GTK_ORIENTATION_VERTICAL);
662 }
663
664 void
665 gtk_handle_box_set_shadow_type (GtkHandleBox  *handle_box,
666                                 GtkShadowType  type)
667 {
668   g_return_if_fail (handle_box != NULL);
669   g_return_if_fail (GTK_IS_HANDLE_BOX (handle_box));
670
671   if ((GtkShadowType) handle_box->shadow_type != type)
672     {
673       handle_box->shadow_type = type;
674
675       if (GTK_WIDGET_DRAWABLE (handle_box))
676         {
677           gtk_widget_queue_clear (GTK_WIDGET (handle_box));
678         }
679       gtk_widget_queue_resize (GTK_WIDGET (handle_box));
680     }
681 }
682
683 static void
684 gtk_handle_box_paint (GtkWidget      *widget,
685                       GdkEventExpose *event,
686                       GdkRectangle   *area)
687 {
688   GtkBin *bin;
689   GtkHandleBox *hb;
690   guint width;
691   guint height;
692   guint border_width;
693   GdkRectangle rect;
694
695   bin = GTK_BIN (widget);
696   hb = GTK_HANDLE_BOX (widget);
697
698   border_width = GTK_CONTAINER (hb)->border_width;
699
700   if (hb->child_detached)
701     {
702       width = bin->child->allocation.width + 2 * border_width;
703       height = bin->child->allocation.height + 2 * border_width;
704     }
705   else if (hb->handle_position == GTK_POS_LEFT ||
706            hb->handle_position == GTK_POS_RIGHT)
707     {
708       width = widget->allocation.width - DRAG_HANDLE_SIZE;
709       height = widget->allocation.height;
710     }
711   else
712     {
713       width = widget->allocation.width;
714       height = widget->allocation.height - DRAG_HANDLE_SIZE;
715     }
716
717   if (!event)
718    gtk_paint_box(widget->style,
719                  hb->bin_window,
720                  GTK_WIDGET_STATE (widget),
721                  hb->shadow_type,
722                  area, widget, "handlebox_bin",
723                  0, 0, -1, -1);
724   else
725    gtk_paint_box(widget->style,
726                  hb->bin_window,
727                  GTK_WIDGET_STATE (widget),
728                  hb->shadow_type,
729                  &event->area, widget, "handlebox_bin",
730                  0, 0, -1, -1);
731
732 /* We currently draw the handle _above_ the relief of the handlebox.
733  * it could also be drawn on the same level...
734
735                  hb->handle_position == GTK_POS_LEFT ? DRAG_HANDLE_SIZE : 0,
736                  hb->handle_position == GTK_POS_TOP ? DRAG_HANDLE_SIZE : 0,
737                  width,
738                  height);*/
739
740   rect.x = 0;
741   rect.y = 0; 
742   rect.width = (hb->handle_position == GTK_POS_LEFT) ? DRAG_HANDLE_SIZE : width;
743   rect.height = (hb->handle_position == GTK_POS_TOP) ? DRAG_HANDLE_SIZE : height;
744
745   draw_textured_frame (widget, hb->bin_window, &rect, GTK_SHADOW_OUT, event ? &event->area : area);
746
747   if (bin->child && GTK_WIDGET_VISIBLE (bin->child))
748     {
749       GdkRectangle child_area;
750       GdkEventExpose child_event;
751
752       if (!event) /* we were called from draw() */
753         {
754           if (gtk_widget_intersect (bin->child, area, &child_area))
755             gtk_widget_draw (bin->child, &child_area);
756         }
757       else /* we were called from expose() */
758         {
759           child_event = *event;
760           
761           if (GTK_WIDGET_NO_WINDOW (bin->child) &&
762               gtk_widget_intersect (bin->child, &event->area, &child_event.area))
763             gtk_widget_event (bin->child, (GdkEvent *) &child_event);
764         }
765     }
766 }
767
768 static void
769 gtk_handle_box_draw (GtkWidget    *widget,
770                      GdkRectangle *area)
771 {
772   GtkHandleBox *hb;
773
774   g_return_if_fail (widget != NULL);
775   g_return_if_fail (GTK_IS_HANDLE_BOX (widget));
776   g_return_if_fail (area != NULL);
777
778   hb = GTK_HANDLE_BOX (widget);
779
780   if (GTK_WIDGET_DRAWABLE (widget))
781     {
782       if (hb->child_detached)
783         {
784           /* The area parameter does not make sense in this case, so we
785            * repaint everything.
786            */
787
788           gtk_handle_box_draw_ghost (hb);
789
790           area->x = 0;
791           area->y = 0;
792           area->width = 2 * GTK_CONTAINER (hb)->border_width + DRAG_HANDLE_SIZE;
793           area->height = area->width + GTK_BIN (hb)->child->allocation.height;
794           area->width += GTK_BIN (hb)->child->allocation.width;
795
796           gtk_handle_box_paint (widget, NULL, area);
797         }
798       else
799         gtk_handle_box_paint (widget, NULL, area);
800     }
801 }
802
803 static gint
804 gtk_handle_box_expose (GtkWidget      *widget,
805                        GdkEventExpose *event)
806 {
807   GtkHandleBox *hb;
808
809   g_return_val_if_fail (widget != NULL, FALSE);
810   g_return_val_if_fail (GTK_IS_HANDLE_BOX (widget), FALSE);
811   g_return_val_if_fail (event != NULL, FALSE);
812
813   if (GTK_WIDGET_DRAWABLE (widget))
814     {
815       hb = GTK_HANDLE_BOX (widget);
816
817       if (event->window == widget->window)
818         {
819           if (hb->child_detached)
820             gtk_handle_box_draw_ghost (hb);
821         }
822       else
823         gtk_handle_box_paint (widget, event, NULL);
824     }
825   
826   return FALSE;
827 }
828
829 static gint
830 gtk_handle_box_button_changed (GtkWidget      *widget,
831                                GdkEventButton *event)
832 {
833   GtkHandleBox *hb;
834   gboolean event_handled;
835   GdkCursor *fleur;
836   
837   g_return_val_if_fail (widget != NULL, FALSE);
838   g_return_val_if_fail (GTK_IS_HANDLE_BOX (widget), FALSE);
839   g_return_val_if_fail (event != NULL, FALSE);
840
841   hb = GTK_HANDLE_BOX (widget);
842
843   if (event->window != hb->bin_window)
844     return FALSE;
845
846   event_handled = FALSE;
847   if (event->button == 1 &&
848       event->type == GDK_BUTTON_PRESS)
849     {
850       GtkWidget *child;
851       gboolean in_handle;
852       
853       child = GTK_BIN (hb)->child;
854       
855       switch (hb->handle_position)
856         {
857         case GTK_POS_LEFT:
858           in_handle = event->x < DRAG_HANDLE_SIZE;
859           break;
860         case GTK_POS_TOP:
861           in_handle = event->y < DRAG_HANDLE_SIZE;
862           break;
863         case GTK_POS_RIGHT:
864           in_handle = event->x > 2 * GTK_CONTAINER (hb)->border_width + child->allocation.width;
865           break;
866         case GTK_POS_BOTTOM:
867           in_handle = event->y > 2 * GTK_CONTAINER (hb)->border_width + child->allocation.height;
868           break;
869         default:
870           in_handle = FALSE;
871           break;
872         }
873
874       if (!child)
875         {
876           in_handle = FALSE;
877           event_handled = TRUE;
878         }
879       
880       if (in_handle)
881         {
882           hb->dragoff_x = event->x;
883           hb->dragoff_y = event->y;
884
885           gtk_grab_add (widget);
886           hb->in_drag = TRUE;
887           fleur = gdk_cursor_new (GDK_FLEUR);
888           while (gdk_pointer_grab (hb->bin_window,
889                                    FALSE,
890                                    (GDK_BUTTON1_MOTION_MASK |
891                                     GDK_POINTER_MOTION_HINT_MASK |
892                                     GDK_BUTTON_RELEASE_MASK),
893                                    NULL,
894                                    fleur,
895                                    GDK_CURRENT_TIME) != 0); /* wait for success */
896           gdk_cursor_destroy (fleur);
897           event_handled = TRUE;
898         }
899     }
900   else if (event->type == GDK_BUTTON_RELEASE &&
901            hb->in_drag)
902     {
903       gdk_pointer_ungrab (GDK_CURRENT_TIME);
904       gtk_grab_remove (widget);
905       hb->in_drag = FALSE;
906       event_handled = TRUE;
907     }
908   
909   return event_handled;
910 }
911
912 static gint
913 gtk_handle_box_motion (GtkWidget      *widget,
914                        GdkEventMotion *event)
915 {
916   GtkHandleBox *hb;
917   gint new_x, new_y;
918   gint ox, oy;
919   gint snap_x, snap_y;
920   gboolean in_handle;
921   GdkCursor *fleur;
922
923   g_return_val_if_fail (widget != NULL, FALSE);
924   g_return_val_if_fail (GTK_IS_HANDLE_BOX (widget), FALSE);
925   g_return_val_if_fail (event != NULL, FALSE);
926
927   hb = GTK_HANDLE_BOX (widget);
928   if (!hb->in_drag)
929     return FALSE;
930
931   if (event->window != hb->bin_window)
932     return FALSE;
933
934   ox = 0;
935   oy = 0;
936   gdk_window_get_deskrelative_origin (hb->float_window, &ox, &oy);
937   new_x = 0;
938   new_y = 0;
939   gdk_window_get_pointer (hb->float_window, &new_x, &new_y, NULL);
940   new_x += ox;
941   new_y += oy;
942   new_x -= hb->dragoff_x;
943   new_y -= hb->dragoff_y;
944   snap_x = 0;
945   snap_y = 0;
946   gdk_window_get_pointer (widget->window, &snap_x, &snap_y, NULL);
947   
948   switch (hb->handle_position)
949     {
950     case GTK_POS_LEFT:
951       in_handle = (snap_x >= 0 && snap_x <= DRAG_HANDLE_SIZE &&
952                    snap_y >= 0 && snap_y <= widget->allocation.height);
953       break;
954     case GTK_POS_TOP:
955       in_handle = (snap_x >= 0 && snap_x <= widget->allocation.width &&
956                    snap_y >= 0 && snap_y <= DRAG_HANDLE_SIZE);
957       break;
958     case GTK_POS_RIGHT:
959       in_handle = (snap_x >= widget->allocation.width - DRAG_HANDLE_SIZE && snap_x <= widget->allocation.width &&
960                    snap_y >= 0 && snap_y <= widget->allocation.height);
961       break;
962     case GTK_POS_BOTTOM:
963       in_handle = (snap_x >= 0 && snap_x <= widget->allocation.width &&
964                    snap_y >= widget->allocation.height - DRAG_HANDLE_SIZE && snap_y <= widget->allocation.height);
965       break;
966     default:
967       in_handle = FALSE;
968       break;
969     }
970
971   if (in_handle)
972     {
973       if (hb->child_detached)
974         {
975           gdk_pointer_ungrab (GDK_CURRENT_TIME);
976           
977           hb->child_detached = FALSE;
978           gdk_window_hide (hb->float_window);
979           gdk_window_reparent (hb->bin_window, widget->window, 0, 0);
980           hb->float_window_mapped = FALSE;
981           gtk_signal_emit (GTK_OBJECT (hb),
982                            handle_box_signals[SIGNAL_CHILD_ATTACHED],
983                            GTK_BIN (hb)->child);
984           
985           fleur = gdk_cursor_new (GDK_FLEUR);
986           while (gdk_pointer_grab (hb->bin_window,
987                                    FALSE,
988                                    (GDK_BUTTON1_MOTION_MASK |
989                                     GDK_POINTER_MOTION_HINT_MASK |
990                                     GDK_BUTTON_RELEASE_MASK),
991                                    NULL,
992                                    fleur,
993                                    GDK_CURRENT_TIME) != 0); /* wait for success */
994           gdk_cursor_destroy (fleur);
995           
996           gtk_widget_queue_resize (widget);
997         }
998     }
999   else
1000     {
1001       if (hb->child_detached)
1002         {
1003           gdk_window_move (hb->float_window, new_x, new_y);
1004           gdk_window_raise (hb->float_window);
1005         }
1006       else
1007         {
1008           gint width;
1009           gint height;
1010
1011           gdk_pointer_ungrab (GDK_CURRENT_TIME);
1012
1013           hb->child_detached = TRUE;
1014           width = 0;
1015           height = 0;
1016           gdk_window_get_size (hb->bin_window, &width, &height);
1017           gdk_window_move_resize (hb->float_window, new_x, new_y, width, height);
1018           gdk_window_reparent (hb->bin_window, hb->float_window, 0, 0);
1019           gdk_window_set_hints (hb->float_window, new_x, new_y, 0, 0, 0, 0, GDK_HINT_POS);
1020           gdk_window_show (hb->float_window);
1021           hb->float_window_mapped = TRUE;
1022 #if     0
1023           /* this extra move is neccessary if we use decorations, or our
1024            * window manager insists on decorations.
1025            */
1026           gdk_flush ();
1027           gdk_window_move (hb->float_window, new_x, new_y);
1028           gdk_flush ();
1029 #endif  /* 0 */
1030           gtk_signal_emit (GTK_OBJECT (hb),
1031                            handle_box_signals[SIGNAL_CHILD_DETACHED],
1032                            GTK_BIN (hb)->child);
1033           gtk_handle_box_draw_ghost (hb);
1034           
1035           fleur = gdk_cursor_new (GDK_FLEUR);
1036           while (gdk_pointer_grab (hb->bin_window,
1037                                    FALSE,
1038                                    (GDK_BUTTON1_MOTION_MASK |
1039                                     GDK_POINTER_MOTION_HINT_MASK |
1040                                     GDK_BUTTON_RELEASE_MASK),
1041                                    NULL,
1042                                    fleur,
1043                                    GDK_CURRENT_TIME) != 0); /* wait for success */
1044           gdk_cursor_destroy (fleur);
1045           
1046           gtk_widget_queue_resize (widget);
1047         }
1048     }
1049
1050   return TRUE;
1051 }
1052
1053 static void
1054 gtk_handle_box_add (GtkContainer *container,
1055                     GtkWidget    *widget)
1056 {
1057   g_return_if_fail (GTK_IS_HANDLE_BOX (container));
1058   g_return_if_fail (GTK_BIN (container)->child == NULL);
1059   g_return_if_fail (widget->parent == NULL);
1060
1061   gtk_widget_set_parent_window (widget, GTK_HANDLE_BOX (container)->bin_window);
1062   GTK_CONTAINER_CLASS (parent_class)->add (container, widget);
1063 }
1064
1065 static void
1066 gtk_handle_box_remove (GtkContainer *container,
1067                        GtkWidget    *widget)
1068 {
1069   GtkHandleBox *hb;
1070
1071   g_return_if_fail (GTK_IS_HANDLE_BOX (container));
1072   g_return_if_fail (GTK_BIN (container)->child == widget);
1073
1074   hb = GTK_HANDLE_BOX (container);
1075
1076   if (hb->child_detached)
1077     {
1078       hb->child_detached = FALSE;
1079       if (GTK_WIDGET_REALIZED (hb))
1080         {
1081           gdk_window_hide (hb->float_window);
1082           gdk_window_reparent (hb->bin_window, GTK_WIDGET (hb)->window, 0, 0);
1083         }
1084       hb->float_window_mapped = FALSE;
1085     }
1086   if (hb->in_drag)
1087     {
1088       gdk_pointer_ungrab (GDK_CURRENT_TIME);
1089       gtk_grab_remove (GTK_WIDGET (container));
1090       hb->in_drag = FALSE;
1091     }
1092   
1093   GTK_CONTAINER_CLASS (parent_class)->remove (container, widget);
1094 }
1095
1096 static gint
1097 gtk_handle_box_delete_event (GtkWidget *widget,
1098                              GdkEventAny  *event)
1099 {
1100   GtkHandleBox *hb;
1101
1102   g_return_val_if_fail (widget != NULL, FALSE);
1103   g_return_val_if_fail (GTK_IS_HANDLE_BOX (widget), FALSE);
1104   g_return_val_if_fail (event != NULL, FALSE);
1105
1106   hb = GTK_HANDLE_BOX (widget);
1107   g_return_val_if_fail (event->window == hb->float_window, FALSE);
1108   
1109   hb->child_detached = FALSE;
1110   gdk_window_hide (hb->float_window);
1111   gdk_window_reparent (hb->bin_window, widget->window, 0, 0);
1112   hb->float_window_mapped = FALSE;
1113   gtk_signal_emit (GTK_OBJECT (hb),
1114                    handle_box_signals[SIGNAL_CHILD_ATTACHED],
1115                    GTK_BIN (hb)->child);
1116   if (hb->in_drag)
1117     {
1118       gdk_pointer_ungrab (GDK_CURRENT_TIME);
1119       gtk_grab_remove (widget);
1120       hb->in_drag = FALSE;
1121     }
1122   
1123   gtk_widget_queue_resize (GTK_WIDGET (hb));
1124
1125   return TRUE;
1126 }
1127