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