]> Pileus Git - ~andy/gtk/blob - gtk/gtkscrolledwindow.c
Handle quoting of / with \; properly handle __ in paths, quote " and \n in
[~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 gint gtk_scrolled_window_expose             (GtkWidget              *widget,
94                                                     GdkEventExpose         *event);
95 static void gtk_scrolled_window_size_request       (GtkWidget              *widget,
96                                                     GtkRequisition         *requisition);
97 static void gtk_scrolled_window_size_allocate      (GtkWidget              *widget,
98                                                     GtkAllocation          *allocation);
99 static gint gtk_scrolled_window_scroll_event       (GtkWidget              *widget,
100                                                     GdkEventScroll         *event);
101 static void gtk_scrolled_window_add                (GtkContainer           *container,
102                                                     GtkWidget              *widget);
103 static void gtk_scrolled_window_remove             (GtkContainer           *container,
104                                                     GtkWidget              *widget);
105 static void gtk_scrolled_window_forall             (GtkContainer           *container,
106                                                     gboolean                include_internals,
107                                                     GtkCallback             callback,
108                                                     gpointer                callback_data);
109 static void gtk_scrolled_window_relative_allocation(GtkWidget              *widget,
110                                                     GtkAllocation          *allocation);
111 static void gtk_scrolled_window_adjustment_changed (GtkAdjustment          *adjustment,
112                                                     gpointer                data);
113
114 static GtkContainerClass *parent_class = NULL;
115
116
117 GtkType
118 gtk_scrolled_window_get_type (void)
119 {
120   static GtkType scrolled_window_type = 0;
121
122   if (!scrolled_window_type)
123     {
124       static const GtkTypeInfo scrolled_window_info =
125       {
126         "GtkScrolledWindow",
127         sizeof (GtkScrolledWindow),
128         sizeof (GtkScrolledWindowClass),
129         (GtkClassInitFunc) gtk_scrolled_window_class_init,
130         (GtkObjectInitFunc) gtk_scrolled_window_init,
131         /* reserved_1 */ NULL,
132         /* reserved_2 */ NULL,
133         (GtkClassInitFunc) NULL,
134       };
135
136       scrolled_window_type = gtk_type_unique (GTK_TYPE_BIN, &scrolled_window_info);
137     }
138
139   return scrolled_window_type;
140 }
141
142 static void
143 gtk_scrolled_window_class_init (GtkScrolledWindowClass *class)
144 {
145   GObjectClass *gobject_class = G_OBJECT_CLASS (class);
146   GtkObjectClass *object_class;
147   GtkWidgetClass *widget_class;
148   GtkContainerClass *container_class;
149
150   object_class = (GtkObjectClass*) class;
151   widget_class = (GtkWidgetClass*) class;
152   container_class = (GtkContainerClass*) class;
153   parent_class = gtk_type_class (GTK_TYPE_BIN);
154
155   gobject_class->finalize = gtk_scrolled_window_finalize;
156
157   object_class->set_arg = gtk_scrolled_window_set_arg;
158   object_class->get_arg = gtk_scrolled_window_get_arg;
159   object_class->destroy = gtk_scrolled_window_destroy;
160
161   widget_class->map = gtk_scrolled_window_map;
162   widget_class->unmap = gtk_scrolled_window_unmap;
163   widget_class->expose_event = gtk_scrolled_window_expose;
164   widget_class->size_request = gtk_scrolled_window_size_request;
165   widget_class->size_allocate = gtk_scrolled_window_size_allocate;
166   widget_class->scroll_event = gtk_scrolled_window_scroll_event;
167
168   container_class->add = gtk_scrolled_window_add;
169   container_class->remove = gtk_scrolled_window_remove;
170   container_class->forall = gtk_scrolled_window_forall;
171
172   class->scrollbar_spacing = DEFAULT_SCROLLBAR_SPACING;
173
174   gtk_object_add_arg_type ("GtkScrolledWindow::hadjustment",
175                            GTK_TYPE_ADJUSTMENT,
176                            GTK_ARG_READWRITE | GTK_ARG_CONSTRUCT,
177                            ARG_HADJUSTMENT);
178   gtk_object_add_arg_type ("GtkScrolledWindow::vadjustment",
179                            GTK_TYPE_ADJUSTMENT,
180                            GTK_ARG_READWRITE | GTK_ARG_CONSTRUCT,
181                            ARG_VADJUSTMENT);
182   gtk_object_add_arg_type ("GtkScrolledWindow::hscrollbar_policy",
183                            GTK_TYPE_POLICY_TYPE,
184                            GTK_ARG_READWRITE,
185                            ARG_HSCROLLBAR_POLICY);
186   gtk_object_add_arg_type ("GtkScrolledWindow::vscrollbar_policy",
187                            GTK_TYPE_POLICY_TYPE,
188                            GTK_ARG_READWRITE,
189                            ARG_VSCROLLBAR_POLICY);
190   gtk_object_add_arg_type ("GtkScrolledWindow::window_placement",
191                            GTK_TYPE_CORNER_TYPE,
192                            GTK_ARG_READWRITE,
193                            ARG_WINDOW_PLACEMENT);
194   gtk_object_add_arg_type ("GtkScrolledWindow::shadow",
195                            GTK_TYPE_SHADOW_TYPE,
196                            GTK_ARG_READWRITE,
197                            ARG_SHADOW);
198 }
199
200 static void
201 gtk_scrolled_window_set_arg (GtkObject        *object,
202                              GtkArg           *arg,
203                              guint             arg_id)
204 {
205   GtkScrolledWindow *scrolled_window;
206
207   scrolled_window = GTK_SCROLLED_WINDOW (object);
208
209   switch (arg_id)
210     {
211     case ARG_HADJUSTMENT:
212       gtk_scrolled_window_set_hadjustment (scrolled_window, GTK_VALUE_POINTER (*arg));
213       break;
214     case ARG_VADJUSTMENT:
215       gtk_scrolled_window_set_vadjustment (scrolled_window, GTK_VALUE_POINTER (*arg));
216       break;
217     case ARG_HSCROLLBAR_POLICY:
218       gtk_scrolled_window_set_policy (scrolled_window,
219                                       GTK_VALUE_ENUM (*arg),
220                                       scrolled_window->vscrollbar_policy);
221       break;
222     case ARG_VSCROLLBAR_POLICY:
223       gtk_scrolled_window_set_policy (scrolled_window,
224                                       scrolled_window->hscrollbar_policy,
225                                       GTK_VALUE_ENUM (*arg));
226       break;
227     case ARG_WINDOW_PLACEMENT:
228       gtk_scrolled_window_set_placement (scrolled_window,
229                                          GTK_VALUE_ENUM (*arg));
230       break;
231     case ARG_SHADOW:
232       gtk_scrolled_window_set_shadow_type (scrolled_window,
233                                            GTK_VALUE_ENUM (*arg));
234       break;
235     default:
236       break;
237     }
238 }
239
240 static void
241 gtk_scrolled_window_get_arg (GtkObject        *object,
242                              GtkArg           *arg,
243                              guint             arg_id)
244 {
245   GtkScrolledWindow *scrolled_window;
246
247   scrolled_window = GTK_SCROLLED_WINDOW (object);
248
249   switch (arg_id)
250     {
251     case ARG_HADJUSTMENT:
252       GTK_VALUE_POINTER (*arg) = gtk_scrolled_window_get_hadjustment (scrolled_window);
253       break;
254     case ARG_VADJUSTMENT:
255       GTK_VALUE_POINTER (*arg) = gtk_scrolled_window_get_vadjustment (scrolled_window);
256       break;
257     case ARG_HSCROLLBAR_POLICY:
258       GTK_VALUE_ENUM (*arg) = scrolled_window->hscrollbar_policy;
259       break;
260     case ARG_VSCROLLBAR_POLICY:
261       GTK_VALUE_ENUM (*arg) = scrolled_window->vscrollbar_policy;
262       break;
263     case ARG_WINDOW_PLACEMENT:
264       GTK_VALUE_ENUM (*arg) = scrolled_window->window_placement;
265       break;
266     case ARG_SHADOW:
267       GTK_VALUE_ENUM (*arg) = scrolled_window->shadow_type;
268       break;
269     default:
270       arg->type = GTK_TYPE_INVALID;
271       break;
272     }
273 }
274
275 static void
276 gtk_scrolled_window_init (GtkScrolledWindow *scrolled_window)
277 {
278   GTK_WIDGET_SET_FLAGS (scrolled_window, GTK_NO_WINDOW);
279
280   gtk_container_set_resize_mode (GTK_CONTAINER (scrolled_window), GTK_RESIZE_QUEUE);
281
282   scrolled_window->hscrollbar = NULL;
283   scrolled_window->vscrollbar = NULL;
284   scrolled_window->hscrollbar_policy = GTK_POLICY_ALWAYS;
285   scrolled_window->vscrollbar_policy = GTK_POLICY_ALWAYS;
286   scrolled_window->hscrollbar_visible = FALSE;
287   scrolled_window->vscrollbar_visible = FALSE;
288   scrolled_window->window_placement = GTK_CORNER_TOP_LEFT;
289   
290 }
291
292 GtkWidget*
293 gtk_scrolled_window_new (GtkAdjustment *hadjustment,
294                          GtkAdjustment *vadjustment)
295 {
296   GtkWidget *scrolled_window;
297
298   if (hadjustment)
299     g_return_val_if_fail (GTK_IS_ADJUSTMENT (hadjustment), NULL);
300
301   if (vadjustment)
302     g_return_val_if_fail (GTK_IS_ADJUSTMENT (vadjustment), NULL);
303
304   scrolled_window = gtk_widget_new (GTK_TYPE_SCROLLED_WINDOW,
305                                     "hadjustment", hadjustment,
306                                     "vadjustment", vadjustment,
307                                     NULL);
308
309   return scrolled_window;
310 }
311
312 void
313 gtk_scrolled_window_set_hadjustment (GtkScrolledWindow *scrolled_window,
314                                      GtkAdjustment     *hadjustment)
315 {
316   GtkBin *bin;
317
318   g_return_if_fail (scrolled_window != NULL);
319   g_return_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window));
320   if (hadjustment)
321     g_return_if_fail (GTK_IS_ADJUSTMENT (hadjustment));
322   else
323     hadjustment = (GtkAdjustment*) gtk_object_new (GTK_TYPE_ADJUSTMENT, NULL);
324
325   bin = GTK_BIN (scrolled_window);
326
327   if (!scrolled_window->hscrollbar)
328     {
329       gtk_widget_push_composite_child ();
330       scrolled_window->hscrollbar = gtk_hscrollbar_new (hadjustment);
331       gtk_widget_set_composite_name (scrolled_window->hscrollbar, "hscrollbar");
332       gtk_widget_pop_composite_child ();
333
334       gtk_widget_set_parent (scrolled_window->hscrollbar, GTK_WIDGET (scrolled_window));
335       gtk_widget_ref (scrolled_window->hscrollbar);
336       gtk_widget_show (scrolled_window->hscrollbar);
337     }
338   else
339     {
340       GtkAdjustment *old_adjustment;
341       
342       old_adjustment = gtk_range_get_adjustment (GTK_RANGE (scrolled_window->hscrollbar));
343       if (old_adjustment == hadjustment)
344         return;
345
346       gtk_signal_disconnect_by_func (GTK_OBJECT (old_adjustment),
347                                      GTK_SIGNAL_FUNC (gtk_scrolled_window_adjustment_changed),
348                                      scrolled_window);
349       gtk_range_set_adjustment (GTK_RANGE (scrolled_window->hscrollbar),
350                                 hadjustment);
351     }
352   hadjustment = gtk_range_get_adjustment (GTK_RANGE (scrolled_window->hscrollbar));
353   gtk_signal_connect (GTK_OBJECT (hadjustment),
354                       "changed",
355                       GTK_SIGNAL_FUNC (gtk_scrolled_window_adjustment_changed),
356                       scrolled_window);
357   gtk_scrolled_window_adjustment_changed (hadjustment, scrolled_window);
358   
359   if (bin->child)
360     gtk_widget_set_scroll_adjustments (bin->child,
361                                        gtk_range_get_adjustment (GTK_RANGE (scrolled_window->hscrollbar)),
362                                        gtk_range_get_adjustment (GTK_RANGE (scrolled_window->vscrollbar)));
363 }
364
365 void
366 gtk_scrolled_window_set_vadjustment (GtkScrolledWindow *scrolled_window,
367                                      GtkAdjustment     *vadjustment)
368 {
369   GtkBin *bin;
370
371   g_return_if_fail (scrolled_window != NULL);
372   g_return_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window));
373   if (vadjustment)
374     g_return_if_fail (GTK_IS_ADJUSTMENT (vadjustment));
375   else
376     vadjustment = (GtkAdjustment*) gtk_object_new (GTK_TYPE_ADJUSTMENT, NULL);
377
378   bin = GTK_BIN (scrolled_window);
379
380   if (!scrolled_window->vscrollbar)
381     {
382       gtk_widget_push_composite_child ();
383       scrolled_window->vscrollbar = gtk_vscrollbar_new (vadjustment);
384       gtk_widget_set_composite_name (scrolled_window->vscrollbar, "vscrollbar");
385       gtk_widget_pop_composite_child ();
386
387       gtk_widget_set_parent (scrolled_window->vscrollbar, GTK_WIDGET (scrolled_window));
388       gtk_widget_ref (scrolled_window->vscrollbar);
389       gtk_widget_show (scrolled_window->vscrollbar);
390     }
391   else
392     {
393       GtkAdjustment *old_adjustment;
394       
395       old_adjustment = gtk_range_get_adjustment (GTK_RANGE (scrolled_window->vscrollbar));
396       if (old_adjustment == vadjustment)
397         return;
398
399       gtk_signal_disconnect_by_func (GTK_OBJECT (old_adjustment),
400                                      GTK_SIGNAL_FUNC (gtk_scrolled_window_adjustment_changed),
401                                      scrolled_window);
402       gtk_range_set_adjustment (GTK_RANGE (scrolled_window->vscrollbar),
403                                 vadjustment);
404     }
405   vadjustment = gtk_range_get_adjustment (GTK_RANGE (scrolled_window->vscrollbar));
406   gtk_signal_connect (GTK_OBJECT (vadjustment),
407                       "changed",
408                       GTK_SIGNAL_FUNC (gtk_scrolled_window_adjustment_changed),
409                       scrolled_window);
410   gtk_scrolled_window_adjustment_changed (vadjustment, scrolled_window);
411
412   if (bin->child)
413     gtk_widget_set_scroll_adjustments (bin->child,
414                                        gtk_range_get_adjustment (GTK_RANGE (scrolled_window->hscrollbar)),
415                                        gtk_range_get_adjustment (GTK_RANGE (scrolled_window->vscrollbar)));
416 }
417
418 GtkAdjustment*
419 gtk_scrolled_window_get_hadjustment (GtkScrolledWindow *scrolled_window)
420 {
421   g_return_val_if_fail (scrolled_window != NULL, NULL);
422   g_return_val_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window), NULL);
423
424   return (scrolled_window->hscrollbar ?
425           gtk_range_get_adjustment (GTK_RANGE (scrolled_window->hscrollbar)) :
426           NULL);
427 }
428
429 GtkAdjustment*
430 gtk_scrolled_window_get_vadjustment (GtkScrolledWindow *scrolled_window)
431 {
432   g_return_val_if_fail (scrolled_window != NULL, NULL);
433   g_return_val_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window), NULL);
434
435   return (scrolled_window->vscrollbar ?
436           gtk_range_get_adjustment (GTK_RANGE (scrolled_window->vscrollbar)) :
437           NULL);
438 }
439
440 void
441 gtk_scrolled_window_set_policy (GtkScrolledWindow *scrolled_window,
442                                 GtkPolicyType      hscrollbar_policy,
443                                 GtkPolicyType      vscrollbar_policy)
444 {
445   g_return_if_fail (scrolled_window != NULL);
446   g_return_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window));
447
448   if ((scrolled_window->hscrollbar_policy != hscrollbar_policy) ||
449       (scrolled_window->vscrollbar_policy != vscrollbar_policy))
450     {
451       scrolled_window->hscrollbar_policy = hscrollbar_policy;
452       scrolled_window->vscrollbar_policy = vscrollbar_policy;
453
454       gtk_widget_queue_resize (GTK_WIDGET (scrolled_window));
455     }
456 }
457
458 void
459 gtk_scrolled_window_set_placement (GtkScrolledWindow *scrolled_window,
460                                    GtkCornerType      window_placement)
461 {
462   g_return_if_fail (scrolled_window != NULL);
463   g_return_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window));
464
465   if (scrolled_window->window_placement != window_placement)
466     {
467       scrolled_window->window_placement = window_placement;
468
469       gtk_widget_queue_resize (GTK_WIDGET (scrolled_window));
470     }
471 }
472
473 /**
474  * gtk_scrolled_window_set_shadow_type:
475  * @scrolled_window: a #GtkScrolledWindow
476  * @type: kind of shadow to draw around scrolled window contents
477  *
478  * Changes the type of shadow drawn around the contents of
479  * @scrolled_window.
480  * 
481  **/
482 void
483 gtk_scrolled_window_set_shadow_type (GtkScrolledWindow *scrolled_window,
484                                      GtkShadowType      type)
485 {
486   g_return_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window));
487   g_return_if_fail (type >= GTK_SHADOW_NONE && type <= GTK_SHADOW_ETCHED_OUT);
488   
489   if (scrolled_window->shadow_type != type)
490     {
491       scrolled_window->shadow_type = type;
492
493       if (GTK_WIDGET_DRAWABLE (scrolled_window))
494         gtk_widget_queue_clear (GTK_WIDGET (scrolled_window));
495
496       gtk_widget_queue_resize (GTK_WIDGET (scrolled_window));
497     }
498 }
499
500 static void
501 gtk_scrolled_window_destroy (GtkObject *object)
502 {
503   GtkScrolledWindow *scrolled_window;
504
505   g_return_if_fail (object != NULL);
506   g_return_if_fail (GTK_IS_SCROLLED_WINDOW (object));
507
508   scrolled_window = GTK_SCROLLED_WINDOW (object);
509
510   gtk_widget_unparent (scrolled_window->hscrollbar);
511   gtk_widget_unparent (scrolled_window->vscrollbar);
512   gtk_widget_destroy (scrolled_window->hscrollbar);
513   gtk_widget_destroy (scrolled_window->vscrollbar);
514
515   GTK_OBJECT_CLASS (parent_class)->destroy (object);
516 }
517
518 static void
519 gtk_scrolled_window_finalize (GObject *object)
520 {
521   GtkScrolledWindow *scrolled_window = GTK_SCROLLED_WINDOW (object);
522
523   gtk_widget_unref (scrolled_window->hscrollbar);
524   gtk_widget_unref (scrolled_window->vscrollbar);
525
526   G_OBJECT_CLASS (parent_class)->finalize (object);
527 }
528
529 static void
530 gtk_scrolled_window_map (GtkWidget *widget)
531 {
532   GtkScrolledWindow *scrolled_window;
533
534   g_return_if_fail (widget != NULL);
535   g_return_if_fail (GTK_IS_SCROLLED_WINDOW (widget));
536
537   scrolled_window = GTK_SCROLLED_WINDOW (widget);
538
539   /* chain parent class handler to map self and child */
540   GTK_WIDGET_CLASS (parent_class)->map (widget);
541   
542   if (GTK_WIDGET_VISIBLE (scrolled_window->hscrollbar) &&
543       !GTK_WIDGET_MAPPED (scrolled_window->hscrollbar))
544     gtk_widget_map (scrolled_window->hscrollbar);
545   
546   if (GTK_WIDGET_VISIBLE (scrolled_window->vscrollbar) &&
547       !GTK_WIDGET_MAPPED (scrolled_window->vscrollbar))
548     gtk_widget_map (scrolled_window->vscrollbar);
549 }
550
551 static void
552 gtk_scrolled_window_unmap (GtkWidget *widget)
553 {
554   GtkScrolledWindow *scrolled_window;
555
556   g_return_if_fail (widget != NULL);
557   g_return_if_fail (GTK_IS_SCROLLED_WINDOW (widget));
558
559   scrolled_window = GTK_SCROLLED_WINDOW (widget);
560
561   /* chain parent class handler to unmap self and child */
562   GTK_WIDGET_CLASS (parent_class)->unmap (widget);
563   
564   if (GTK_WIDGET_MAPPED (scrolled_window->hscrollbar))
565     gtk_widget_unmap (scrolled_window->hscrollbar);
566   
567   if (GTK_WIDGET_MAPPED (scrolled_window->vscrollbar))
568     gtk_widget_unmap (scrolled_window->vscrollbar);
569 }
570
571 static void
572 gtk_scrolled_window_paint (GtkWidget    *widget,
573                            GdkRectangle *area)
574 {
575   GtkAllocation relative_allocation;
576   GtkScrolledWindow *scrolled_window = GTK_SCROLLED_WINDOW (widget);
577
578   if (scrolled_window->shadow_type != GTK_SHADOW_NONE)
579     {
580       gtk_scrolled_window_relative_allocation (widget, &relative_allocation);
581       
582       relative_allocation.x -= widget->style->xthickness;
583       relative_allocation.y -= widget->style->ythickness;
584       relative_allocation.width += 2 * widget->style->xthickness;
585       relative_allocation.height += 2 * widget->style->ythickness;
586       
587       gtk_paint_shadow (widget->style, widget->window,
588                         GTK_STATE_NORMAL, scrolled_window->shadow_type,
589                         area, widget, "scrolled_window",
590                         widget->allocation.x + relative_allocation.x,
591                         widget->allocation.y + relative_allocation.y,
592                         relative_allocation.width,
593                         relative_allocation.height);
594     }
595 }
596
597 static gint
598 gtk_scrolled_window_expose (GtkWidget      *widget,
599                             GdkEventExpose *event)
600 {
601   if (GTK_WIDGET_DRAWABLE (widget))
602     {
603       gtk_scrolled_window_paint (widget, &event->area);
604
605       (* GTK_WIDGET_CLASS (parent_class)->expose_event) (widget, event);
606     }
607
608   return FALSE;
609 }
610
611 static void
612 gtk_scrolled_window_forall (GtkContainer *container,
613                             gboolean      include_internals,
614                             GtkCallback   callback,
615                             gpointer      callback_data)
616 {
617   g_return_if_fail (container != NULL);
618   g_return_if_fail (GTK_IS_SCROLLED_WINDOW (container));
619   g_return_if_fail (callback != NULL);
620
621   GTK_CONTAINER_CLASS (parent_class)->forall (container,
622                                               include_internals,
623                                               callback,
624                                               callback_data);
625   if (include_internals)
626     {
627       GtkScrolledWindow *scrolled_window;
628
629       scrolled_window = GTK_SCROLLED_WINDOW (container);
630       
631       if (scrolled_window->vscrollbar)
632         callback (scrolled_window->vscrollbar, callback_data);
633       if (scrolled_window->hscrollbar)
634         callback (scrolled_window->hscrollbar, callback_data);
635     }
636 }
637
638 static void
639 gtk_scrolled_window_size_request (GtkWidget      *widget,
640                                   GtkRequisition *requisition)
641 {
642   GtkScrolledWindow *scrolled_window;
643   GtkBin *bin;
644   gint extra_width;
645   gint extra_height;
646   GtkRequisition hscrollbar_requisition;
647   GtkRequisition vscrollbar_requisition;
648   GtkRequisition child_requisition;
649
650   g_return_if_fail (widget != NULL);
651   g_return_if_fail (GTK_IS_SCROLLED_WINDOW (widget));
652   g_return_if_fail (requisition != NULL);
653
654   scrolled_window = GTK_SCROLLED_WINDOW (widget);
655   bin = GTK_BIN (scrolled_window);
656
657   extra_width = 0;
658   extra_height = 0;
659   requisition->width = 0;
660   requisition->height = 0;
661   
662   gtk_widget_size_request (scrolled_window->hscrollbar,
663                            &hscrollbar_requisition);
664   gtk_widget_size_request (scrolled_window->vscrollbar,
665                            &vscrollbar_requisition);
666   
667   if (bin->child && GTK_WIDGET_VISIBLE (bin->child))
668     {
669       static guint quark_aux_info = 0;
670
671       if (!quark_aux_info)
672         quark_aux_info = g_quark_from_static_string ("gtk-aux-info");
673
674       gtk_widget_size_request (bin->child, &child_requisition);
675
676       if (scrolled_window->hscrollbar_policy == GTK_POLICY_NEVER)
677         requisition->width += child_requisition.width;
678       else
679         {
680           GtkWidgetAuxInfo *aux_info;
681
682           aux_info = gtk_object_get_data_by_id (GTK_OBJECT (bin->child), quark_aux_info);
683           if (aux_info && aux_info->width > 0)
684             {
685               requisition->width += aux_info->width;
686               extra_width = -1;
687             }
688           else
689             requisition->width += vscrollbar_requisition.width;
690         }
691
692       if (scrolled_window->vscrollbar_policy == GTK_POLICY_NEVER)
693         requisition->height += child_requisition.height;
694       else
695         {
696           GtkWidgetAuxInfo *aux_info;
697
698           aux_info = gtk_object_get_data_by_id (GTK_OBJECT (bin->child), quark_aux_info);
699           if (aux_info && aux_info->height > 0)
700             {
701               requisition->height += aux_info->height;
702               extra_height = -1;
703             }
704           else
705             requisition->height += hscrollbar_requisition.height;
706         }
707     }
708
709   if (scrolled_window->hscrollbar_policy == GTK_POLICY_AUTOMATIC ||
710       scrolled_window->hscrollbar_policy == GTK_POLICY_ALWAYS)
711     {
712       requisition->width = MAX (requisition->width, hscrollbar_requisition.width);
713       if (!extra_height || scrolled_window->hscrollbar_policy == GTK_POLICY_ALWAYS)
714         extra_height = SCROLLBAR_SPACING (scrolled_window) + hscrollbar_requisition.height;
715     }
716
717   if (scrolled_window->vscrollbar_policy == GTK_POLICY_AUTOMATIC ||
718       scrolled_window->vscrollbar_policy == GTK_POLICY_ALWAYS)
719     {
720       requisition->height = MAX (requisition->height, vscrollbar_requisition.height);
721       if (!extra_height || scrolled_window->vscrollbar_policy == GTK_POLICY_ALWAYS)
722         extra_width = SCROLLBAR_SPACING (scrolled_window) + vscrollbar_requisition.width;
723     }
724
725   requisition->width += GTK_CONTAINER (widget)->border_width * 2 + MAX (0, extra_width);
726   requisition->height += GTK_CONTAINER (widget)->border_width * 2 + MAX (0, extra_height);
727
728   if (scrolled_window->shadow_type != GTK_SHADOW_NONE)
729     {
730       requisition->width += 2 * widget->style->xthickness;
731       requisition->height += 2 * widget->style->ythickness;
732     }
733 }
734
735 static void
736 gtk_scrolled_window_relative_allocation (GtkWidget     *widget,
737                                          GtkAllocation *allocation)
738 {
739   GtkScrolledWindow *scrolled_window;
740
741   g_return_if_fail (widget != NULL);
742   g_return_if_fail (allocation != NULL);
743
744   scrolled_window = GTK_SCROLLED_WINDOW (widget);
745
746   allocation->x = GTK_CONTAINER (widget)->border_width;
747   allocation->y = GTK_CONTAINER (widget)->border_width;
748
749   if (scrolled_window->shadow_type != GTK_SHADOW_NONE)
750     {
751       allocation->x += widget->style->xthickness;
752       allocation->y += widget->style->ythickness;
753     }
754   
755   allocation->width = MAX (1, (gint)widget->allocation.width - allocation->x * 2);
756   allocation->height = MAX (1, (gint)widget->allocation.height - allocation->y * 2);
757
758   if (scrolled_window->vscrollbar_visible)
759     {
760       GtkRequisition vscrollbar_requisition;
761       gtk_widget_get_child_requisition (scrolled_window->vscrollbar,
762                                         &vscrollbar_requisition);
763   
764       if (scrolled_window->window_placement == GTK_CORNER_TOP_RIGHT ||
765           scrolled_window->window_placement == GTK_CORNER_BOTTOM_RIGHT)
766         allocation->x += (vscrollbar_requisition.width +
767                           SCROLLBAR_SPACING (scrolled_window));
768
769       allocation->width = MAX (1, (gint)allocation->width -
770                                ((gint)vscrollbar_requisition.width +
771                                 (gint)SCROLLBAR_SPACING (scrolled_window)));
772     }
773   if (scrolled_window->hscrollbar_visible)
774     {
775       GtkRequisition hscrollbar_requisition;
776       gtk_widget_get_child_requisition (scrolled_window->hscrollbar,
777                                         &hscrollbar_requisition);
778   
779       if (scrolled_window->window_placement == GTK_CORNER_BOTTOM_LEFT ||
780           scrolled_window->window_placement == GTK_CORNER_BOTTOM_RIGHT)
781         allocation->y += (hscrollbar_requisition.height +
782                           SCROLLBAR_SPACING (scrolled_window));
783
784       allocation->height = MAX (1, (gint)allocation->height -
785                                 ((gint)hscrollbar_requisition.height +
786                                  (gint)SCROLLBAR_SPACING (scrolled_window)));
787     }
788 }
789
790 static void
791 gtk_scrolled_window_size_allocate (GtkWidget     *widget,
792                                    GtkAllocation *allocation)
793 {
794   GtkScrolledWindow *scrolled_window;
795   GtkBin *bin;
796   GtkAllocation relative_allocation;
797   GtkAllocation child_allocation;
798   
799   g_return_if_fail (widget != NULL);
800   g_return_if_fail (GTK_IS_SCROLLED_WINDOW (widget));
801   g_return_if_fail (allocation != NULL);
802
803   scrolled_window = GTK_SCROLLED_WINDOW (widget);
804   bin = GTK_BIN (scrolled_window);
805
806   widget->allocation = *allocation;
807
808   if (scrolled_window->hscrollbar_policy == GTK_POLICY_ALWAYS)
809     scrolled_window->hscrollbar_visible = TRUE;
810   else if (scrolled_window->hscrollbar_policy == GTK_POLICY_NEVER)
811     scrolled_window->hscrollbar_visible = FALSE;
812   if (scrolled_window->vscrollbar_policy == GTK_POLICY_ALWAYS)
813     scrolled_window->vscrollbar_visible = TRUE;
814   else if (scrolled_window->vscrollbar_policy == GTK_POLICY_NEVER)
815     scrolled_window->vscrollbar_visible = FALSE;
816
817   if (bin->child && GTK_WIDGET_VISIBLE (bin->child))
818     {
819       gboolean previous_hvis;
820       gboolean previous_vvis;
821       guint count = 0;
822       
823       do
824         {
825           gtk_scrolled_window_relative_allocation (widget, &relative_allocation);
826           
827           child_allocation.x = relative_allocation.x + allocation->x;
828           child_allocation.y = relative_allocation.y + allocation->y;
829           child_allocation.width = relative_allocation.width;
830           child_allocation.height = relative_allocation.height;
831           
832           previous_hvis = scrolled_window->hscrollbar_visible;
833           previous_vvis = scrolled_window->vscrollbar_visible;
834           
835           gtk_widget_size_allocate (bin->child, &child_allocation);
836
837           /* If, after the first iteration, the hscrollbar and the
838            * vscrollbar flip visiblity, then we need both.
839            */
840           if (count &&
841               previous_hvis != scrolled_window->hscrollbar_visible &&
842               previous_vvis != scrolled_window->vscrollbar_visible)
843             {
844               scrolled_window->hscrollbar_visible = TRUE;
845               scrolled_window->vscrollbar_visible = TRUE;
846
847               /* a new resize is already queued at this point,
848                * so we will immediatedly get reinvoked
849                */
850               return;
851             }
852           
853           count++;
854         }
855       while (previous_hvis != scrolled_window->hscrollbar_visible ||
856              previous_vvis != scrolled_window->vscrollbar_visible);
857     }
858   else
859     gtk_scrolled_window_relative_allocation (widget, &relative_allocation);
860   
861   if (scrolled_window->hscrollbar_visible)
862     {
863       GtkRequisition hscrollbar_requisition;
864       gtk_widget_get_child_requisition (scrolled_window->hscrollbar,
865                                         &hscrollbar_requisition);
866   
867       if (!GTK_WIDGET_VISIBLE (scrolled_window->hscrollbar))
868         gtk_widget_show (scrolled_window->hscrollbar);
869
870       child_allocation.x = relative_allocation.x;
871       if (scrolled_window->window_placement == GTK_CORNER_TOP_LEFT ||
872           scrolled_window->window_placement == GTK_CORNER_TOP_RIGHT)
873         child_allocation.y = (relative_allocation.y +
874                               relative_allocation.height +
875                               SCROLLBAR_SPACING (scrolled_window) +
876                               (scrolled_window->shadow_type == GTK_SHADOW_NONE ?
877                                0 : widget->style->ythickness));
878       else
879         child_allocation.y = GTK_CONTAINER (scrolled_window)->border_width;
880
881       child_allocation.width = relative_allocation.width;
882       child_allocation.height = hscrollbar_requisition.height;
883       child_allocation.x += allocation->x;
884       child_allocation.y += allocation->y;
885
886       if (scrolled_window->shadow_type != GTK_SHADOW_NONE)
887         {
888           child_allocation.x -= widget->style->xthickness;
889           child_allocation.width += 2 * widget->style->xthickness;
890         }
891
892       gtk_widget_size_allocate (scrolled_window->hscrollbar, &child_allocation);
893     }
894   else if (GTK_WIDGET_VISIBLE (scrolled_window->hscrollbar))
895     gtk_widget_hide (scrolled_window->hscrollbar);
896
897   if (scrolled_window->vscrollbar_visible)
898     {
899       GtkRequisition vscrollbar_requisition;
900       if (!GTK_WIDGET_VISIBLE (scrolled_window->vscrollbar))
901         gtk_widget_show (scrolled_window->vscrollbar);
902
903       gtk_widget_get_child_requisition (scrolled_window->vscrollbar,
904                                         &vscrollbar_requisition);
905
906       if (scrolled_window->window_placement == GTK_CORNER_TOP_LEFT ||
907           scrolled_window->window_placement == GTK_CORNER_BOTTOM_LEFT)
908         child_allocation.x = (relative_allocation.x +
909                               relative_allocation.width +
910                               SCROLLBAR_SPACING (scrolled_window) +
911                               (scrolled_window->shadow_type == GTK_SHADOW_NONE ?
912                                0 : widget->style->xthickness));
913       else
914         child_allocation.x = GTK_CONTAINER (scrolled_window)->border_width;
915
916       child_allocation.y = relative_allocation.y;
917       child_allocation.width = vscrollbar_requisition.width;
918       child_allocation.height = relative_allocation.height;
919       child_allocation.x += allocation->x;
920       child_allocation.y += allocation->y;
921
922       if (scrolled_window->shadow_type != GTK_SHADOW_NONE)
923         {
924           child_allocation.y -= widget->style->ythickness;
925           child_allocation.height += 2 * widget->style->ythickness;
926         }
927
928       gtk_widget_size_allocate (scrolled_window->vscrollbar, &child_allocation);
929     }
930   else if (GTK_WIDGET_VISIBLE (scrolled_window->vscrollbar))
931     gtk_widget_hide (scrolled_window->vscrollbar);
932 }
933
934 static gint
935 gtk_scrolled_window_scroll_event (GtkWidget *widget,
936                                   GdkEventScroll *event)
937 {
938   GtkWidget *range;
939
940   g_return_val_if_fail (widget != NULL, FALSE);
941   g_return_val_if_fail (GTK_IS_SCROLLED_WINDOW (widget), FALSE);
942   g_return_val_if_fail (event != NULL, FALSE);  
943
944   if (event->direction == GDK_SCROLL_UP || event->direction == GDK_SCROLL_DOWN)
945     range = GTK_SCROLLED_WINDOW (widget)->vscrollbar;
946   else
947     range = GTK_SCROLLED_WINDOW (widget)->hscrollbar;
948
949   if (range && GTK_WIDGET_VISIBLE (range))
950     {
951       GtkAdjustment *adj = GTK_RANGE (range)->adjustment;
952       gdouble new_value;
953
954       if (event->direction == GDK_SCROLL_UP || event->direction == GDK_SCROLL_LEFT)
955         new_value = adj->value - adj->page_increment / 2;
956       else
957         new_value = adj->value + adj->page_increment / 2;
958
959       new_value = CLAMP (new_value, adj->lower, adj->upper - adj->page_size);
960       gtk_adjustment_set_value (adj, new_value);
961
962       return TRUE;
963     }
964
965   return FALSE;
966 }
967
968 static void
969 gtk_scrolled_window_adjustment_changed (GtkAdjustment *adjustment,
970                                         gpointer       data)
971 {
972   GtkScrolledWindow *scrolled_win;
973
974   g_return_if_fail (adjustment != NULL);
975   g_return_if_fail (data != NULL);
976
977   scrolled_win = GTK_SCROLLED_WINDOW (data);
978
979   if (adjustment == gtk_range_get_adjustment (GTK_RANGE (scrolled_win->hscrollbar)))
980     {
981       if (scrolled_win->hscrollbar_policy == GTK_POLICY_AUTOMATIC)
982         {
983           gboolean visible;
984           
985           visible = scrolled_win->hscrollbar_visible;
986           scrolled_win->hscrollbar_visible = (adjustment->upper - adjustment->lower >
987                                               adjustment->page_size);
988           if (scrolled_win->hscrollbar_visible != visible)
989             gtk_widget_queue_resize (GTK_WIDGET (scrolled_win));
990         }
991     }
992   else if (adjustment == gtk_range_get_adjustment (GTK_RANGE (scrolled_win->vscrollbar)))
993     {
994       if (scrolled_win->vscrollbar_policy == GTK_POLICY_AUTOMATIC)
995         {
996           gboolean visible;
997
998           visible = scrolled_win->vscrollbar_visible;
999           scrolled_win->vscrollbar_visible = (adjustment->upper - adjustment->lower >
1000                                               adjustment->page_size);
1001           if (scrolled_win->vscrollbar_visible != visible)
1002             gtk_widget_queue_resize (GTK_WIDGET (scrolled_win));
1003         }
1004     }
1005 }
1006
1007 static void
1008 gtk_scrolled_window_add (GtkContainer *container,
1009                          GtkWidget    *child)
1010 {
1011   GtkScrolledWindow *scrolled_window;
1012   GtkBin *bin;
1013
1014   bin = GTK_BIN (container);
1015   g_return_if_fail (bin->child == NULL);
1016
1017   scrolled_window = GTK_SCROLLED_WINDOW (container);
1018
1019   bin->child = child;
1020   gtk_widget_set_parent (child, GTK_WIDGET (bin));
1021
1022   /* this is a temporary message */
1023   if (!gtk_widget_set_scroll_adjustments (child,
1024                                           gtk_range_get_adjustment (GTK_RANGE (scrolled_window->hscrollbar)),
1025                                           gtk_range_get_adjustment (GTK_RANGE (scrolled_window->vscrollbar))))
1026     g_warning ("gtk_scrolled_window_add(): cannot add non scrollable widget "
1027                "use gtk_scrolled_window_add_with_viewport() instead");
1028
1029   if (GTK_WIDGET_REALIZED (child->parent))
1030     gtk_widget_realize (child);
1031
1032   if (GTK_WIDGET_VISIBLE (child->parent) && GTK_WIDGET_VISIBLE (child))
1033     {
1034       if (GTK_WIDGET_MAPPED (child->parent))
1035         gtk_widget_map (child);
1036
1037       gtk_widget_queue_resize (child);
1038     }
1039 }
1040
1041 static void
1042 gtk_scrolled_window_remove (GtkContainer *container,
1043                             GtkWidget    *child)
1044 {
1045   g_return_if_fail (container != NULL);
1046   g_return_if_fail (GTK_IS_SCROLLED_WINDOW (container));
1047   g_return_if_fail (child != NULL);
1048   g_return_if_fail (GTK_BIN (container)->child == child);
1049   
1050   gtk_widget_set_scroll_adjustments (child, NULL, NULL);
1051
1052   /* chain parent class handler to remove child */
1053   GTK_CONTAINER_CLASS (parent_class)->remove (container, child);
1054 }
1055
1056 void
1057 gtk_scrolled_window_add_with_viewport (GtkScrolledWindow *scrolled_window,
1058                                        GtkWidget         *child)
1059 {
1060   GtkBin *bin;
1061   GtkWidget *viewport;
1062
1063   g_return_if_fail (scrolled_window != NULL);
1064   g_return_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window));
1065   g_return_if_fail (child != NULL);
1066   g_return_if_fail (GTK_IS_WIDGET (child));
1067   g_return_if_fail (child->parent == NULL);
1068
1069   bin = GTK_BIN (scrolled_window);
1070
1071   if (bin->child != NULL)
1072     {
1073       g_return_if_fail (GTK_IS_VIEWPORT (bin->child));
1074       g_return_if_fail (GTK_BIN (bin->child)->child == NULL);
1075
1076       viewport = bin->child;
1077     }
1078   else
1079     {
1080       viewport =
1081         gtk_viewport_new (gtk_scrolled_window_get_hadjustment (scrolled_window),
1082                           gtk_scrolled_window_get_vadjustment (scrolled_window));
1083       gtk_container_add (GTK_CONTAINER (scrolled_window), viewport);
1084     }
1085
1086   gtk_widget_show (viewport);
1087   gtk_container_add (GTK_CONTAINER (viewport), child);
1088 }