]> Pileus Git - ~andy/gtk/blob - gtk/gtkhandlebox.c
Added style_set handler.
[~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->fleur_cursor = gdk_cursor_new (GDK_FLEUR);
187   handle_box->dragoff_x = 0;
188   handle_box->dragoff_y = 0;
189 }
190
191 GtkWidget*
192 gtk_handle_box_new (void)
193 {
194   return GTK_WIDGET (gtk_type_new (gtk_handle_box_get_type ()));
195 }
196
197 static void
198 gtk_handle_box_destroy (GtkObject *object)
199 {
200   GtkHandleBox *hb;
201
202   g_return_if_fail (object != NULL);
203   g_return_if_fail (GTK_IS_HANDLE_BOX (object));
204
205   hb = GTK_HANDLE_BOX (object);
206
207   if (GTK_OBJECT_CLASS (parent_class)->destroy)
208     (* GTK_OBJECT_CLASS (parent_class)->destroy) (object);
209 }
210
211 static void
212 gtk_handle_box_map (GtkWidget *widget)
213 {
214   GtkBin *bin;
215   GtkHandleBox *hb;
216
217   g_return_if_fail (widget != NULL);
218   g_return_if_fail (GTK_IS_HANDLE_BOX (widget));
219
220   GTK_WIDGET_SET_FLAGS (widget, GTK_MAPPED);
221
222   bin = GTK_BIN (widget);
223   hb = GTK_HANDLE_BOX (widget);
224
225   gdk_window_show (hb->bin_window);
226   gdk_window_show (widget->window);
227
228   if (hb->child_detached && !hb->float_window_mapped)
229     {
230       gdk_window_show (hb->float_window);
231       hb->float_window_mapped = TRUE;
232     }
233
234   if (bin->child &&
235       GTK_WIDGET_VISIBLE (bin->child) &&
236       !GTK_WIDGET_MAPPED (bin->child))
237     gtk_widget_map (bin->child);
238 }
239
240 static void
241 gtk_handle_box_unmap (GtkWidget *widget)
242 {
243   GtkHandleBox *hb;
244
245   g_return_if_fail (widget != NULL);
246   g_return_if_fail (GTK_IS_HANDLE_BOX (widget));
247
248   GTK_WIDGET_UNSET_FLAGS (widget, GTK_MAPPED);
249
250   hb = GTK_HANDLE_BOX (widget);
251
252   gdk_window_hide (widget->window);
253   if (hb->float_window_mapped)
254     {
255       gdk_window_hide (hb->float_window);
256       hb->float_window_mapped = FALSE;
257     }
258 }
259
260 static void
261 gtk_handle_box_realize (GtkWidget *widget)
262 {
263   GdkWindowAttr attributes;
264   gint attributes_mask;
265   GtkHandleBox *hb;
266
267   g_return_if_fail (widget != NULL);
268   g_return_if_fail (GTK_IS_HANDLE_BOX (widget));
269
270   hb = GTK_HANDLE_BOX (widget);
271
272   GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
273
274   attributes.x = widget->allocation.x;
275   attributes.y = widget->allocation.y;
276   attributes.width = widget->allocation.width;
277   attributes.height = widget->allocation.height;
278   attributes.window_type = GDK_WINDOW_CHILD;
279   attributes.wclass = GDK_INPUT_OUTPUT;
280   attributes.visual = gtk_widget_get_visual (widget);
281   attributes.colormap = gtk_widget_get_colormap (widget);
282   attributes.event_mask = (gtk_widget_get_events (widget)
283                            | GDK_EXPOSURE_MASK);
284   attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
285   widget->window = gdk_window_new (gtk_widget_get_parent_window (widget), &attributes, attributes_mask);
286   gdk_window_set_user_data (widget->window, widget);
287
288   attributes.x = 0;
289   attributes.y = 0;
290   attributes.width = widget->allocation.width;
291   attributes.height = widget->allocation.height;
292   attributes.window_type = GDK_WINDOW_CHILD;
293   attributes.event_mask |= (gtk_widget_get_events (widget) |
294                             GDK_EXPOSURE_MASK |
295                             GDK_BUTTON1_MOTION_MASK |
296                             GDK_POINTER_MOTION_HINT_MASK |
297                             GDK_BUTTON_PRESS_MASK |
298                             GDK_BUTTON_RELEASE_MASK);
299   attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
300   hb->bin_window = gdk_window_new (widget->window, &attributes, attributes_mask);
301   gdk_window_set_user_data (hb->bin_window, widget);
302   if (GTK_BIN (hb)->child)
303     gtk_widget_set_parent_window (GTK_BIN (hb)->child, hb->bin_window);
304   
305   attributes.x = 0;
306   attributes.y = 0;
307   attributes.width = widget->requisition.width;
308   attributes.height = widget->requisition.height;
309   attributes.window_type = GDK_WINDOW_TOPLEVEL;
310   attributes.wclass = GDK_INPUT_OUTPUT;
311   attributes.visual = gtk_widget_get_visual (widget);
312   attributes.colormap = gtk_widget_get_colormap (widget);
313   attributes.event_mask = (gtk_widget_get_events (widget) |
314                            GDK_KEY_PRESS_MASK |
315                            GDK_ENTER_NOTIFY_MASK |
316                            GDK_LEAVE_NOTIFY_MASK |
317                            GDK_FOCUS_CHANGE_MASK |
318                            GDK_STRUCTURE_MASK);
319   attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
320   hb->float_window = gdk_window_new (NULL, &attributes, attributes_mask);
321   gdk_window_set_user_data (hb->float_window, widget);
322   gdk_window_set_decorations (hb->float_window, 0);
323   
324
325   widget->style = gtk_style_attach (widget->style, widget->window);
326   gtk_style_set_background (widget->style, widget->window, GTK_WIDGET_STATE (hb));
327   gtk_style_set_background (widget->style, hb->bin_window, GTK_WIDGET_STATE (hb));
328   gtk_style_set_background (widget->style, hb->float_window, GTK_WIDGET_STATE (hb));
329 }
330
331 static void
332 gtk_handle_box_unrealize (GtkWidget *widget)
333 {
334   GtkHandleBox *hb;
335
336   g_return_if_fail (widget != NULL);
337   g_return_if_fail (GTK_IS_HANDLE_BOX (widget));
338
339   hb = GTK_HANDLE_BOX (widget);
340
341   gdk_window_set_user_data (hb->bin_window, NULL);
342   gdk_window_destroy (hb->bin_window);
343   hb->bin_window = NULL;
344   gdk_window_set_user_data (hb->float_window, NULL);
345   gdk_window_destroy (hb->float_window);
346   hb->float_window = NULL;
347
348   gdk_cursor_destroy (hb->fleur_cursor);
349   hb->fleur_cursor = NULL;
350
351   if (GTK_WIDGET_CLASS (parent_class)->unrealize)
352     (* GTK_WIDGET_CLASS (parent_class)->unrealize) (widget);
353 }
354
355 static void
356 gtk_handle_box_style_set (GtkWidget *widget,
357                           GtkStyle  *previous_style)
358 {
359   GtkHandleBox *hb;
360
361   g_return_if_fail (widget != NULL);
362   g_return_if_fail (GTK_IS_HANDLE_BOX (widget));
363
364   hb = GTK_HANDLE_BOX (widget);
365
366   if (GTK_WIDGET_REALIZED (widget) &&
367       !GTK_WIDGET_NO_WINDOW (widget))
368     {
369       gtk_style_set_background (widget->style, widget->window,
370 widget->state);
371       gtk_style_set_background (widget->style, hb->bin_window, widget->state);
372       gtk_style_set_background (widget->style, hb->float_window, widget->state);
373       if (GTK_WIDGET_DRAWABLE (widget))
374         gdk_window_clear (widget->window);
375     }
376 }
377
378 static void
379 gtk_handle_box_size_request (GtkWidget      *widget,
380                              GtkRequisition *requisition)
381 {
382   GtkBin *bin;
383   GtkHandleBox *hb;
384
385   g_return_if_fail (widget != NULL);
386   g_return_if_fail (GTK_IS_HANDLE_BOX (widget));
387   g_return_if_fail (requisition != NULL);
388
389   bin = GTK_BIN (widget);
390   hb = GTK_HANDLE_BOX (widget);
391
392   if (hb->handle_position == GTK_POS_LEFT ||
393       hb->handle_position == GTK_POS_RIGHT)
394     {
395       requisition->width = DRAG_HANDLE_SIZE;
396       requisition->height = 0;
397     }
398   else
399     {
400       requisition->width = 0;
401       requisition->height = DRAG_HANDLE_SIZE;
402     }
403
404   /* if our child is not visible, we still request its size, since we
405    * won't have any usefull hint for our size otherwise.
406    */
407   if (bin->child)
408     gtk_widget_size_request (bin->child, &bin->child->requisition);
409
410   if (hb->child_detached)
411     {
412       if (!hb->shrink_on_detach)
413         {
414           if (hb->handle_position == GTK_POS_LEFT ||
415               hb->handle_position == GTK_POS_RIGHT)
416             requisition->height += bin->child->requisition.height;
417           else
418             requisition->width += bin->child->requisition.width;
419         }
420       else
421         {
422           if (hb->handle_position == GTK_POS_LEFT ||
423               hb->handle_position == GTK_POS_RIGHT)
424             requisition->height += widget->style->klass->ythickness;
425           else
426             requisition->width += widget->style->klass->xthickness;
427         }
428     }
429   else
430     {
431       requisition->width += GTK_CONTAINER (widget)->border_width * 2;
432       requisition->height += GTK_CONTAINER (widget)->border_width * 2;
433       
434       if (bin->child)
435         {
436           requisition->width += bin->child->requisition.width;
437           requisition->height += bin->child->requisition.height;
438         }
439       else
440         {
441           requisition->width += CHILDLESS_SIZE;
442           requisition->height += CHILDLESS_SIZE;
443         }
444     }
445 }
446
447 static void
448 gtk_handle_box_size_allocate (GtkWidget     *widget,
449                               GtkAllocation *allocation)
450 {
451   GtkBin *bin;
452   GtkHandleBox *hb;
453   
454   g_return_if_fail (widget != NULL);
455   g_return_if_fail (GTK_IS_HANDLE_BOX (widget));
456   g_return_if_fail (allocation != NULL);
457   
458   bin = GTK_BIN (widget);
459   hb = GTK_HANDLE_BOX (widget);
460   
461   widget->allocation.x = allocation->x;
462
463   if (hb->child_detached)
464     {
465       guint max_req_height;
466       guint max_req_width;
467
468       max_req_height = MAX (widget->requisition.height,
469                             bin->child->requisition.height +
470                             2 * widget->style->klass->ythickness);
471       max_req_width = MAX (widget->requisition.width,
472                            bin->child->requisition.width +
473                            2 * widget->style->klass->xthickness);
474       
475       if (allocation->height > max_req_height)
476         widget->allocation.y = allocation->y +
477           (allocation->height - max_req_height) / 2;
478       else
479         widget->allocation.y = allocation->y;
480       
481       widget->allocation.height = MIN (allocation->height, max_req_height);
482       widget->allocation.width = MIN (allocation->width, max_req_width);
483     }
484   else
485     {
486       widget->allocation = *allocation;
487     }  
488
489   if (GTK_WIDGET_REALIZED (hb))
490     gdk_window_move_resize (widget->window,
491                             widget->allocation.x,
492                             widget->allocation.y,
493                             widget->allocation.width,
494                             widget->allocation.height);
495
496
497   if (bin->child && GTK_WIDGET_VISIBLE (bin->child))
498     {
499       GtkWidget *child;
500       GtkAllocation child_allocation;
501       guint border_width;
502
503       child = bin->child;
504       border_width = GTK_CONTAINER (widget)->border_width;
505
506       child_allocation.x = border_width;
507       child_allocation.y = border_width;
508       if (hb->handle_position == GTK_POS_LEFT)
509         child_allocation.x += DRAG_HANDLE_SIZE;
510       else if (hb->handle_position == GTK_POS_TOP)
511         child_allocation.y += DRAG_HANDLE_SIZE;
512
513       if (hb->child_detached)
514         {
515           guint float_width;
516           guint float_height;
517           
518           child_allocation.width = child->requisition.width;
519           child_allocation.height = child->requisition.height;
520           
521           float_width = child_allocation.width + 2 * border_width;
522           float_height = child_allocation.height + 2 * border_width;
523           
524           if (hb->handle_position == GTK_POS_LEFT ||
525               hb->handle_position == GTK_POS_RIGHT)
526             float_width += DRAG_HANDLE_SIZE;
527           else
528             float_height += DRAG_HANDLE_SIZE;
529
530           if (GTK_WIDGET_REALIZED (hb))
531             {
532               gdk_window_resize (hb->float_window,
533                                  float_width,
534                                  float_height);
535               gdk_window_move_resize (hb->bin_window,
536                                       0,
537                                       0,
538                                       float_width,
539                                       float_height);
540             }
541         }
542       else
543         {
544           child_allocation.width = MAX (1, widget->allocation.width - 2 * border_width);
545           child_allocation.height = MAX (1, widget->allocation.height - 2 * border_width);
546
547           if (hb->handle_position == GTK_POS_LEFT ||
548               hb->handle_position == GTK_POS_RIGHT)
549             child_allocation.width -= DRAG_HANDLE_SIZE;
550           else
551             child_allocation.height -= DRAG_HANDLE_SIZE;
552           
553           if (GTK_WIDGET_REALIZED (hb))
554             gdk_window_move_resize (hb->bin_window,
555                                     0,
556                                     0,
557                                     widget->allocation.width,
558                                     widget->allocation.height);
559         }
560
561       gtk_widget_size_allocate (bin->child, &child_allocation);
562     }
563 }
564
565 static void
566 gtk_handle_box_draw_ghost (GtkHandleBox *hb)
567 {
568   GtkWidget *widget;
569   guint x;
570   guint y;
571   guint width;
572   guint height;
573
574   widget = GTK_WIDGET (hb);
575
576   if (hb->handle_position == GTK_POS_LEFT ||
577       hb->handle_position == GTK_POS_RIGHT)
578     {
579       x = hb->handle_position == GTK_POS_LEFT ? 0 : widget->allocation.width;
580       y = 0;
581       width = DRAG_HANDLE_SIZE;
582       height = widget->allocation.height;
583     }
584   else
585     {
586       x = 0;
587       y = hb->handle_position == GTK_POS_TOP ? 0 : widget->allocation.height;
588       width = widget->allocation.width;
589       height = DRAG_HANDLE_SIZE;
590     }
591   gtk_draw_shadow (widget->style,
592                    widget->window,
593                    GTK_WIDGET_STATE (widget),
594                    GTK_SHADOW_ETCHED_IN,
595                    x,
596                    y,
597                    width,
598                    height);
599   /*
600   if (hb->handle_position == GTK_POS_LEFT ||
601       hb->handle_position == GTK_POS_RIGHT)
602     gtk_draw_hline (widget->style,
603                     widget->window,
604                     GTK_WIDGET_STATE (widget),
605                     hb->handle_position == GTK_POS_LEFT ? DRAG_HANDLE_SIZE : widget->allocation.width - DRAG_HANDLE_SIZE,
606                     widget->allocation.width - DRAG_HANDLE_SIZE,
607                     widget->allocation.height / 2);
608   else
609     gtk_draw_vline (widget->style,
610                     widget->window,
611                     GTK_WIDGET_STATE (widget),
612                     hb->handle_position == GTK_POS_TOP ? DRAG_HANDLE_SIZE : widget->allocation.height - DRAG_HANDLE_SIZE,
613                     widget->allocation.height - DRAG_HANDLE_SIZE,
614                     widget->allocation.width / 2);
615                     */
616 }
617
618 static void
619 draw_textured_frame (GtkWidget *widget, GdkWindow *window, GdkRectangle *rect, GtkShadowType shadow)
620 {
621   int x, y;
622   int xthick, ythick;
623   GdkGC *light_gc, *dark_gc;
624
625   gdk_draw_rectangle (window,
626                       widget->style->bg_gc[GTK_STATE_NORMAL],
627                       TRUE,
628                       rect->x, rect->y,
629                       rect->width, rect->height);
630
631   light_gc = widget->style->light_gc[GTK_STATE_NORMAL];
632   dark_gc = widget->style->dark_gc[GTK_STATE_NORMAL];
633
634   xthick = widget->style->klass->xthickness;
635   ythick = widget->style->klass->ythickness;
636
637   gdk_gc_set_clip_rectangle (light_gc, rect);
638   gdk_gc_set_clip_rectangle (dark_gc, rect);
639
640   for (y = rect->y + ythick; y < (rect->y + rect->height - ythick); y += 3)
641     for (x = rect->x + xthick; x < (rect->x + rect->width - xthick); x += 6)
642       {
643         gdk_draw_point (window, light_gc, x, y);
644         gdk_draw_point (window, dark_gc, x + 1, y + 1);
645
646         gdk_draw_point (window, light_gc, x + 3, y + 1);
647         gdk_draw_point (window, dark_gc, x + 4, y + 2);
648       }
649         
650   gdk_gc_set_clip_rectangle (light_gc, NULL);
651   gdk_gc_set_clip_rectangle (dark_gc, NULL);
652
653   gtk_draw_shadow (widget->style, window,
654                    GTK_STATE_NORMAL, shadow,
655                    rect->x, rect->y,
656                    rect->width, rect->height);
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);
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   
805   g_return_val_if_fail (widget != NULL, FALSE);
806   g_return_val_if_fail (GTK_IS_HANDLE_BOX (widget), FALSE);
807   g_return_val_if_fail (event != NULL, FALSE);
808
809   hb = GTK_HANDLE_BOX (widget);
810
811   event_handled = FALSE;
812   if (event->button == 1 &&
813       event->type == GDK_BUTTON_PRESS)
814     {
815       GtkWidget *child;
816       gboolean in_handle;
817       
818       child = GTK_BIN (hb)->child;
819       
820       switch (hb->handle_position)
821         {
822         case GTK_POS_LEFT:
823           in_handle = event->x < DRAG_HANDLE_SIZE;
824           break;
825         case GTK_POS_TOP:
826           in_handle = event->y < DRAG_HANDLE_SIZE;
827           break;
828         case GTK_POS_RIGHT:
829           in_handle = event->x > 2 * GTK_CONTAINER (hb)->border_width + child->allocation.width;
830           break;
831         case GTK_POS_BOTTOM:
832           in_handle = event->y > 2 * GTK_CONTAINER (hb)->border_width + child->allocation.height;
833           break;
834         default:
835           in_handle = FALSE;
836           break;
837         }
838
839       if (!child)
840         {
841           in_handle = FALSE;
842           event_handled = TRUE;
843         }
844       
845       if (in_handle)
846         {
847           hb->dragoff_x = event->x;
848           hb->dragoff_y = event->y;
849
850           gtk_grab_add (widget);
851           hb->in_drag = TRUE;
852           while (gdk_pointer_grab (hb->bin_window,
853                                    FALSE,
854                                    (GDK_BUTTON1_MOTION_MASK |
855                                     GDK_POINTER_MOTION_HINT_MASK |
856                                     GDK_BUTTON_RELEASE_MASK),
857                                    NULL,
858                                    hb->fleur_cursor,
859                                    GDK_CURRENT_TIME) != 0); /* wait for success */
860           event_handled = TRUE;
861         }
862     }
863   else if (event->type == GDK_BUTTON_RELEASE &&
864            hb->in_drag)
865     {
866       gdk_pointer_ungrab (GDK_CURRENT_TIME);
867       gtk_grab_remove (widget);
868       hb->in_drag = FALSE;
869       event_handled = TRUE;
870     }
871   
872   return event_handled;
873 }
874
875 static gint
876 gtk_handle_box_motion (GtkWidget      *widget,
877                        GdkEventMotion *event)
878 {
879   GtkHandleBox *hb;
880   gint new_x, new_y;
881   gint ox, oy;
882   gint snap_x, snap_y;
883   gboolean in_handle;
884
885   g_return_val_if_fail (widget != NULL, FALSE);
886   g_return_val_if_fail (GTK_IS_HANDLE_BOX (widget), FALSE);
887   g_return_val_if_fail (event != NULL, FALSE);
888
889   hb = GTK_HANDLE_BOX (widget);
890   if (!hb->in_drag)
891     return FALSE;
892
893   ox = 0;
894   oy = 0;
895   gdk_window_get_origin (hb->float_window, &ox, &oy);
896   new_x = 0;
897   new_y = 0;
898   gdk_window_get_pointer (hb->float_window, &new_x, &new_y, NULL);
899   new_x += ox;
900   new_y += oy;
901   new_x -= hb->dragoff_x;
902   new_y -= hb->dragoff_y;
903   snap_x = 0;
904   snap_y = 0;
905   gdk_window_get_pointer (widget->window, &snap_x, &snap_y, NULL);
906   
907   switch (hb->handle_position)
908     {
909     case GTK_POS_LEFT:
910       in_handle = (snap_x >= 0 && snap_x <= DRAG_HANDLE_SIZE &&
911                    snap_y >= 0 && snap_y <= widget->allocation.height);
912       break;
913     case GTK_POS_TOP:
914       in_handle = (snap_x >= 0 && snap_x <= widget->allocation.width &&
915                    snap_y >= 0 && snap_y <= DRAG_HANDLE_SIZE);
916       break;
917     case GTK_POS_RIGHT:
918       in_handle = (snap_x >= widget->allocation.width - DRAG_HANDLE_SIZE && snap_x <= widget->allocation.width &&
919                    snap_y >= 0 && snap_y <= widget->allocation.height);
920       break;
921     case GTK_POS_BOTTOM:
922       in_handle = (snap_x >= 0 && snap_x <= widget->allocation.width &&
923                    snap_y >= widget->allocation.height - DRAG_HANDLE_SIZE && snap_y <= widget->allocation.height);
924       break;
925     default:
926       in_handle = FALSE;
927       break;
928     }
929
930   if (in_handle)
931     {
932       if (hb->child_detached)
933         {
934           gdk_pointer_ungrab (GDK_CURRENT_TIME);
935           
936           hb->child_detached = FALSE;
937           gdk_window_hide (hb->float_window);
938           gdk_window_reparent (hb->bin_window, widget->window, 0, 0);
939           hb->float_window_mapped = FALSE;
940           gtk_signal_emit (GTK_OBJECT (hb),
941                            handle_box_signals[SIGNAL_CHILD_ATTACHED],
942                            GTK_BIN (hb)->child);
943           
944           while (gdk_pointer_grab (hb->bin_window,
945                                    FALSE,
946                                    (GDK_BUTTON1_MOTION_MASK |
947                                     GDK_POINTER_MOTION_HINT_MASK |
948                                     GDK_BUTTON_RELEASE_MASK),
949                                    NULL,
950                                    hb->fleur_cursor,
951                                    GDK_CURRENT_TIME) != 0); /* wait for success */
952           
953           gtk_widget_queue_resize (widget);
954         }
955     }
956   else
957     {
958       if (hb->child_detached)
959         {
960           gdk_window_move (hb->float_window, new_x, new_y);
961           gdk_window_raise (hb->float_window);
962         }
963       else
964         {
965           gint width;
966           gint height;
967
968           gdk_pointer_ungrab (GDK_CURRENT_TIME);
969
970           hb->child_detached = TRUE;
971           width = 0;
972           height = 0;
973           gdk_window_get_size (hb->bin_window, &width, &height);
974           gdk_window_move_resize (hb->float_window, new_x, new_y, width, height);
975           gdk_window_reparent (hb->bin_window, hb->float_window, 0, 0);
976           gdk_window_set_hints (hb->float_window, new_x, new_y, 0, 0, 0, 0, GDK_HINT_POS);
977           gdk_window_show (hb->float_window);
978           hb->float_window_mapped = TRUE;
979 #if     0
980           /* this extra move is neccessary if we use decorations, or our
981            * window manager insists on decorations.
982            */
983           gdk_flush ();
984           gdk_window_move (hb->float_window, new_x, new_y);
985           gdk_flush ();
986 #endif  /* 0 */
987           gtk_signal_emit (GTK_OBJECT (hb),
988                            handle_box_signals[SIGNAL_CHILD_DETACHED],
989                            GTK_BIN (hb)->child);
990           gtk_handle_box_draw_ghost (hb);
991           
992           while (gdk_pointer_grab (hb->bin_window,
993                                    FALSE,
994                                    (GDK_BUTTON1_MOTION_MASK |
995                                     GDK_POINTER_MOTION_HINT_MASK |
996                                     GDK_BUTTON_RELEASE_MASK),
997                                    NULL,
998                                    hb->fleur_cursor,
999                                    GDK_CURRENT_TIME) != 0); /* wait for success */
1000           
1001           gtk_widget_queue_resize (widget);
1002         }
1003     }
1004
1005   return TRUE;
1006 }
1007
1008 static void
1009 gtk_handle_box_add (GtkContainer *container,
1010                     GtkWidget    *widget)
1011 {
1012   g_return_if_fail (GTK_IS_HANDLE_BOX (container));
1013   g_return_if_fail (GTK_BIN (container)->child == NULL);
1014   g_return_if_fail (widget->parent == NULL);
1015
1016   gtk_widget_set_parent_window (widget, GTK_HANDLE_BOX (container)->bin_window);
1017   GTK_CONTAINER_CLASS (parent_class)->add (container, widget);
1018 }
1019
1020 static void
1021 gtk_handle_box_remove (GtkContainer *container,
1022                        GtkWidget    *widget)
1023 {
1024   GtkHandleBox *hb;
1025
1026   g_return_if_fail (GTK_IS_HANDLE_BOX (container));
1027   g_return_if_fail (GTK_BIN (container)->child == widget);
1028
1029   hb = GTK_HANDLE_BOX (container);
1030
1031   if (hb->child_detached)
1032     {
1033       hb->child_detached = FALSE;
1034       if (GTK_WIDGET_REALIZED (hb))
1035         {
1036           gdk_window_hide (hb->float_window);
1037           gdk_window_reparent (hb->bin_window, GTK_WIDGET (hb)->window, 0, 0);
1038         }
1039       hb->float_window_mapped = FALSE;
1040     }
1041   if (hb->in_drag)
1042     {
1043       gdk_pointer_ungrab (GDK_CURRENT_TIME);
1044       gtk_grab_remove (GTK_WIDGET (container));
1045       hb->in_drag = FALSE;
1046     }
1047   
1048   GTK_CONTAINER_CLASS (parent_class)->remove (container, widget);
1049 }
1050
1051 static gint
1052 gtk_handle_box_delete_event (GtkWidget *widget,
1053                              GdkEventAny  *event)
1054 {
1055   GtkHandleBox *hb;
1056
1057   g_return_val_if_fail (widget != NULL, FALSE);
1058   g_return_val_if_fail (GTK_IS_HANDLE_BOX (widget), FALSE);
1059   g_return_val_if_fail (event != NULL, FALSE);
1060
1061   hb = GTK_HANDLE_BOX (widget);
1062   g_return_val_if_fail (event->window == hb->float_window, FALSE);
1063   
1064   hb->child_detached = FALSE;
1065   gdk_window_hide (hb->float_window);
1066   gdk_window_reparent (hb->bin_window, widget->window, 0, 0);
1067   hb->float_window_mapped = FALSE;
1068   gtk_signal_emit (GTK_OBJECT (hb),
1069                    handle_box_signals[SIGNAL_CHILD_ATTACHED],
1070                    GTK_BIN (hb)->child);
1071   if (hb->in_drag)
1072     {
1073       gdk_pointer_ungrab (GDK_CURRENT_TIME);
1074       gtk_grab_remove (widget);
1075       hb->in_drag = FALSE;
1076     }
1077   
1078   gtk_widget_queue_resize (GTK_WIDGET (hb));
1079
1080   return TRUE;
1081 }
1082