]> Pileus Git - ~andy/gtk/blob - gtk/gtkscrolledwindow.c
applied patch from Andreas Persenius <ndap@swipnet.se> that updates the
[~andy/gtk] / gtk / gtkscrolledwindow.c
1 /* GTK - The GIMP Toolkit
2  * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17  * Boston, MA 02111-1307, USA.
18  */
19
20 /*
21  * Modified by the GTK+ Team and others 1997-2000.  See the AUTHORS
22  * file for a list of people on the GTK+ Team.  See the ChangeLog
23  * files for a list of changes.  These files are distributed with
24  * GTK+ at ftp://ftp.gtk.org/pub/gtk/. 
25  */
26
27 #include "gtkscrolledwindow.h"
28 #include "gtksignal.h"
29
30
31 /* scrolled window policy and size requisition handling:
32  *
33  * gtk size requisition works as follows:
34  *   a widget upon size-request reports the width and height that it finds
35  *   to be best suited to display its contents, including children.
36  *   the width and/or height reported from a widget upon size requisition
37  *   may be overidden by the user by specifying a width and/or height
38  *   other than 0 through gtk_widget_set_usize().
39  *
40  * a scrolled window needs (for imlementing all three policy types) to
41  * request its width and height based on two different rationales.
42  * 1)   the user wants the scrolled window to just fit into the space
43  *      that it gets allocated for a specifc dimension.
44  * 1.1) this does not apply if the user specified a concrete value
45  *      value for that specific dimension by either specifying usize for the
46  *      scrolled window or for its child.
47  * 2)   the user wants the scrolled window to take as much space up as
48  *      is desired by the child for a specifc dimension (i.e. POLICY_NEVER).
49  *
50  * also, kinda obvious:
51  * 3)   a user would certainly not have choosen a scrolled window as a container
52  *      for the child, if the resulting allocation takes up more space than the
53  *      child would have allocated without the scrolled window.
54  *
55  * conclusions:
56  * A) from 1) follows: the scrolled window shouldn't request more space for a
57  *    specifc dimension than is required at minimum.
58  * B) from 1.1) follows: the requisition may be overidden by usize of the scrolled
59  *    window (done automatically) or by usize of the child (needs to be checked).
60  * C) from 2) follows: for POLICY_NEVER, the scrolled window simply reports the
61  *    child's dimension.
62  * D) from 3) follows: the scrolled window child's minimum width and minimum height
63  *    under A) at least correspond to the space taken up by its scrollbars.
64  */
65
66 #define SCROLLBAR_SPACING(w) (GTK_SCROLLED_WINDOW_GET_CLASS (w)->scrollbar_spacing)
67
68 #define DEFAULT_SCROLLBAR_SPACING  3
69
70 enum {
71   ARG_0,
72   ARG_HADJUSTMENT,
73   ARG_VADJUSTMENT,
74   ARG_HSCROLLBAR_POLICY,
75   ARG_VSCROLLBAR_POLICY,
76   ARG_WINDOW_PLACEMENT,
77   ARG_SHADOW
78 };
79
80
81 static void gtk_scrolled_window_class_init         (GtkScrolledWindowClass *klass);
82 static void gtk_scrolled_window_init               (GtkScrolledWindow      *scrolled_window);
83 static void gtk_scrolled_window_set_arg            (GtkObject              *object,
84                                                     GtkArg                 *arg,
85                                                     guint                   arg_id);
86 static void gtk_scrolled_window_get_arg            (GtkObject              *object,
87                                                     GtkArg                 *arg,
88                                                     guint                   arg_id);
89 static void gtk_scrolled_window_destroy            (GtkObject              *object);
90 static void gtk_scrolled_window_finalize           (GObject                *object);
91 static void gtk_scrolled_window_map                (GtkWidget              *widget);
92 static void gtk_scrolled_window_unmap              (GtkWidget              *widget);
93 static void gtk_scrolled_window_draw               (GtkWidget              *widget,
94                                                     GdkRectangle           *area);
95 static gint gtk_scrolled_window_expose             (GtkWidget              *widget,
96                                                     GdkEventExpose         *event);
97 static void gtk_scrolled_window_size_request       (GtkWidget              *widget,
98                                                     GtkRequisition         *requisition);
99 static void gtk_scrolled_window_size_allocate      (GtkWidget              *widget,
100                                                     GtkAllocation          *allocation);
101 static gint gtk_scrolled_window_scroll_event       (GtkWidget              *widget,
102                                                     GdkEventScroll         *event);
103 static void gtk_scrolled_window_add                (GtkContainer           *container,
104                                                     GtkWidget              *widget);
105 static void gtk_scrolled_window_remove             (GtkContainer           *container,
106                                                     GtkWidget              *widget);
107 static void gtk_scrolled_window_forall             (GtkContainer           *container,
108                                                     gboolean                include_internals,
109                                                     GtkCallback             callback,
110                                                     gpointer                callback_data);
111 static void gtk_scrolled_window_relative_allocation(GtkWidget              *widget,
112                                                     GtkAllocation          *allocation);
113 static void gtk_scrolled_window_adjustment_changed (GtkAdjustment          *adjustment,
114                                                     gpointer                data);
115
116 static GtkContainerClass *parent_class = NULL;
117
118
119 GtkType
120 gtk_scrolled_window_get_type (void)
121 {
122   static GtkType scrolled_window_type = 0;
123
124   if (!scrolled_window_type)
125     {
126       static const GtkTypeInfo scrolled_window_info =
127       {
128         "GtkScrolledWindow",
129         sizeof (GtkScrolledWindow),
130         sizeof (GtkScrolledWindowClass),
131         (GtkClassInitFunc) gtk_scrolled_window_class_init,
132         (GtkObjectInitFunc) gtk_scrolled_window_init,
133         /* reserved_1 */ NULL,
134         /* reserved_2 */ NULL,
135         (GtkClassInitFunc) NULL,
136       };
137
138       scrolled_window_type = gtk_type_unique (GTK_TYPE_BIN, &scrolled_window_info);
139     }
140
141   return scrolled_window_type;
142 }
143
144 static void
145 gtk_scrolled_window_class_init (GtkScrolledWindowClass *class)
146 {
147   GObjectClass *gobject_class = G_OBJECT_CLASS (class);
148   GtkObjectClass *object_class;
149   GtkWidgetClass *widget_class;
150   GtkContainerClass *container_class;
151
152   object_class = (GtkObjectClass*) class;
153   widget_class = (GtkWidgetClass*) class;
154   container_class = (GtkContainerClass*) class;
155   parent_class = gtk_type_class (GTK_TYPE_BIN);
156
157   gobject_class->finalize = gtk_scrolled_window_finalize;
158
159   object_class->set_arg = gtk_scrolled_window_set_arg;
160   object_class->get_arg = gtk_scrolled_window_get_arg;
161   object_class->destroy = gtk_scrolled_window_destroy;
162
163   widget_class->map = gtk_scrolled_window_map;
164   widget_class->unmap = gtk_scrolled_window_unmap;
165   widget_class->draw = gtk_scrolled_window_draw;
166   widget_class->expose_event = gtk_scrolled_window_expose;
167   widget_class->size_request = gtk_scrolled_window_size_request;
168   widget_class->size_allocate = gtk_scrolled_window_size_allocate;
169   widget_class->scroll_event = gtk_scrolled_window_scroll_event;
170
171   container_class->add = gtk_scrolled_window_add;
172   container_class->remove = gtk_scrolled_window_remove;
173   container_class->forall = gtk_scrolled_window_forall;
174
175   class->scrollbar_spacing = DEFAULT_SCROLLBAR_SPACING;
176
177   gtk_object_add_arg_type ("GtkScrolledWindow::hadjustment",
178                            GTK_TYPE_ADJUSTMENT,
179                            GTK_ARG_READWRITE | GTK_ARG_CONSTRUCT,
180                            ARG_HADJUSTMENT);
181   gtk_object_add_arg_type ("GtkScrolledWindow::vadjustment",
182                            GTK_TYPE_ADJUSTMENT,
183                            GTK_ARG_READWRITE | GTK_ARG_CONSTRUCT,
184                            ARG_VADJUSTMENT);
185   gtk_object_add_arg_type ("GtkScrolledWindow::hscrollbar_policy",
186                            GTK_TYPE_POLICY_TYPE,
187                            GTK_ARG_READWRITE,
188                            ARG_HSCROLLBAR_POLICY);
189   gtk_object_add_arg_type ("GtkScrolledWindow::vscrollbar_policy",
190                            GTK_TYPE_POLICY_TYPE,
191                            GTK_ARG_READWRITE,
192                            ARG_VSCROLLBAR_POLICY);
193   gtk_object_add_arg_type ("GtkScrolledWindow::window_placement",
194                            GTK_TYPE_CORNER_TYPE,
195                            GTK_ARG_READWRITE,
196                            ARG_WINDOW_PLACEMENT);
197   gtk_object_add_arg_type ("GtkScrolledWindow::shadow",
198                            GTK_TYPE_SHADOW_TYPE,
199                            GTK_ARG_READWRITE,
200                            ARG_SHADOW);
201 }
202
203 static void
204 gtk_scrolled_window_set_arg (GtkObject        *object,
205                              GtkArg           *arg,
206                              guint             arg_id)
207 {
208   GtkScrolledWindow *scrolled_window;
209
210   scrolled_window = GTK_SCROLLED_WINDOW (object);
211
212   switch (arg_id)
213     {
214     case ARG_HADJUSTMENT:
215       gtk_scrolled_window_set_hadjustment (scrolled_window, GTK_VALUE_POINTER (*arg));
216       break;
217     case ARG_VADJUSTMENT:
218       gtk_scrolled_window_set_vadjustment (scrolled_window, GTK_VALUE_POINTER (*arg));
219       break;
220     case ARG_HSCROLLBAR_POLICY:
221       gtk_scrolled_window_set_policy (scrolled_window,
222                                       GTK_VALUE_ENUM (*arg),
223                                       scrolled_window->vscrollbar_policy);
224       break;
225     case ARG_VSCROLLBAR_POLICY:
226       gtk_scrolled_window_set_policy (scrolled_window,
227                                       scrolled_window->hscrollbar_policy,
228                                       GTK_VALUE_ENUM (*arg));
229       break;
230     case ARG_WINDOW_PLACEMENT:
231       gtk_scrolled_window_set_placement (scrolled_window,
232                                          GTK_VALUE_ENUM (*arg));
233       break;
234     case ARG_SHADOW:
235       gtk_scrolled_window_set_shadow_type (scrolled_window,
236                                            GTK_VALUE_ENUM (*arg));
237       break;
238     default:
239       break;
240     }
241 }
242
243 static void
244 gtk_scrolled_window_get_arg (GtkObject        *object,
245                              GtkArg           *arg,
246                              guint             arg_id)
247 {
248   GtkScrolledWindow *scrolled_window;
249
250   scrolled_window = GTK_SCROLLED_WINDOW (object);
251
252   switch (arg_id)
253     {
254     case ARG_HADJUSTMENT:
255       GTK_VALUE_POINTER (*arg) = gtk_scrolled_window_get_hadjustment (scrolled_window);
256       break;
257     case ARG_VADJUSTMENT:
258       GTK_VALUE_POINTER (*arg) = gtk_scrolled_window_get_vadjustment (scrolled_window);
259       break;
260     case ARG_HSCROLLBAR_POLICY:
261       GTK_VALUE_ENUM (*arg) = scrolled_window->hscrollbar_policy;
262       break;
263     case ARG_VSCROLLBAR_POLICY:
264       GTK_VALUE_ENUM (*arg) = scrolled_window->vscrollbar_policy;
265       break;
266     case ARG_WINDOW_PLACEMENT:
267       GTK_VALUE_ENUM (*arg) = scrolled_window->window_placement;
268       break;
269     case ARG_SHADOW:
270       GTK_VALUE_ENUM (*arg) = scrolled_window->shadow_type;
271       break;
272     default:
273       arg->type = GTK_TYPE_INVALID;
274       break;
275     }
276 }
277
278 static void
279 gtk_scrolled_window_init (GtkScrolledWindow *scrolled_window)
280 {
281   GTK_WIDGET_SET_FLAGS (scrolled_window, GTK_NO_WINDOW);
282
283   gtk_container_set_resize_mode (GTK_CONTAINER (scrolled_window), GTK_RESIZE_QUEUE);
284
285   scrolled_window->hscrollbar = NULL;
286   scrolled_window->vscrollbar = NULL;
287   scrolled_window->hscrollbar_policy = GTK_POLICY_ALWAYS;
288   scrolled_window->vscrollbar_policy = GTK_POLICY_ALWAYS;
289   scrolled_window->hscrollbar_visible = FALSE;
290   scrolled_window->vscrollbar_visible = FALSE;
291   scrolled_window->window_placement = GTK_CORNER_TOP_LEFT;
292   
293 }
294
295 GtkWidget*
296 gtk_scrolled_window_new (GtkAdjustment *hadjustment,
297                          GtkAdjustment *vadjustment)
298 {
299   GtkWidget *scrolled_window;
300
301   if (hadjustment)
302     g_return_val_if_fail (GTK_IS_ADJUSTMENT (hadjustment), NULL);
303
304   if (vadjustment)
305     g_return_val_if_fail (GTK_IS_ADJUSTMENT (vadjustment), NULL);
306
307   scrolled_window = gtk_widget_new (GTK_TYPE_SCROLLED_WINDOW,
308                                     "hadjustment", hadjustment,
309                                     "vadjustment", vadjustment,
310                                     NULL);
311
312   return scrolled_window;
313 }
314
315 void
316 gtk_scrolled_window_set_hadjustment (GtkScrolledWindow *scrolled_window,
317                                      GtkAdjustment     *hadjustment)
318 {
319   GtkBin *bin;
320
321   g_return_if_fail (scrolled_window != NULL);
322   g_return_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window));
323   if (hadjustment)
324     g_return_if_fail (GTK_IS_ADJUSTMENT (hadjustment));
325   else
326     hadjustment = (GtkAdjustment*) gtk_object_new (GTK_TYPE_ADJUSTMENT, NULL);
327
328   bin = GTK_BIN (scrolled_window);
329
330   if (!scrolled_window->hscrollbar)
331     {
332       gtk_widget_push_composite_child ();
333       scrolled_window->hscrollbar = gtk_hscrollbar_new (hadjustment);
334       gtk_widget_set_composite_name (scrolled_window->hscrollbar, "hscrollbar");
335       gtk_widget_pop_composite_child ();
336
337       gtk_widget_set_parent (scrolled_window->hscrollbar, GTK_WIDGET (scrolled_window));
338       gtk_widget_ref (scrolled_window->hscrollbar);
339       gtk_widget_show (scrolled_window->hscrollbar);
340     }
341   else
342     {
343       GtkAdjustment *old_adjustment;
344       
345       old_adjustment = gtk_range_get_adjustment (GTK_RANGE (scrolled_window->hscrollbar));
346       if (old_adjustment == hadjustment)
347         return;
348
349       gtk_signal_disconnect_by_func (GTK_OBJECT (old_adjustment),
350                                      GTK_SIGNAL_FUNC (gtk_scrolled_window_adjustment_changed),
351                                      scrolled_window);
352       gtk_range_set_adjustment (GTK_RANGE (scrolled_window->hscrollbar),
353                                 hadjustment);
354     }
355   hadjustment = gtk_range_get_adjustment (GTK_RANGE (scrolled_window->hscrollbar));
356   gtk_signal_connect (GTK_OBJECT (hadjustment),
357                       "changed",
358                       GTK_SIGNAL_FUNC (gtk_scrolled_window_adjustment_changed),
359                       scrolled_window);
360   gtk_scrolled_window_adjustment_changed (hadjustment, scrolled_window);
361   
362   if (bin->child)
363     gtk_widget_set_scroll_adjustments (bin->child,
364                                        gtk_range_get_adjustment (GTK_RANGE (scrolled_window->hscrollbar)),
365                                        gtk_range_get_adjustment (GTK_RANGE (scrolled_window->vscrollbar)));
366 }
367
368 void
369 gtk_scrolled_window_set_vadjustment (GtkScrolledWindow *scrolled_window,
370                                      GtkAdjustment     *vadjustment)
371 {
372   GtkBin *bin;
373
374   g_return_if_fail (scrolled_window != NULL);
375   g_return_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window));
376   if (vadjustment)
377     g_return_if_fail (GTK_IS_ADJUSTMENT (vadjustment));
378   else
379     vadjustment = (GtkAdjustment*) gtk_object_new (GTK_TYPE_ADJUSTMENT, NULL);
380
381   bin = GTK_BIN (scrolled_window);
382
383   if (!scrolled_window->vscrollbar)
384     {
385       gtk_widget_push_composite_child ();
386       scrolled_window->vscrollbar = gtk_vscrollbar_new (vadjustment);
387       gtk_widget_set_composite_name (scrolled_window->vscrollbar, "vscrollbar");
388       gtk_widget_pop_composite_child ();
389
390       gtk_widget_set_parent (scrolled_window->vscrollbar, GTK_WIDGET (scrolled_window));
391       gtk_widget_ref (scrolled_window->vscrollbar);
392       gtk_widget_show (scrolled_window->vscrollbar);
393     }
394   else
395     {
396       GtkAdjustment *old_adjustment;
397       
398       old_adjustment = gtk_range_get_adjustment (GTK_RANGE (scrolled_window->vscrollbar));
399       if (old_adjustment == vadjustment)
400         return;
401
402       gtk_signal_disconnect_by_func (GTK_OBJECT (old_adjustment),
403                                      GTK_SIGNAL_FUNC (gtk_scrolled_window_adjustment_changed),
404                                      scrolled_window);
405       gtk_range_set_adjustment (GTK_RANGE (scrolled_window->vscrollbar),
406                                 vadjustment);
407     }
408   vadjustment = gtk_range_get_adjustment (GTK_RANGE (scrolled_window->vscrollbar));
409   gtk_signal_connect (GTK_OBJECT (vadjustment),
410                       "changed",
411                       GTK_SIGNAL_FUNC (gtk_scrolled_window_adjustment_changed),
412                       scrolled_window);
413   gtk_scrolled_window_adjustment_changed (vadjustment, scrolled_window);
414
415   if (bin->child)
416     gtk_widget_set_scroll_adjustments (bin->child,
417                                        gtk_range_get_adjustment (GTK_RANGE (scrolled_window->hscrollbar)),
418                                        gtk_range_get_adjustment (GTK_RANGE (scrolled_window->vscrollbar)));
419 }
420
421 GtkAdjustment*
422 gtk_scrolled_window_get_hadjustment (GtkScrolledWindow *scrolled_window)
423 {
424   g_return_val_if_fail (scrolled_window != NULL, NULL);
425   g_return_val_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window), NULL);
426
427   return (scrolled_window->hscrollbar ?
428           gtk_range_get_adjustment (GTK_RANGE (scrolled_window->hscrollbar)) :
429           NULL);
430 }
431
432 GtkAdjustment*
433 gtk_scrolled_window_get_vadjustment (GtkScrolledWindow *scrolled_window)
434 {
435   g_return_val_if_fail (scrolled_window != NULL, NULL);
436   g_return_val_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window), NULL);
437
438   return (scrolled_window->vscrollbar ?
439           gtk_range_get_adjustment (GTK_RANGE (scrolled_window->vscrollbar)) :
440           NULL);
441 }
442
443 void
444 gtk_scrolled_window_set_policy (GtkScrolledWindow *scrolled_window,
445                                 GtkPolicyType      hscrollbar_policy,
446                                 GtkPolicyType      vscrollbar_policy)
447 {
448   g_return_if_fail (scrolled_window != NULL);
449   g_return_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window));
450
451   if ((scrolled_window->hscrollbar_policy != hscrollbar_policy) ||
452       (scrolled_window->vscrollbar_policy != vscrollbar_policy))
453     {
454       scrolled_window->hscrollbar_policy = hscrollbar_policy;
455       scrolled_window->vscrollbar_policy = vscrollbar_policy;
456
457       gtk_widget_queue_resize (GTK_WIDGET (scrolled_window));
458     }
459 }
460
461 void
462 gtk_scrolled_window_set_placement (GtkScrolledWindow *scrolled_window,
463                                    GtkCornerType      window_placement)
464 {
465   g_return_if_fail (scrolled_window != NULL);
466   g_return_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window));
467
468   if (scrolled_window->window_placement != window_placement)
469     {
470       scrolled_window->window_placement = window_placement;
471
472       gtk_widget_queue_resize (GTK_WIDGET (scrolled_window));
473     }
474 }
475
476 void
477 gtk_scrolled_window_set_shadow_type (GtkScrolledWindow *scrolled_window,
478                                      GtkShadowType      type)
479 {
480   g_return_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window));
481   g_return_if_fail (type >= GTK_SHADOW_NONE && type <= GTK_SHADOW_ETCHED_OUT);
482   
483   if (scrolled_window->shadow_type != type)
484     {
485       scrolled_window->shadow_type = type;
486
487       if (GTK_WIDGET_DRAWABLE (scrolled_window))
488         gtk_widget_queue_clear (GTK_WIDGET (scrolled_window));
489
490       gtk_widget_queue_resize (GTK_WIDGET (scrolled_window));
491     }
492 }
493
494 static void
495 gtk_scrolled_window_destroy (GtkObject *object)
496 {
497   GtkScrolledWindow *scrolled_window;
498
499   g_return_if_fail (object != NULL);
500   g_return_if_fail (GTK_IS_SCROLLED_WINDOW (object));
501
502   scrolled_window = GTK_SCROLLED_WINDOW (object);
503
504   gtk_widget_unparent (scrolled_window->hscrollbar);
505   gtk_widget_unparent (scrolled_window->vscrollbar);
506   gtk_widget_destroy (scrolled_window->hscrollbar);
507   gtk_widget_destroy (scrolled_window->vscrollbar);
508
509   GTK_OBJECT_CLASS (parent_class)->destroy (object);
510 }
511
512 static void
513 gtk_scrolled_window_finalize (GObject *object)
514 {
515   GtkScrolledWindow *scrolled_window = GTK_SCROLLED_WINDOW (object);
516
517   gtk_widget_unref (scrolled_window->hscrollbar);
518   gtk_widget_unref (scrolled_window->vscrollbar);
519
520   G_OBJECT_CLASS (parent_class)->finalize (object);
521 }
522
523 static void
524 gtk_scrolled_window_map (GtkWidget *widget)
525 {
526   GtkScrolledWindow *scrolled_window;
527
528   g_return_if_fail (widget != NULL);
529   g_return_if_fail (GTK_IS_SCROLLED_WINDOW (widget));
530
531   scrolled_window = GTK_SCROLLED_WINDOW (widget);
532
533   /* chain parent class handler to map self and child */
534   GTK_WIDGET_CLASS (parent_class)->map (widget);
535   
536   if (GTK_WIDGET_VISIBLE (scrolled_window->hscrollbar) &&
537       !GTK_WIDGET_MAPPED (scrolled_window->hscrollbar))
538     gtk_widget_map (scrolled_window->hscrollbar);
539   
540   if (GTK_WIDGET_VISIBLE (scrolled_window->vscrollbar) &&
541       !GTK_WIDGET_MAPPED (scrolled_window->vscrollbar))
542     gtk_widget_map (scrolled_window->vscrollbar);
543 }
544
545 static void
546 gtk_scrolled_window_unmap (GtkWidget *widget)
547 {
548   GtkScrolledWindow *scrolled_window;
549
550   g_return_if_fail (widget != NULL);
551   g_return_if_fail (GTK_IS_SCROLLED_WINDOW (widget));
552
553   scrolled_window = GTK_SCROLLED_WINDOW (widget);
554
555   /* chain parent class handler to unmap self and child */
556   GTK_WIDGET_CLASS (parent_class)->unmap (widget);
557   
558   if (GTK_WIDGET_MAPPED (scrolled_window->hscrollbar))
559     gtk_widget_unmap (scrolled_window->hscrollbar);
560   
561   if (GTK_WIDGET_MAPPED (scrolled_window->vscrollbar))
562     gtk_widget_unmap (scrolled_window->vscrollbar);
563 }
564
565 static void
566 gtk_scrolled_window_paint (GtkWidget    *widget,
567                            GdkRectangle *area)
568 {
569   GtkAllocation relative_allocation;
570   GtkScrolledWindow *scrolled_window = GTK_SCROLLED_WINDOW (widget);
571
572   if (scrolled_window->shadow_type != GTK_SHADOW_NONE)
573     {
574       gtk_scrolled_window_relative_allocation (widget, &relative_allocation);
575       
576       relative_allocation.x -= widget->style->xthickness;
577       relative_allocation.y -= widget->style->ythickness;
578       relative_allocation.width += 2 * widget->style->xthickness;
579       relative_allocation.height += 2 * widget->style->ythickness;
580       
581       gtk_paint_shadow (widget->style, widget->window,
582                         GTK_STATE_NORMAL, scrolled_window->shadow_type,
583                         area, widget, "scrolled_window",
584                         widget->allocation.x + relative_allocation.x,
585                         widget->allocation.y + relative_allocation.y,
586                         relative_allocation.width,
587                         relative_allocation.height);
588     }
589 }
590
591 static void
592 gtk_scrolled_window_draw (GtkWidget    *widget,
593                           GdkRectangle *area)
594 {
595   GtkScrolledWindow *scrolled_window;
596   GtkBin *bin;
597   GdkRectangle child_area;
598
599   g_return_if_fail (widget != NULL);
600   g_return_if_fail (GTK_IS_SCROLLED_WINDOW (widget));
601   g_return_if_fail (area != NULL);
602   
603   scrolled_window = GTK_SCROLLED_WINDOW (widget);
604   bin = GTK_BIN (widget);
605
606   if (GTK_WIDGET_DRAWABLE (widget))
607     {
608       gtk_scrolled_window_paint (widget, area);
609   
610       if (bin->child && GTK_WIDGET_VISIBLE (bin->child) &&
611           gtk_widget_intersect (bin->child, area, &child_area))
612         gtk_widget_draw (bin->child, &child_area);
613       
614       if (GTK_WIDGET_VISIBLE (scrolled_window->hscrollbar) &&
615           gtk_widget_intersect (scrolled_window->hscrollbar, area, &child_area))
616         gtk_widget_draw (scrolled_window->hscrollbar, &child_area);
617       
618       if (GTK_WIDGET_VISIBLE (scrolled_window->vscrollbar) &&
619           gtk_widget_intersect (scrolled_window->vscrollbar, area, &child_area))
620         gtk_widget_draw (scrolled_window->vscrollbar, &child_area);
621     }
622 }
623   
624 static gint
625 gtk_scrolled_window_expose (GtkWidget      *widget,
626                             GdkEventExpose *event)
627 {
628   GtkBin *bin = GTK_BIN (widget);
629   GdkEventExpose child_event;
630
631   if (GTK_WIDGET_DRAWABLE (widget))
632     {
633       gtk_scrolled_window_paint (widget, &event->area);
634
635       if (bin->child && GTK_WIDGET_VISIBLE (bin->child) && GTK_WIDGET_NO_WINDOW (bin->child))
636         {
637           child_event = *event;   
638           if (gtk_widget_intersect (bin->child, &event->area, &child_event.area))
639             gtk_widget_event (bin->child, (GdkEvent*) &child_event);
640         }
641
642       /* We rely on our knowledge that scrollbars are !NO_WINDOW widgets */
643     }
644
645   return FALSE;
646 }
647
648 static void
649 gtk_scrolled_window_forall (GtkContainer *container,
650                             gboolean      include_internals,
651                             GtkCallback   callback,
652                             gpointer      callback_data)
653 {
654   g_return_if_fail (container != NULL);
655   g_return_if_fail (GTK_IS_SCROLLED_WINDOW (container));
656   g_return_if_fail (callback != NULL);
657
658   GTK_CONTAINER_CLASS (parent_class)->forall (container,
659                                               include_internals,
660                                               callback,
661                                               callback_data);
662   if (include_internals)
663     {
664       GtkScrolledWindow *scrolled_window;
665
666       scrolled_window = GTK_SCROLLED_WINDOW (container);
667       
668       if (scrolled_window->vscrollbar)
669         callback (scrolled_window->vscrollbar, callback_data);
670       if (scrolled_window->hscrollbar)
671         callback (scrolled_window->hscrollbar, callback_data);
672     }
673 }
674
675 static void
676 gtk_scrolled_window_size_request (GtkWidget      *widget,
677                                   GtkRequisition *requisition)
678 {
679   GtkScrolledWindow *scrolled_window;
680   GtkBin *bin;
681   gint extra_width;
682   gint extra_height;
683   GtkRequisition hscrollbar_requisition;
684   GtkRequisition vscrollbar_requisition;
685   GtkRequisition child_requisition;
686
687   g_return_if_fail (widget != NULL);
688   g_return_if_fail (GTK_IS_SCROLLED_WINDOW (widget));
689   g_return_if_fail (requisition != NULL);
690
691   scrolled_window = GTK_SCROLLED_WINDOW (widget);
692   bin = GTK_BIN (scrolled_window);
693
694   extra_width = 0;
695   extra_height = 0;
696   requisition->width = 0;
697   requisition->height = 0;
698   
699   gtk_widget_size_request (scrolled_window->hscrollbar,
700                            &hscrollbar_requisition);
701   gtk_widget_size_request (scrolled_window->vscrollbar,
702                            &vscrollbar_requisition);
703   
704   if (bin->child && GTK_WIDGET_VISIBLE (bin->child))
705     {
706       static guint quark_aux_info = 0;
707
708       if (!quark_aux_info)
709         quark_aux_info = g_quark_from_static_string ("gtk-aux-info");
710
711       gtk_widget_size_request (bin->child, &child_requisition);
712
713       if (scrolled_window->hscrollbar_policy == GTK_POLICY_NEVER)
714         requisition->width += child_requisition.width;
715       else
716         {
717           GtkWidgetAuxInfo *aux_info;
718
719           aux_info = gtk_object_get_data_by_id (GTK_OBJECT (bin->child), quark_aux_info);
720           if (aux_info && aux_info->width > 0)
721             {
722               requisition->width += aux_info->width;
723               extra_width = -1;
724             }
725           else
726             requisition->width += vscrollbar_requisition.width;
727         }
728
729       if (scrolled_window->vscrollbar_policy == GTK_POLICY_NEVER)
730         requisition->height += child_requisition.height;
731       else
732         {
733           GtkWidgetAuxInfo *aux_info;
734
735           aux_info = gtk_object_get_data_by_id (GTK_OBJECT (bin->child), quark_aux_info);
736           if (aux_info && aux_info->height > 0)
737             {
738               requisition->height += aux_info->height;
739               extra_height = -1;
740             }
741           else
742             requisition->height += hscrollbar_requisition.height;
743         }
744     }
745
746   if (scrolled_window->hscrollbar_policy == GTK_POLICY_AUTOMATIC ||
747       scrolled_window->hscrollbar_policy == GTK_POLICY_ALWAYS)
748     {
749       requisition->width = MAX (requisition->width, hscrollbar_requisition.width);
750       if (!extra_height || scrolled_window->hscrollbar_policy == GTK_POLICY_ALWAYS)
751         extra_height = SCROLLBAR_SPACING (scrolled_window) + hscrollbar_requisition.height;
752     }
753
754   if (scrolled_window->vscrollbar_policy == GTK_POLICY_AUTOMATIC ||
755       scrolled_window->vscrollbar_policy == GTK_POLICY_ALWAYS)
756     {
757       requisition->height = MAX (requisition->height, vscrollbar_requisition.height);
758       if (!extra_height || scrolled_window->vscrollbar_policy == GTK_POLICY_ALWAYS)
759         extra_width = SCROLLBAR_SPACING (scrolled_window) + vscrollbar_requisition.width;
760     }
761
762   requisition->width += GTK_CONTAINER (widget)->border_width * 2 + MAX (0, extra_width);
763   requisition->height += GTK_CONTAINER (widget)->border_width * 2 + MAX (0, extra_height);
764
765   if (scrolled_window->shadow_type != GTK_SHADOW_NONE)
766     {
767       requisition->width += 2 * widget->style->xthickness;
768       requisition->height += 2 * widget->style->ythickness;
769     }
770 }
771
772 static void
773 gtk_scrolled_window_relative_allocation (GtkWidget     *widget,
774                                          GtkAllocation *allocation)
775 {
776   GtkScrolledWindow *scrolled_window;
777
778   g_return_if_fail (widget != NULL);
779   g_return_if_fail (allocation != NULL);
780
781   scrolled_window = GTK_SCROLLED_WINDOW (widget);
782
783   allocation->x = GTK_CONTAINER (widget)->border_width;
784   allocation->y = GTK_CONTAINER (widget)->border_width;
785
786   if (scrolled_window->shadow_type != GTK_SHADOW_NONE)
787     {
788       allocation->x += widget->style->xthickness;
789       allocation->y += widget->style->ythickness;
790     }
791   
792   allocation->width = MAX (1, (gint)widget->allocation.width - allocation->x * 2);
793   allocation->height = MAX (1, (gint)widget->allocation.height - allocation->y * 2);
794
795   if (scrolled_window->vscrollbar_visible)
796     {
797       GtkRequisition vscrollbar_requisition;
798       gtk_widget_get_child_requisition (scrolled_window->vscrollbar,
799                                         &vscrollbar_requisition);
800   
801       if (scrolled_window->window_placement == GTK_CORNER_TOP_RIGHT ||
802           scrolled_window->window_placement == GTK_CORNER_BOTTOM_RIGHT)
803         allocation->x += (vscrollbar_requisition.width +
804                           SCROLLBAR_SPACING (scrolled_window));
805
806       allocation->width = MAX (1, (gint)allocation->width -
807                                ((gint)vscrollbar_requisition.width +
808                                 (gint)SCROLLBAR_SPACING (scrolled_window)));
809     }
810   if (scrolled_window->hscrollbar_visible)
811     {
812       GtkRequisition hscrollbar_requisition;
813       gtk_widget_get_child_requisition (scrolled_window->hscrollbar,
814                                         &hscrollbar_requisition);
815   
816       if (scrolled_window->window_placement == GTK_CORNER_BOTTOM_LEFT ||
817           scrolled_window->window_placement == GTK_CORNER_BOTTOM_RIGHT)
818         allocation->y += (hscrollbar_requisition.height +
819                           SCROLLBAR_SPACING (scrolled_window));
820
821       allocation->height = MAX (1, (gint)allocation->height -
822                                 ((gint)hscrollbar_requisition.height +
823                                  (gint)SCROLLBAR_SPACING (scrolled_window)));
824     }
825 }
826
827 static void
828 gtk_scrolled_window_size_allocate (GtkWidget     *widget,
829                                    GtkAllocation *allocation)
830 {
831   GtkScrolledWindow *scrolled_window;
832   GtkBin *bin;
833   GtkAllocation relative_allocation;
834   GtkAllocation child_allocation;
835   
836   g_return_if_fail (widget != NULL);
837   g_return_if_fail (GTK_IS_SCROLLED_WINDOW (widget));
838   g_return_if_fail (allocation != NULL);
839
840   scrolled_window = GTK_SCROLLED_WINDOW (widget);
841   bin = GTK_BIN (scrolled_window);
842
843   widget->allocation = *allocation;
844
845   if (scrolled_window->hscrollbar_policy == GTK_POLICY_ALWAYS)
846     scrolled_window->hscrollbar_visible = TRUE;
847   else if (scrolled_window->hscrollbar_policy == GTK_POLICY_NEVER)
848     scrolled_window->hscrollbar_visible = FALSE;
849   if (scrolled_window->vscrollbar_policy == GTK_POLICY_ALWAYS)
850     scrolled_window->vscrollbar_visible = TRUE;
851   else if (scrolled_window->vscrollbar_policy == GTK_POLICY_NEVER)
852     scrolled_window->vscrollbar_visible = FALSE;
853
854   if (bin->child && GTK_WIDGET_VISIBLE (bin->child))
855     {
856       gboolean previous_hvis;
857       gboolean previous_vvis;
858       guint count = 0;
859       
860       do
861         {
862           gtk_scrolled_window_relative_allocation (widget, &relative_allocation);
863           
864           child_allocation.x = relative_allocation.x + allocation->x;
865           child_allocation.y = relative_allocation.y + allocation->y;
866           child_allocation.width = relative_allocation.width;
867           child_allocation.height = relative_allocation.height;
868           
869           previous_hvis = scrolled_window->hscrollbar_visible;
870           previous_vvis = scrolled_window->vscrollbar_visible;
871           
872           gtk_widget_size_allocate (bin->child, &child_allocation);
873
874           /* If, after the first iteration, the hscrollbar and the
875            * vscrollbar flip visiblity, then we need both.
876            */
877           if (count &&
878               previous_hvis != scrolled_window->hscrollbar_visible &&
879               previous_vvis != scrolled_window->vscrollbar_visible)
880             {
881               scrolled_window->hscrollbar_visible = TRUE;
882               scrolled_window->vscrollbar_visible = TRUE;
883
884               /* a new resize is already queued at this point,
885                * so we will immediatedly get reinvoked
886                */
887               return;
888             }
889           
890           count++;
891         }
892       while (previous_hvis != scrolled_window->hscrollbar_visible ||
893              previous_vvis != scrolled_window->vscrollbar_visible);
894     }
895   else
896     gtk_scrolled_window_relative_allocation (widget, &relative_allocation);
897   
898   if (scrolled_window->hscrollbar_visible)
899     {
900       GtkRequisition hscrollbar_requisition;
901       gtk_widget_get_child_requisition (scrolled_window->hscrollbar,
902                                         &hscrollbar_requisition);
903   
904       if (!GTK_WIDGET_VISIBLE (scrolled_window->hscrollbar))
905         gtk_widget_show (scrolled_window->hscrollbar);
906
907       child_allocation.x = relative_allocation.x;
908       if (scrolled_window->window_placement == GTK_CORNER_TOP_LEFT ||
909           scrolled_window->window_placement == GTK_CORNER_TOP_RIGHT)
910         child_allocation.y = (relative_allocation.y +
911                               relative_allocation.height +
912                               SCROLLBAR_SPACING (scrolled_window) +
913                               (scrolled_window->shadow_type == GTK_SHADOW_NONE ?
914                                0 : widget->style->ythickness));
915       else
916         child_allocation.y = GTK_CONTAINER (scrolled_window)->border_width;
917
918       child_allocation.width = relative_allocation.width;
919       child_allocation.height = hscrollbar_requisition.height;
920       child_allocation.x += allocation->x;
921       child_allocation.y += allocation->y;
922
923       if (scrolled_window->shadow_type != GTK_SHADOW_NONE)
924         {
925           child_allocation.x -= widget->style->xthickness;
926           child_allocation.width += 2 * widget->style->xthickness;
927         }
928
929       gtk_widget_size_allocate (scrolled_window->hscrollbar, &child_allocation);
930     }
931   else if (GTK_WIDGET_VISIBLE (scrolled_window->hscrollbar))
932     gtk_widget_hide (scrolled_window->hscrollbar);
933
934   if (scrolled_window->vscrollbar_visible)
935     {
936       GtkRequisition vscrollbar_requisition;
937       if (!GTK_WIDGET_VISIBLE (scrolled_window->vscrollbar))
938         gtk_widget_show (scrolled_window->vscrollbar);
939
940       gtk_widget_get_child_requisition (scrolled_window->vscrollbar,
941                                         &vscrollbar_requisition);
942
943       if (scrolled_window->window_placement == GTK_CORNER_TOP_LEFT ||
944           scrolled_window->window_placement == GTK_CORNER_BOTTOM_LEFT)
945         child_allocation.x = (relative_allocation.x +
946                               relative_allocation.width +
947                               SCROLLBAR_SPACING (scrolled_window) +
948                               (scrolled_window->shadow_type == GTK_SHADOW_NONE ?
949                                0 : widget->style->xthickness));
950       else
951         child_allocation.x = GTK_CONTAINER (scrolled_window)->border_width;
952
953       child_allocation.y = relative_allocation.y;
954       child_allocation.width = vscrollbar_requisition.width;
955       child_allocation.height = relative_allocation.height;
956       child_allocation.x += allocation->x;
957       child_allocation.y += allocation->y;
958
959       if (scrolled_window->shadow_type != GTK_SHADOW_NONE)
960         {
961           child_allocation.y -= widget->style->ythickness;
962           child_allocation.height += 2 * widget->style->ythickness;
963         }
964
965       gtk_widget_size_allocate (scrolled_window->vscrollbar, &child_allocation);
966     }
967   else if (GTK_WIDGET_VISIBLE (scrolled_window->vscrollbar))
968     gtk_widget_hide (scrolled_window->vscrollbar);
969 }
970
971 static gint
972 gtk_scrolled_window_scroll_event (GtkWidget *widget,
973                                   GdkEventScroll *event)
974 {
975   GtkWidget *range;
976
977   g_return_val_if_fail (widget != NULL, FALSE);
978   g_return_val_if_fail (GTK_IS_SCROLLED_WINDOW (widget), FALSE);
979   g_return_val_if_fail (event != NULL, FALSE);  
980
981   if (event->direction == GDK_SCROLL_UP || event->direction == GDK_SCROLL_DOWN)
982     range = GTK_SCROLLED_WINDOW (widget)->vscrollbar;
983   else
984     range = GTK_SCROLLED_WINDOW (widget)->hscrollbar;
985
986   if (range && GTK_WIDGET_VISIBLE (range))
987     {
988       GtkAdjustment *adj = GTK_RANGE (range)->adjustment;
989       gfloat new_value;
990
991       if (event->direction == GDK_SCROLL_UP || event->direction == GDK_SCROLL_LEFT)
992         new_value = adj->value - adj->page_increment / 2;
993       else
994         new_value = adj->value + adj->page_increment / 2;
995
996       new_value = CLAMP (new_value, adj->lower, adj->upper - adj->page_size);
997       gtk_adjustment_set_value (adj, new_value);
998
999       return TRUE;
1000     }
1001
1002   return FALSE;
1003 }
1004
1005 static void
1006 gtk_scrolled_window_adjustment_changed (GtkAdjustment *adjustment,
1007                                         gpointer       data)
1008 {
1009   GtkScrolledWindow *scrolled_win;
1010
1011   g_return_if_fail (adjustment != NULL);
1012   g_return_if_fail (data != NULL);
1013
1014   scrolled_win = GTK_SCROLLED_WINDOW (data);
1015
1016   if (adjustment == gtk_range_get_adjustment (GTK_RANGE (scrolled_win->hscrollbar)))
1017     {
1018       if (scrolled_win->hscrollbar_policy == GTK_POLICY_AUTOMATIC)
1019         {
1020           gboolean visible;
1021           
1022           visible = scrolled_win->hscrollbar_visible;
1023           scrolled_win->hscrollbar_visible = (adjustment->upper - adjustment->lower >
1024                                               adjustment->page_size);
1025           if (scrolled_win->hscrollbar_visible != visible)
1026             gtk_widget_queue_resize (GTK_WIDGET (scrolled_win));
1027         }
1028     }
1029   else if (adjustment == gtk_range_get_adjustment (GTK_RANGE (scrolled_win->vscrollbar)))
1030     {
1031       if (scrolled_win->vscrollbar_policy == GTK_POLICY_AUTOMATIC)
1032         {
1033           gboolean visible;
1034
1035           visible = scrolled_win->vscrollbar_visible;
1036           scrolled_win->vscrollbar_visible = (adjustment->upper - adjustment->lower >
1037                                               adjustment->page_size);
1038           if (scrolled_win->vscrollbar_visible != visible)
1039             gtk_widget_queue_resize (GTK_WIDGET (scrolled_win));
1040         }
1041     }
1042 }
1043
1044 static void
1045 gtk_scrolled_window_add (GtkContainer *container,
1046                          GtkWidget    *child)
1047 {
1048   GtkScrolledWindow *scrolled_window;
1049   GtkBin *bin;
1050
1051   bin = GTK_BIN (container);
1052   g_return_if_fail (bin->child == NULL);
1053
1054   scrolled_window = GTK_SCROLLED_WINDOW (container);
1055
1056   bin->child = child;
1057   gtk_widget_set_parent (child, GTK_WIDGET (bin));
1058
1059   /* this is a temporary message */
1060   if (!gtk_widget_set_scroll_adjustments (child,
1061                                           gtk_range_get_adjustment (GTK_RANGE (scrolled_window->hscrollbar)),
1062                                           gtk_range_get_adjustment (GTK_RANGE (scrolled_window->vscrollbar))))
1063     g_warning ("gtk_scrolled_window_add(): cannot add non scrollable widget "
1064                "use gtk_scrolled_window_add_with_viewport() instead");
1065
1066   if (GTK_WIDGET_REALIZED (child->parent))
1067     gtk_widget_realize (child);
1068
1069   if (GTK_WIDGET_VISIBLE (child->parent) && GTK_WIDGET_VISIBLE (child))
1070     {
1071       if (GTK_WIDGET_MAPPED (child->parent))
1072         gtk_widget_map (child);
1073
1074       gtk_widget_queue_resize (child);
1075     }
1076 }
1077
1078 static void
1079 gtk_scrolled_window_remove (GtkContainer *container,
1080                             GtkWidget    *child)
1081 {
1082   g_return_if_fail (container != NULL);
1083   g_return_if_fail (GTK_IS_SCROLLED_WINDOW (container));
1084   g_return_if_fail (child != NULL);
1085   g_return_if_fail (GTK_BIN (container)->child == child);
1086   
1087   gtk_widget_set_scroll_adjustments (child, NULL, NULL);
1088
1089   /* chain parent class handler to remove child */
1090   GTK_CONTAINER_CLASS (parent_class)->remove (container, child);
1091 }
1092
1093 void
1094 gtk_scrolled_window_add_with_viewport (GtkScrolledWindow *scrolled_window,
1095                                        GtkWidget         *child)
1096 {
1097   GtkBin *bin;
1098   GtkWidget *viewport;
1099
1100   g_return_if_fail (scrolled_window != NULL);
1101   g_return_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window));
1102   g_return_if_fail (child != NULL);
1103   g_return_if_fail (GTK_IS_WIDGET (child));
1104   g_return_if_fail (child->parent == NULL);
1105
1106   bin = GTK_BIN (scrolled_window);
1107
1108   if (bin->child != NULL)
1109     {
1110       g_return_if_fail (GTK_IS_VIEWPORT (bin->child));
1111       g_return_if_fail (GTK_BIN (bin->child)->child == NULL);
1112
1113       viewport = bin->child;
1114     }
1115   else
1116     {
1117       viewport =
1118         gtk_viewport_new (gtk_scrolled_window_get_hadjustment (scrolled_window),
1119                           gtk_scrolled_window_get_vadjustment (scrolled_window));
1120       gtk_container_add (GTK_CONTAINER (scrolled_window), viewport);
1121     }
1122
1123   gtk_widget_show (viewport);
1124   gtk_container_add (GTK_CONTAINER (viewport), child);
1125 }