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