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