]> Pileus Git - ~andy/gtk/blob - gtk/gtkscrolledwindow.c
More extensive debugging output
[~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 Library 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  * Library General Public License for more details.
13  *
14  * You should have received a copy of the GNU Library General Public
15  * License along with this library; if not, write to the Free
16  * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17  */
18 #include "gtkscrolledwindow.h"
19 #include "gtksignal.h"
20
21
22 #define SCROLLBAR_SPACING(w) (GTK_SCROLLED_WINDOW_CLASS (GTK_OBJECT (w)->klass)->scrollbar_spacing)
23
24
25 static void gtk_scrolled_window_class_init         (GtkScrolledWindowClass *klass);
26 static void gtk_scrolled_window_init               (GtkScrolledWindow      *scrolled_window);
27 static void gtk_scrolled_window_destroy            (GtkObject              *object);
28 static void gtk_scrolled_window_finalize           (GtkObject              *object);
29 static void gtk_scrolled_window_map                (GtkWidget              *widget);
30 static void gtk_scrolled_window_unmap              (GtkWidget              *widget);
31 static void gtk_scrolled_window_draw               (GtkWidget              *widget,
32                                                     GdkRectangle           *area);
33 static void gtk_scrolled_window_size_request       (GtkWidget              *widget,
34                                                     GtkRequisition         *requisition);
35 static void gtk_scrolled_window_size_allocate      (GtkWidget              *widget,
36                                                     GtkAllocation          *allocation);
37 static void gtk_scrolled_window_add                (GtkContainer           *container,
38                                                     GtkWidget              *widget);
39 static void gtk_scrolled_window_remove             (GtkContainer           *container,
40                                                     GtkWidget              *widget);
41 static void gtk_scrolled_window_foreach            (GtkContainer           *container,
42                                                     GtkCallback             callback,
43                                                     gpointer                callback_data);
44 static void gtk_scrolled_window_viewport_allocate  (GtkWidget              *widget,
45                                                     GtkAllocation          *allocation);
46 static void gtk_scrolled_window_adjustment_changed (GtkAdjustment          *adjustment,
47                                                     gpointer                data);
48
49
50 static GtkContainerClass *parent_class = NULL;
51
52
53 guint
54 gtk_scrolled_window_get_type ()
55 {
56   static guint scrolled_window_type = 0;
57
58   if (!scrolled_window_type)
59     {
60       GtkTypeInfo scrolled_window_info =
61       {
62         "GtkScrolledWindow",
63         sizeof (GtkScrolledWindow),
64         sizeof (GtkScrolledWindowClass),
65         (GtkClassInitFunc) gtk_scrolled_window_class_init,
66         (GtkObjectInitFunc) gtk_scrolled_window_init,
67         (GtkArgSetFunc) NULL,
68         (GtkArgGetFunc) NULL,
69       };
70
71       scrolled_window_type = gtk_type_unique (gtk_container_get_type (), &scrolled_window_info);
72     }
73
74   return scrolled_window_type;
75 }
76
77 static void
78 gtk_scrolled_window_class_init (GtkScrolledWindowClass *class)
79 {
80   GtkObjectClass *object_class;
81   GtkWidgetClass *widget_class;
82   GtkContainerClass *container_class;
83
84   object_class = (GtkObjectClass*) class;
85   widget_class = (GtkWidgetClass*) class;
86   container_class = (GtkContainerClass*) class;
87
88   parent_class = gtk_type_class (gtk_container_get_type ());
89
90   object_class->destroy = gtk_scrolled_window_destroy;
91   object_class->finalize = gtk_scrolled_window_finalize;
92
93   widget_class->map = gtk_scrolled_window_map;
94   widget_class->unmap = gtk_scrolled_window_unmap;
95   widget_class->draw = gtk_scrolled_window_draw;
96   widget_class->size_request = gtk_scrolled_window_size_request;
97   widget_class->size_allocate = gtk_scrolled_window_size_allocate;
98
99   container_class->add = gtk_scrolled_window_add;
100   container_class->remove = gtk_scrolled_window_remove;
101   container_class->foreach = gtk_scrolled_window_foreach;
102
103   class->scrollbar_spacing = 5;
104 }
105
106 static void
107 gtk_scrolled_window_init (GtkScrolledWindow *scrolled_window)
108 {
109   GTK_WIDGET_SET_FLAGS (scrolled_window, GTK_NO_WINDOW);
110
111   scrolled_window->hscrollbar = NULL;
112   scrolled_window->vscrollbar = NULL;
113   scrolled_window->hscrollbar_policy = GTK_POLICY_ALWAYS;
114   scrolled_window->vscrollbar_policy = GTK_POLICY_ALWAYS;
115 }
116
117 GtkWidget*
118 gtk_scrolled_window_new (GtkAdjustment *hadjustment,
119                          GtkAdjustment *vadjustment)
120 {
121   GtkWidget *scrolled_window;
122
123   scrolled_window = gtk_type_new (gtk_scrolled_window_get_type ());
124
125   gtk_scrolled_window_construct (GTK_SCROLLED_WINDOW (scrolled_window), hadjustment, vadjustment);
126   
127   return scrolled_window;
128 }
129
130 void
131 gtk_scrolled_window_construct (GtkScrolledWindow *scrolled_window,
132                                GtkAdjustment     *hadjustment,
133                                GtkAdjustment     *vadjustment)
134 {
135   g_return_if_fail (scrolled_window != NULL);
136   g_return_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window));
137   g_return_if_fail (scrolled_window->viewport == NULL);
138
139   scrolled_window->viewport = gtk_viewport_new (hadjustment, vadjustment);
140   hadjustment = gtk_viewport_get_hadjustment (GTK_VIEWPORT (scrolled_window->viewport));
141   vadjustment = gtk_viewport_get_vadjustment (GTK_VIEWPORT (scrolled_window->viewport));
142
143   gtk_signal_connect (GTK_OBJECT (hadjustment), "changed",
144                       (GtkSignalFunc) gtk_scrolled_window_adjustment_changed,
145                       (gpointer) scrolled_window);
146   gtk_signal_connect (GTK_OBJECT (vadjustment), "changed",
147                       (GtkSignalFunc) gtk_scrolled_window_adjustment_changed,
148                       (gpointer) scrolled_window);
149
150   scrolled_window->hscrollbar = gtk_hscrollbar_new (hadjustment);
151   scrolled_window->vscrollbar = gtk_vscrollbar_new (vadjustment);
152
153   gtk_widget_set_parent (scrolled_window->viewport, GTK_WIDGET (scrolled_window));
154   gtk_widget_set_parent (scrolled_window->hscrollbar, GTK_WIDGET (scrolled_window));
155   gtk_widget_set_parent (scrolled_window->vscrollbar, GTK_WIDGET (scrolled_window));
156
157   gtk_widget_show (scrolled_window->viewport);
158   gtk_widget_show (scrolled_window->hscrollbar);
159   gtk_widget_show (scrolled_window->vscrollbar);
160   
161   gtk_widget_ref (scrolled_window->viewport);
162   gtk_widget_ref (scrolled_window->hscrollbar);
163   gtk_widget_ref (scrolled_window->vscrollbar);
164 }
165
166 GtkAdjustment*
167 gtk_scrolled_window_get_hadjustment (GtkScrolledWindow *scrolled_window)
168 {
169   g_return_val_if_fail (scrolled_window != NULL, NULL);
170   g_return_val_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window), NULL);
171
172   return gtk_range_get_adjustment (GTK_RANGE (scrolled_window->hscrollbar));
173 }
174
175 GtkAdjustment*
176 gtk_scrolled_window_get_vadjustment (GtkScrolledWindow *scrolled_window)
177 {
178   g_return_val_if_fail (scrolled_window != NULL, NULL);
179   g_return_val_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window), NULL);
180
181   return gtk_range_get_adjustment (GTK_RANGE (scrolled_window->vscrollbar));
182 }
183
184 void
185 gtk_scrolled_window_set_policy (GtkScrolledWindow *scrolled_window,
186                                 GtkPolicyType      hscrollbar_policy,
187                                 GtkPolicyType      vscrollbar_policy)
188 {
189   g_return_if_fail (scrolled_window != NULL);
190   g_return_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window));
191
192   if ((scrolled_window->hscrollbar_policy != hscrollbar_policy) ||
193       (scrolled_window->vscrollbar_policy != vscrollbar_policy))
194     {
195       scrolled_window->hscrollbar_policy = hscrollbar_policy;
196       scrolled_window->vscrollbar_policy = vscrollbar_policy;
197
198       if (GTK_WIDGET (scrolled_window)->parent)
199         gtk_widget_queue_resize (GTK_WIDGET (scrolled_window));
200     }
201 }
202
203
204 static void
205 gtk_scrolled_window_destroy (GtkObject *object)
206 {
207   GtkScrolledWindow *scrolled_window;
208
209   g_return_if_fail (object != NULL);
210   g_return_if_fail (GTK_IS_SCROLLED_WINDOW (object));
211
212   scrolled_window = GTK_SCROLLED_WINDOW (object);
213
214   gtk_widget_destroy (scrolled_window->viewport);
215   gtk_widget_destroy (scrolled_window->hscrollbar);
216   gtk_widget_destroy (scrolled_window->vscrollbar);
217
218   if (GTK_OBJECT_CLASS (parent_class)->destroy)
219     (* GTK_OBJECT_CLASS (parent_class)->destroy) (object);
220 }
221
222 static void
223 gtk_scrolled_window_finalize (GtkObject *object)
224 {
225   GtkScrolledWindow *scrolled_window;
226
227   scrolled_window = GTK_SCROLLED_WINDOW (object);
228   gtk_widget_unref (scrolled_window->viewport);
229   gtk_widget_unref (scrolled_window->hscrollbar);
230   gtk_widget_unref (scrolled_window->vscrollbar);
231
232   GTK_OBJECT_CLASS (parent_class)->finalize (object);
233 }
234
235 static void
236 gtk_scrolled_window_map (GtkWidget *widget)
237 {
238   GtkScrolledWindow *scrolled_window;
239
240   g_return_if_fail (widget != NULL);
241   g_return_if_fail (GTK_IS_SCROLLED_WINDOW (widget));
242
243   if (!GTK_WIDGET_MAPPED (widget))
244     {
245       GTK_WIDGET_SET_FLAGS (widget, GTK_MAPPED);
246       scrolled_window = GTK_SCROLLED_WINDOW (widget);
247
248       if (GTK_WIDGET_VISIBLE (scrolled_window->viewport) &&
249           !GTK_WIDGET_MAPPED (scrolled_window->viewport))
250         gtk_widget_map (scrolled_window->viewport);
251
252       if (GTK_WIDGET_VISIBLE (scrolled_window->hscrollbar) &&
253           !GTK_WIDGET_MAPPED (scrolled_window->hscrollbar))
254         gtk_widget_map (scrolled_window->hscrollbar);
255
256       if (GTK_WIDGET_VISIBLE (scrolled_window->vscrollbar) &&
257           !GTK_WIDGET_MAPPED (scrolled_window->vscrollbar))
258         gtk_widget_map (scrolled_window->vscrollbar);
259     }
260 }
261
262 static void
263 gtk_scrolled_window_unmap (GtkWidget *widget)
264 {
265   GtkScrolledWindow *scrolled_window;
266
267   g_return_if_fail (widget != NULL);
268   g_return_if_fail (GTK_IS_SCROLLED_WINDOW (widget));
269
270   if (GTK_WIDGET_MAPPED (widget))
271     {
272       GTK_WIDGET_UNSET_FLAGS (widget, GTK_MAPPED);
273       scrolled_window = GTK_SCROLLED_WINDOW (widget);
274
275       if (GTK_WIDGET_MAPPED (scrolled_window->viewport))
276         gtk_widget_unmap (scrolled_window->viewport);
277
278       if (GTK_WIDGET_MAPPED (scrolled_window->hscrollbar))
279         gtk_widget_unmap (scrolled_window->hscrollbar);
280
281       if (GTK_WIDGET_MAPPED (scrolled_window->vscrollbar))
282         gtk_widget_unmap (scrolled_window->vscrollbar);
283     }
284 }
285
286 static void
287 gtk_scrolled_window_draw (GtkWidget    *widget,
288                           GdkRectangle *area)
289 {
290   GtkScrolledWindow *scrolled_window;
291   GdkRectangle child_area;
292
293   g_return_if_fail (widget != NULL);
294   g_return_if_fail (GTK_IS_SCROLLED_WINDOW (widget));
295   g_return_if_fail (area != NULL);
296
297   if (GTK_WIDGET_DRAWABLE (widget))
298     {
299       scrolled_window = GTK_SCROLLED_WINDOW (widget);
300
301       if (gtk_widget_intersect (scrolled_window->viewport, area, &child_area))
302         gtk_widget_draw (scrolled_window->viewport, &child_area);
303
304       if (gtk_widget_intersect (scrolled_window->hscrollbar, area, &child_area))
305         gtk_widget_draw (scrolled_window->hscrollbar, &child_area);
306
307       if (gtk_widget_intersect (scrolled_window->vscrollbar, area, &child_area))
308         gtk_widget_draw (scrolled_window->vscrollbar, &child_area);
309     }
310 }
311
312 static void
313 gtk_scrolled_window_size_request (GtkWidget      *widget,
314                                   GtkRequisition *requisition)
315 {
316   GtkScrolledWindow *scrolled_window;
317   gint extra_height;
318   gint extra_width;
319
320   g_return_if_fail (widget != NULL);
321   g_return_if_fail (GTK_IS_SCROLLED_WINDOW (widget));
322   g_return_if_fail (requisition != NULL);
323
324   scrolled_window = GTK_SCROLLED_WINDOW (widget);
325
326   requisition->width = 0;
327   requisition->height = 0;
328
329   if (GTK_WIDGET_VISIBLE (scrolled_window->viewport))
330     {
331       gtk_widget_size_request (scrolled_window->viewport, &scrolled_window->viewport->requisition);
332
333       requisition->width += scrolled_window->viewport->requisition.width;
334       requisition->height += scrolled_window->viewport->requisition.height;
335     }
336
337   extra_width = 0;
338   extra_height = 0;
339
340   if ((scrolled_window->hscrollbar_policy == GTK_POLICY_AUTOMATIC) ||
341       GTK_WIDGET_VISIBLE (scrolled_window->hscrollbar))
342     {
343       gtk_widget_size_request (scrolled_window->hscrollbar,
344                                &scrolled_window->hscrollbar->requisition);
345
346       requisition->width = MAX (requisition->width, scrolled_window->hscrollbar->requisition.width);
347       extra_height = SCROLLBAR_SPACING (scrolled_window) + scrolled_window->hscrollbar->requisition.height;
348     }
349
350   if ((scrolled_window->vscrollbar_policy == GTK_POLICY_AUTOMATIC) ||
351       GTK_WIDGET_VISIBLE (scrolled_window->vscrollbar))
352     {
353       gtk_widget_size_request (scrolled_window->vscrollbar,
354                                &scrolled_window->vscrollbar->requisition);
355
356       requisition->height = MAX (requisition->height, scrolled_window->vscrollbar->requisition.height);
357       extra_width = SCROLLBAR_SPACING (scrolled_window) + scrolled_window->vscrollbar->requisition.width;
358     }
359
360   requisition->width += GTK_CONTAINER (widget)->border_width * 2 + extra_width;
361   requisition->height += GTK_CONTAINER (widget)->border_width * 2 + extra_height;
362 }
363
364 static void
365 gtk_scrolled_window_size_allocate (GtkWidget     *widget,
366                                    GtkAllocation *allocation)
367 {
368   GtkScrolledWindow *scrolled_window;
369   GtkAllocation viewport_allocation;
370   GtkAllocation child_allocation;
371   guint previous_hvis;
372   guint previous_vvis;
373
374   g_return_if_fail (widget != NULL);
375   g_return_if_fail (GTK_IS_SCROLLED_WINDOW (widget));
376   g_return_if_fail (allocation != NULL);
377
378   scrolled_window = GTK_SCROLLED_WINDOW (widget);
379   widget->allocation = *allocation;
380
381   gtk_scrolled_window_viewport_allocate (widget, &viewport_allocation);
382
383   gtk_container_disable_resize (GTK_CONTAINER (scrolled_window));
384
385   if (GTK_WIDGET_VISIBLE (scrolled_window->viewport))
386     {
387       do {
388         gtk_scrolled_window_viewport_allocate (widget, &viewport_allocation);
389
390         child_allocation.x = viewport_allocation.x + allocation->x;
391         child_allocation.y = viewport_allocation.y + allocation->y;
392         child_allocation.width = viewport_allocation.width;
393         child_allocation.height = viewport_allocation.height;
394
395         previous_hvis = GTK_WIDGET_VISIBLE (scrolled_window->hscrollbar);
396         previous_vvis = GTK_WIDGET_VISIBLE (scrolled_window->vscrollbar);
397
398         gtk_widget_size_allocate (scrolled_window->viewport, &child_allocation);
399       } while ((previous_hvis != GTK_WIDGET_VISIBLE (scrolled_window->hscrollbar)) ||
400                (previous_vvis != GTK_WIDGET_VISIBLE (scrolled_window->vscrollbar)));
401     }
402
403   if (GTK_WIDGET_VISIBLE (scrolled_window->hscrollbar))
404     {
405       child_allocation.x = viewport_allocation.x;
406       child_allocation.y = viewport_allocation.y + viewport_allocation.height + SCROLLBAR_SPACING (scrolled_window);
407       child_allocation.width = viewport_allocation.width;
408       child_allocation.height = scrolled_window->hscrollbar->requisition.height;
409       child_allocation.x += allocation->x;
410       child_allocation.y += allocation->y;
411
412       gtk_widget_size_allocate (scrolled_window->hscrollbar, &child_allocation);
413     }
414
415   if (GTK_WIDGET_VISIBLE (scrolled_window->vscrollbar))
416     {
417       child_allocation.x = viewport_allocation.x + viewport_allocation.width + SCROLLBAR_SPACING (scrolled_window);
418       child_allocation.y = viewport_allocation.y;
419       child_allocation.width = scrolled_window->vscrollbar->requisition.width;
420       child_allocation.height = viewport_allocation.height;
421       child_allocation.x += allocation->x;
422       child_allocation.y += allocation->y;
423
424       gtk_widget_size_allocate (scrolled_window->vscrollbar, &child_allocation);
425     }
426
427   gtk_container_enable_resize (GTK_CONTAINER (scrolled_window));
428 }
429
430 static void
431 gtk_scrolled_window_add (GtkContainer *container,
432                          GtkWidget    *widget)
433 {
434   GtkScrolledWindow *scrolled_window;
435
436   g_return_if_fail (container != NULL);
437   g_return_if_fail (GTK_IS_SCROLLED_WINDOW (container));
438   g_return_if_fail (widget != NULL);
439
440   scrolled_window = GTK_SCROLLED_WINDOW (container);
441   gtk_container_add (GTK_CONTAINER (scrolled_window->viewport), widget);
442 }
443
444 static void
445 gtk_scrolled_window_remove (GtkContainer *container,
446                             GtkWidget    *widget)
447 {
448   GtkScrolledWindow *scrolled_window;
449
450   g_return_if_fail (container != NULL);
451   g_return_if_fail (GTK_IS_SCROLLED_WINDOW (container));
452   g_return_if_fail (widget != NULL);
453
454   scrolled_window = GTK_SCROLLED_WINDOW (container);
455
456   if (scrolled_window->viewport == widget ||
457       scrolled_window->hscrollbar == widget ||
458       scrolled_window->vscrollbar == widget)
459     {
460       /* this happens during destroy */
461       gtk_widget_unparent (widget);
462     }
463   else
464     gtk_container_remove (GTK_CONTAINER (scrolled_window->viewport), widget);
465 }
466
467 static void
468 gtk_scrolled_window_foreach (GtkContainer *container,
469                              GtkCallback   callback,
470                              gpointer      callback_data)
471 {
472   GtkScrolledWindow *scrolled_window;
473
474   g_return_if_fail (container != NULL);
475   g_return_if_fail (GTK_IS_SCROLLED_WINDOW (container));
476   g_return_if_fail (callback != NULL);
477
478   scrolled_window = GTK_SCROLLED_WINDOW (container);
479
480   (* callback) (scrolled_window->viewport, callback_data);
481 }
482
483 static void
484 gtk_scrolled_window_viewport_allocate (GtkWidget     *widget,
485                                        GtkAllocation *allocation)
486 {
487   GtkScrolledWindow *scrolled_window;
488
489   g_return_if_fail (widget != NULL);
490   g_return_if_fail (allocation != NULL);
491
492   scrolled_window = GTK_SCROLLED_WINDOW (widget);
493
494   allocation->x = GTK_CONTAINER (widget)->border_width;
495   allocation->y = GTK_CONTAINER (widget)->border_width;
496   allocation->width = MAX (0, widget->allocation.width - allocation->x * 2);
497   allocation->height = MAX (0, widget->allocation.height - allocation->y * 2);
498
499   if (GTK_WIDGET_VISIBLE (scrolled_window->vscrollbar))
500     allocation->width = MAX (0,
501       allocation->width - (scrolled_window->vscrollbar->requisition.width + SCROLLBAR_SPACING (scrolled_window)));
502   if (GTK_WIDGET_VISIBLE (scrolled_window->hscrollbar))
503     allocation->height = MAX (0, 
504       allocation->height - (scrolled_window->hscrollbar->requisition.height + SCROLLBAR_SPACING (scrolled_window)));
505 }
506
507 static void
508 gtk_scrolled_window_adjustment_changed (GtkAdjustment *adjustment,
509                                         gpointer       data)
510 {
511   GtkScrolledWindow *scrolled_win;
512   GtkWidget *scrollbar;
513   gint hide_scrollbar;
514   gint policy;
515
516   g_return_if_fail (adjustment != NULL);
517   g_return_if_fail (data != NULL);
518
519   scrolled_win = GTK_SCROLLED_WINDOW (data);
520
521   if (adjustment == gtk_range_get_adjustment (GTK_RANGE (scrolled_win->hscrollbar)))
522     {
523       scrollbar = scrolled_win->hscrollbar;
524       policy = scrolled_win->hscrollbar_policy;
525     }
526   else if (adjustment == gtk_range_get_adjustment (GTK_RANGE (scrolled_win->vscrollbar)))
527     {
528       scrollbar = scrolled_win->vscrollbar;
529       policy = scrolled_win->vscrollbar_policy;
530     }
531   else
532     {
533       g_warning ("could not determine which adjustment scrollbar received change signal for");
534       return;
535     }
536
537   if (policy == GTK_POLICY_AUTOMATIC)
538     {
539       hide_scrollbar = FALSE;
540
541       if ((adjustment->upper - adjustment->lower) <= adjustment->page_size)
542         hide_scrollbar = TRUE;
543
544       if (hide_scrollbar)
545         {
546           if (GTK_WIDGET_VISIBLE (scrollbar))
547             gtk_widget_hide (scrollbar);
548         }
549       else
550         {
551           if (!GTK_WIDGET_VISIBLE (scrollbar))
552             gtk_widget_show (scrollbar);
553         }
554     }
555 }