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