]> Pileus Git - ~andy/gtk/blob - gtk/gtkviewport.c
Initial revision
[~andy/gtk] / gtk / gtkviewport.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 "gtksignal.h"
19 #include "gtkviewport.h"
20
21
22 static void gtk_viewport_class_init               (GtkViewportClass *klass);
23 static void gtk_viewport_init                     (GtkViewport      *viewport);
24 static void gtk_viewport_map                      (GtkWidget        *widget);
25 static void gtk_viewport_unmap                    (GtkWidget        *widget);
26 static void gtk_viewport_realize                  (GtkWidget        *widget);
27 static void gtk_viewport_unrealize                (GtkWidget        *widget);
28 static void gtk_viewport_paint                    (GtkWidget        *widget,
29                                                    GdkRectangle     *area);
30 static void gtk_viewport_draw                     (GtkWidget        *widget,
31                                                    GdkRectangle     *area);
32 static gint gtk_viewport_expose                   (GtkWidget        *widget,
33                                                    GdkEventExpose   *event);
34 static void gtk_viewport_size_request             (GtkWidget        *widget,
35                                                    GtkRequisition   *requisition);
36 static void gtk_viewport_size_allocate            (GtkWidget        *widget,
37                                                    GtkAllocation    *allocation);
38 static gint gtk_viewport_need_resize              (GtkContainer     *container);
39 static void gtk_viewport_adjustment_changed       (GtkAdjustment    *adjustment,
40                                                    gpointer          data);
41 static void gtk_viewport_adjustment_value_changed (GtkAdjustment    *adjustment,
42                                                    gpointer          data);
43
44
45 guint
46 gtk_viewport_get_type ()
47 {
48   static guint viewport_type = 0;
49
50   if (!viewport_type)
51     {
52       GtkTypeInfo viewport_info =
53       {
54         "GtkViewport",
55         sizeof (GtkViewport),
56         sizeof (GtkViewportClass),
57         (GtkClassInitFunc) gtk_viewport_class_init,
58         (GtkObjectInitFunc) gtk_viewport_init,
59         (GtkArgFunc) NULL,
60       };
61
62       viewport_type = gtk_type_unique (gtk_bin_get_type (), &viewport_info);
63     }
64
65   return viewport_type;
66 }
67
68 static void
69 gtk_viewport_class_init (GtkViewportClass *class)
70 {
71   GtkWidgetClass *widget_class;
72   GtkContainerClass *container_class;
73
74   widget_class = (GtkWidgetClass*) class;
75   container_class = (GtkContainerClass*) class;
76
77   widget_class->map = gtk_viewport_map;
78   widget_class->unmap = gtk_viewport_unmap;
79   widget_class->realize = gtk_viewport_realize;
80   widget_class->unrealize = gtk_viewport_unrealize;
81   widget_class->draw = gtk_viewport_draw;
82   widget_class->expose_event = gtk_viewport_expose;
83   widget_class->size_request = gtk_viewport_size_request;
84   widget_class->size_allocate = gtk_viewport_size_allocate;
85
86   container_class->need_resize = gtk_viewport_need_resize;
87 }
88
89 static void
90 gtk_viewport_init (GtkViewport *viewport)
91 {
92   GTK_WIDGET_UNSET_FLAGS (viewport, GTK_NO_WINDOW);
93   GTK_WIDGET_SET_FLAGS (viewport, GTK_BASIC);
94
95   viewport->shadow_type = GTK_SHADOW_IN;
96   viewport->main_window = NULL;
97   viewport->view_window = NULL;
98   viewport->hadjustment = NULL;
99   viewport->vadjustment = NULL;
100 }
101
102 GtkWidget*
103 gtk_viewport_new (GtkAdjustment *hadjustment,
104                   GtkAdjustment *vadjustment)
105 {
106   GtkViewport *viewport;
107
108   viewport = gtk_type_new (gtk_viewport_get_type ());
109
110   if (!hadjustment)
111     hadjustment = (GtkAdjustment*) gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0);
112
113   if (!vadjustment)
114     vadjustment = (GtkAdjustment*) gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0);
115
116   gtk_viewport_set_hadjustment (viewport, hadjustment);
117   gtk_viewport_set_vadjustment (viewport, vadjustment);
118
119   return GTK_WIDGET (viewport);
120 }
121
122 GtkAdjustment*
123 gtk_viewport_get_hadjustment (GtkViewport *viewport)
124 {
125   g_return_val_if_fail (viewport != NULL, NULL);
126   g_return_val_if_fail (GTK_IS_VIEWPORT (viewport), NULL);
127
128   return viewport->hadjustment;
129 }
130
131 GtkAdjustment*
132 gtk_viewport_get_vadjustment (GtkViewport *viewport)
133 {
134   g_return_val_if_fail (viewport != NULL, NULL);
135   g_return_val_if_fail (GTK_IS_VIEWPORT (viewport), NULL);
136
137   return viewport->vadjustment;
138 }
139
140 void
141 gtk_viewport_set_hadjustment (GtkViewport   *viewport,
142                               GtkAdjustment *adjustment)
143 {
144   g_return_if_fail (viewport != NULL);
145   g_return_if_fail (GTK_IS_VIEWPORT (viewport));
146   g_return_if_fail (adjustment != NULL);
147
148   if (viewport->hadjustment)
149     {
150       gtk_signal_disconnect_by_data (GTK_OBJECT (viewport->hadjustment), (gpointer) viewport);
151       gtk_object_unref (GTK_OBJECT (viewport->hadjustment));
152     }
153
154   viewport->hadjustment = adjustment;
155   gtk_object_ref (GTK_OBJECT (viewport->hadjustment));
156
157   gtk_signal_connect (GTK_OBJECT (adjustment), "changed",
158                       (GtkSignalFunc) gtk_viewport_adjustment_changed,
159                       (gpointer) viewport);
160   gtk_signal_connect (GTK_OBJECT (adjustment), "value_changed",
161                       (GtkSignalFunc) gtk_viewport_adjustment_value_changed,
162                       (gpointer) viewport);
163
164   gtk_viewport_adjustment_changed (adjustment, (gpointer) viewport);
165 }
166
167 void
168 gtk_viewport_set_vadjustment (GtkViewport   *viewport,
169                               GtkAdjustment *adjustment)
170 {
171   g_return_if_fail (viewport != NULL);
172   g_return_if_fail (GTK_IS_VIEWPORT (viewport));
173   g_return_if_fail (adjustment != NULL);
174
175   if (viewport->vadjustment)
176     {
177       gtk_signal_disconnect_by_data (GTK_OBJECT (viewport->vadjustment), (gpointer) viewport);
178       gtk_object_unref (GTK_OBJECT (viewport->vadjustment));
179     }
180
181   viewport->vadjustment = adjustment;
182   gtk_object_ref (GTK_OBJECT (viewport->vadjustment));
183
184   gtk_signal_connect (GTK_OBJECT (adjustment), "changed",
185                       (GtkSignalFunc) gtk_viewport_adjustment_changed,
186                       (gpointer) viewport);
187   gtk_signal_connect (GTK_OBJECT (adjustment), "value_changed",
188                       (GtkSignalFunc) gtk_viewport_adjustment_value_changed,
189                       (gpointer) viewport);
190
191   gtk_viewport_adjustment_changed (adjustment, (gpointer) viewport);
192 }
193
194 void
195 gtk_viewport_set_shadow_type (GtkViewport   *viewport,
196                               GtkShadowType  type)
197 {
198   g_return_if_fail (viewport != NULL);
199   g_return_if_fail (GTK_IS_VIEWPORT (viewport));
200
201   if ((GtkShadowType) viewport->shadow_type != type)
202     {
203       viewport->shadow_type = type;
204
205       if (GTK_WIDGET_VISIBLE (viewport))
206         {
207           gtk_widget_size_allocate (GTK_WIDGET (viewport), &(GTK_WIDGET (viewport)->allocation));
208           gtk_widget_queue_draw (GTK_WIDGET (viewport));
209         }
210     }
211 }
212
213
214 static void
215 gtk_viewport_map (GtkWidget *widget)
216 {
217   GtkViewport *viewport;
218   GtkBin *bin;
219
220   g_return_if_fail (widget != NULL);
221   g_return_if_fail (GTK_IS_VIEWPORT (widget));
222
223   GTK_WIDGET_SET_FLAGS (widget, GTK_MAPPED);
224   viewport = GTK_VIEWPORT (widget);
225   bin = GTK_BIN (widget);
226
227   gdk_window_show (viewport->main_window);
228
229   if (bin->child &&
230       GTK_WIDGET_VISIBLE (bin->child) &&
231       !GTK_WIDGET_MAPPED (bin->child))
232     gtk_widget_map (bin->child);
233 }
234
235 static void
236 gtk_viewport_unmap (GtkWidget *widget)
237 {
238   GtkViewport *viewport;
239
240   g_return_if_fail (widget != NULL);
241   g_return_if_fail (GTK_IS_VIEWPORT (widget));
242
243   GTK_WIDGET_UNSET_FLAGS (widget, GTK_MAPPED);
244   viewport = GTK_VIEWPORT (widget);
245   
246   gdk_window_hide (viewport->main_window);
247 }
248
249 static void
250 gtk_viewport_realize (GtkWidget *widget)
251 {
252   GtkViewport *viewport;
253   GdkWindowAttr attributes;
254   GtkRequisition *child_requisition;
255   gint attributes_mask;
256
257   g_return_if_fail (widget != NULL);
258   g_return_if_fail (GTK_IS_VIEWPORT (widget));
259
260   viewport = GTK_VIEWPORT (widget);
261   GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
262
263   attributes.x = widget->allocation.x + GTK_CONTAINER (widget)->border_width;
264   attributes.y = widget->allocation.y + GTK_CONTAINER (widget)->border_width;
265   attributes.width = widget->allocation.width - GTK_CONTAINER (widget)->border_width * 2;
266   attributes.height = widget->allocation.height - GTK_CONTAINER (widget)->border_width * 2;
267   attributes.window_type = GDK_WINDOW_CHILD;
268   attributes.wclass = GDK_INPUT_OUTPUT;
269   attributes.visual = gtk_widget_get_visual (widget);
270   attributes.colormap = gtk_widget_get_colormap (widget);
271   attributes.event_mask = gtk_widget_get_events (widget) | GDK_EXPOSURE_MASK;
272
273   attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
274
275   viewport->main_window = gdk_window_new (widget->parent->window, &attributes, attributes_mask);
276   gdk_window_set_user_data (viewport->main_window, viewport);
277
278   attributes.x += widget->style->klass->xthickness;
279   attributes.y += widget->style->klass->ythickness;
280   attributes.width -= widget->style->klass->xthickness * 2;
281   attributes.height -= widget->style->klass->ythickness * 2;
282
283   viewport->view_window = gdk_window_new (viewport->main_window, &attributes, attributes_mask);
284   gdk_window_set_user_data (viewport->view_window, viewport);
285
286   attributes.x = 0;
287   attributes.y = 0;
288
289   if (GTK_BIN (viewport)->child)
290     {
291       child_requisition = &GTK_WIDGET (GTK_BIN (viewport)->child)->requisition;
292       attributes.width = child_requisition->width;
293       attributes.height = child_requisition->height;
294     }
295
296   widget->window = gdk_window_new (viewport->view_window, &attributes, attributes_mask);
297   gdk_window_set_user_data (widget->window, viewport);
298
299   widget->style = gtk_style_attach (widget->style, viewport->main_window);
300   gtk_style_set_background (widget->style, viewport->main_window, GTK_STATE_NORMAL);
301   gtk_style_set_background (widget->style, widget->window, GTK_STATE_NORMAL);
302   
303   gdk_window_show (widget->window);
304   gdk_window_show (viewport->view_window);
305 }
306
307 static void
308 gtk_viewport_unrealize (GtkWidget *widget)
309 {
310   GtkViewport *viewport;
311
312   g_return_if_fail (widget != NULL);
313   g_return_if_fail (GTK_IS_VIEWPORT (widget));
314
315   viewport = GTK_VIEWPORT (widget);
316   GTK_WIDGET_UNSET_FLAGS (widget, GTK_REALIZED | GTK_MAPPED);
317
318   gtk_style_detach (widget->style);
319
320   gdk_window_destroy (widget->window);
321   gdk_window_destroy (viewport->view_window);
322   gdk_window_destroy (viewport->main_window);
323
324   widget->window = NULL;
325   viewport->view_window = NULL;
326   viewport->main_window = NULL;
327 }
328
329 static void
330 gtk_viewport_paint (GtkWidget    *widget,
331                     GdkRectangle *area)
332 {
333   GtkViewport *viewport;
334   GtkStateType state;
335   gint x, y;
336
337   g_return_if_fail (widget != NULL);
338   g_return_if_fail (GTK_IS_VIEWPORT (widget));
339   g_return_if_fail (area != NULL);
340
341   if (GTK_WIDGET_DRAWABLE (widget))
342     {
343       viewport = GTK_VIEWPORT (widget);
344
345       state = widget->state;
346       if (!GTK_WIDGET_IS_SENSITIVE (widget))
347         state = GTK_STATE_INSENSITIVE;
348
349       x = GTK_CONTAINER (viewport)->border_width;
350       y = GTK_CONTAINER (viewport)->border_width;
351
352       gtk_draw_shadow (widget->style, viewport->main_window,
353                        GTK_STATE_NORMAL, viewport->shadow_type,
354                        0, 0, -1, -1);
355     }
356 }
357
358 static void
359 gtk_viewport_draw (GtkWidget    *widget,
360                    GdkRectangle *area)
361 {
362   GtkViewport *viewport;
363   GtkBin *bin;
364   GdkRectangle tmp_area;
365   GdkRectangle child_area;
366
367   g_return_if_fail (widget != NULL);
368   g_return_if_fail (GTK_IS_VIEWPORT (widget));
369   g_return_if_fail (area != NULL);
370
371   if (GTK_WIDGET_DRAWABLE (widget))
372     {
373       viewport = GTK_VIEWPORT (widget);
374       bin = GTK_BIN (widget);
375
376       gtk_viewport_paint (widget, area);
377
378       if (bin->child)
379         {
380           tmp_area = *area;
381           tmp_area.x += viewport->hadjustment->value;
382           tmp_area.y += viewport->vadjustment->value;
383
384           if (gtk_widget_intersect (bin->child, &tmp_area, &child_area))
385             gtk_widget_draw (bin->child, &child_area);
386         }
387     }
388 }
389
390 static gint
391 gtk_viewport_expose (GtkWidget      *widget,
392                      GdkEventExpose *event)
393 {
394   GtkViewport *viewport;
395   GtkBin *bin;
396   GdkEventExpose child_event;
397
398   g_return_val_if_fail (widget != NULL, FALSE);
399   g_return_val_if_fail (GTK_IS_VIEWPORT (widget), FALSE);
400   g_return_val_if_fail (event != NULL, FALSE);
401
402   if (GTK_WIDGET_DRAWABLE (widget))
403     {
404       viewport = GTK_VIEWPORT (widget);
405       bin = GTK_BIN (widget);
406
407       if (event->window == viewport->main_window)
408         gtk_viewport_paint (widget, &event->area);
409
410       child_event = *event;
411       if (bin->child &&
412           GTK_WIDGET_NO_WINDOW (bin->child) &&
413           gtk_widget_intersect (bin->child, &event->area, &child_event.area))
414         gtk_widget_event (bin->child, (GdkEvent*) &child_event);
415     }
416
417   return FALSE;
418 }
419
420 static void
421 gtk_viewport_size_request (GtkWidget      *widget,
422                            GtkRequisition *requisition)
423 {
424   GtkViewport *viewport;
425   GtkBin *bin;
426
427   g_return_if_fail (widget != NULL);
428   g_return_if_fail (GTK_IS_VIEWPORT (widget));
429   g_return_if_fail (requisition != NULL);
430
431   viewport = GTK_VIEWPORT (widget);
432   bin = GTK_BIN (widget);
433
434   requisition->width = (GTK_CONTAINER (widget)->border_width +
435                         GTK_WIDGET (widget)->style->klass->xthickness) * 2 + 5;
436
437   requisition->height = (GTK_CONTAINER (widget)->border_width * 2 +
438                          GTK_WIDGET (widget)->style->klass->ythickness) * 2 + 5;
439
440   if (bin->child && GTK_WIDGET_VISIBLE (bin->child))
441     gtk_widget_size_request (bin->child, &bin->child->requisition);
442 }
443
444 static void
445 gtk_viewport_size_allocate (GtkWidget     *widget,
446                             GtkAllocation *allocation)
447 {
448   GtkViewport *viewport;
449   GtkBin *bin;
450   GtkAllocation child_allocation;
451   gint hval, vval;
452
453   g_return_if_fail (widget != NULL);
454   g_return_if_fail (GTK_IS_VIEWPORT (widget));
455   g_return_if_fail (allocation != NULL);
456
457   widget->allocation = *allocation;
458   viewport = GTK_VIEWPORT (widget);
459   bin = GTK_BIN (widget);
460
461   child_allocation.x = GTK_WIDGET (viewport)->style->klass->xthickness;
462   child_allocation.width = allocation->width - child_allocation.x * 2;
463
464   child_allocation.y = GTK_WIDGET (viewport)->style->klass->ythickness;
465   child_allocation.height = allocation->height - child_allocation.y * 2;
466
467   if (GTK_WIDGET_REALIZED (widget))
468     {
469       gdk_window_move_resize (viewport->main_window,
470                               allocation->x + GTK_CONTAINER (viewport)->border_width,
471                               allocation->y + GTK_CONTAINER (viewport)->border_width,
472                               allocation->width - GTK_CONTAINER (viewport)->border_width * 2,
473                               allocation->height - GTK_CONTAINER (viewport)->border_width * 2);
474
475       gdk_window_move_resize (viewport->view_window,
476                               child_allocation.x,
477                               child_allocation.y,
478                               child_allocation.width,
479                               child_allocation.height);
480     }
481
482   viewport->hadjustment->page_size = child_allocation.width;
483   viewport->hadjustment->page_increment = viewport->hadjustment->page_size / 2;
484   viewport->hadjustment->step_increment = 10;
485
486   viewport->vadjustment->page_size = child_allocation.height;
487   viewport->vadjustment->page_increment = viewport->vadjustment->page_size / 2;
488   viewport->vadjustment->step_increment = 10;
489
490   hval = viewport->hadjustment->value;
491   vval = viewport->vadjustment->value;
492
493   if (bin->child && GTK_WIDGET_VISIBLE (bin->child))
494     {
495       viewport->hadjustment->lower = 0;
496       viewport->hadjustment->upper = MAX (bin->child->requisition.width,
497                                           child_allocation.width);
498
499       hval = CLAMP (hval, 0,
500                     viewport->hadjustment->upper -
501                     viewport->hadjustment->page_size);
502
503       viewport->vadjustment->lower = 0;
504       viewport->vadjustment->upper = MAX (bin->child->requisition.height,
505                                           child_allocation.height);
506
507       vval = CLAMP (vval, 0,
508                     viewport->vadjustment->upper -
509                     viewport->vadjustment->page_size);
510     }
511
512   if (bin->child && GTK_WIDGET_VISIBLE (bin->child))
513     {
514       child_allocation.x = 0;
515       child_allocation.y = 0;
516
517       child_allocation.width = viewport->hadjustment->upper;
518       child_allocation.height = viewport->vadjustment->upper;
519
520       if (!GTK_WIDGET_REALIZED (widget))
521         gtk_widget_realize (widget);
522
523       gdk_window_resize (widget->window,
524                          child_allocation.width,
525                          child_allocation.height);
526
527       child_allocation.x = 0;
528       child_allocation.y = 0;
529       gtk_widget_size_allocate (bin->child, &child_allocation);
530     }
531
532   gtk_signal_emit_by_name (GTK_OBJECT (viewport->hadjustment), "changed");
533   gtk_signal_emit_by_name (GTK_OBJECT (viewport->vadjustment), "changed");
534   if (viewport->hadjustment->value != hval)
535     {
536       viewport->hadjustment->value = hval;
537       gtk_signal_emit_by_name (GTK_OBJECT (viewport->hadjustment), "value_changed");
538     }
539   if (viewport->vadjustment->value != vval)
540     {
541       viewport->vadjustment->value = vval;
542       gtk_signal_emit_by_name (GTK_OBJECT (viewport->vadjustment), "value_changed");
543     }
544 }
545
546 static gint
547 gtk_viewport_need_resize (GtkContainer *container)
548 {
549   GtkBin *bin;
550
551   g_return_val_if_fail (container != NULL, FALSE);
552   g_return_val_if_fail (GTK_IS_VIEWPORT (container), FALSE);
553
554   if (GTK_WIDGET_REALIZED (container))
555     {
556       bin = GTK_BIN (container);
557
558       gtk_widget_size_request (bin->child, &bin->child->requisition);
559
560       gtk_widget_size_allocate (GTK_WIDGET (container),
561                                 &(GTK_WIDGET (container)->allocation));
562     }
563
564   return FALSE;
565 }
566
567 static void
568 gtk_viewport_adjustment_changed (GtkAdjustment *adjustment,
569                                  gpointer       data)
570 {
571   GtkViewport *viewport;
572
573   g_return_if_fail (adjustment != NULL);
574   g_return_if_fail (data != NULL);
575   g_return_if_fail (GTK_IS_VIEWPORT (data));
576
577   viewport = GTK_VIEWPORT (data);
578 }
579
580 static void
581 gtk_viewport_adjustment_value_changed (GtkAdjustment *adjustment,
582                                        gpointer       data)
583 {
584   GtkViewport *viewport;
585   GtkBin *bin;
586   GtkAllocation child_allocation;
587   gint width, height;
588
589   g_return_if_fail (adjustment != NULL);
590   g_return_if_fail (data != NULL);
591   g_return_if_fail (GTK_IS_VIEWPORT (data));
592
593   viewport = GTK_VIEWPORT (data);
594   bin = GTK_BIN (data);
595
596   if (bin->child && GTK_WIDGET_VISIBLE (bin->child))
597     {
598       gdk_window_get_size (viewport->view_window, &width, &height);
599
600       child_allocation.x = 0;
601       child_allocation.y = 0;
602
603       if (viewport->hadjustment->lower != (viewport->hadjustment->upper -
604                                            viewport->hadjustment->page_size))
605         child_allocation.x =  viewport->hadjustment->lower - viewport->hadjustment->value;
606
607       if (viewport->vadjustment->lower != (viewport->vadjustment->upper -
608                                            viewport->vadjustment->page_size))
609         child_allocation.y = viewport->vadjustment->lower - viewport->vadjustment->value;
610
611       if (GTK_WIDGET_REALIZED (viewport))
612         gdk_window_move (GTK_WIDGET (viewport)->window,
613                          child_allocation.x,
614                          child_allocation.y);
615     }
616 }