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