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