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