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