]> Pileus Git - ~andy/gtk/blob - tests/gtkoffscreenbox.c
Merge branch 'master' into client-side-windows
[~andy/gtk] / tests / gtkoffscreenbox.c
1 /*
2  * gtkoffscreenbox.c
3  */
4
5 #include "config.h"
6
7 #include <math.h>
8 #include <gtk/gtk.h>
9
10 #include "gtkoffscreenbox.h"
11
12 static void        gtk_offscreen_box_realize       (GtkWidget       *widget);
13 static void        gtk_offscreen_box_unrealize     (GtkWidget       *widget);
14 static void        gtk_offscreen_box_size_request  (GtkWidget       *widget,
15                                                     GtkRequisition  *requisition);
16 static void        gtk_offscreen_box_size_allocate (GtkWidget       *widget,
17                                                     GtkAllocation   *allocation);
18 static gboolean    gtk_offscreen_box_damage        (GtkWidget       *widget,
19                                                     GdkEventExpose  *event);
20 static gboolean    gtk_offscreen_box_expose        (GtkWidget       *widget,
21                                                     GdkEventExpose  *offscreen);
22
23 static void        gtk_offscreen_box_add           (GtkContainer    *container,
24                                                     GtkWidget       *child);
25 static void        gtk_offscreen_box_remove        (GtkContainer    *container,
26                                                     GtkWidget       *widget);
27 static void        gtk_offscreen_box_forall        (GtkContainer    *container,
28                                                     gboolean         include_internals,
29                                                     GtkCallback      callback,
30                                                     gpointer         callback_data);
31 static GType       gtk_offscreen_box_child_type    (GtkContainer    *container);
32
33 #define CHILD1_SIZE_SCALE 1.0
34 #define CHILD2_SIZE_SCALE 1.0
35
36 G_DEFINE_TYPE (GtkOffscreenBox, gtk_offscreen_box, GTK_TYPE_CONTAINER);
37
38 static void
39 gtk_offscreen_box_class_init (GtkOffscreenBoxClass *klass)
40 {
41   GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
42   GtkContainerClass *container_class = GTK_CONTAINER_CLASS (klass);
43
44   widget_class->realize = gtk_offscreen_box_realize;
45   widget_class->unrealize = gtk_offscreen_box_unrealize;
46   widget_class->size_request = gtk_offscreen_box_size_request;
47   widget_class->size_allocate = gtk_offscreen_box_size_allocate;
48   widget_class->expose_event = gtk_offscreen_box_expose;
49
50   g_signal_override_class_closure (g_signal_lookup ("damage-event", GTK_TYPE_WIDGET),
51                                    GTK_TYPE_OFFSCREEN_BOX,
52                                    g_cclosure_new (G_CALLBACK (gtk_offscreen_box_damage),
53                                                    NULL, NULL));
54
55   container_class->add = gtk_offscreen_box_add;
56   container_class->remove = gtk_offscreen_box_remove;
57   container_class->forall = gtk_offscreen_box_forall;
58   container_class->child_type = gtk_offscreen_box_child_type;
59 }
60
61 static void
62 gtk_offscreen_box_init (GtkOffscreenBox *offscreen_box)
63 {
64   GTK_WIDGET_UNSET_FLAGS (offscreen_box, GTK_NO_WINDOW);
65 }
66
67 GtkWidget *
68 gtk_offscreen_box_new (void)
69 {
70   return g_object_new (GTK_TYPE_OFFSCREEN_BOX, NULL);
71 }
72
73 static void
74 gtk_offscreen_box_realize (GtkWidget *widget)
75 {
76   GtkOffscreenBox *offscreen_box = GTK_OFFSCREEN_BOX (widget);
77   GdkWindowAttr attributes;
78   gint attributes_mask;
79   gint border_width;
80   GtkRequisition child_requisition;
81   int start_y = 0;
82
83   GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
84
85   border_width = GTK_CONTAINER (widget)->border_width;
86
87   attributes.x = widget->allocation.x + border_width;
88   attributes.y = widget->allocation.y + border_width;
89   attributes.width = widget->allocation.width - 2 * border_width;
90   attributes.height = widget->allocation.height - 2 * border_width;
91   attributes.window_type = GDK_WINDOW_CHILD;
92   attributes.event_mask = gtk_widget_get_events (widget)
93                         | GDK_EXPOSURE_MASK
94                         | GDK_POINTER_MOTION_MASK
95                         | GDK_BUTTON_PRESS_MASK
96                         | GDK_BUTTON_RELEASE_MASK
97                         | GDK_SCROLL_MASK
98                         | GDK_ENTER_NOTIFY_MASK
99                         | GDK_LEAVE_NOTIFY_MASK;
100
101   attributes.visual = gtk_widget_get_visual (widget);
102   attributes.colormap = gtk_widget_get_colormap (widget);
103   attributes.wclass = GDK_INPUT_OUTPUT;
104
105   attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
106
107   widget->window = gdk_window_new (gtk_widget_get_parent_window (widget),
108                                    &attributes, attributes_mask);
109   gdk_window_set_user_data (widget->window, widget);
110
111   attributes.window_type = GDK_WINDOW_OFFSCREEN;
112   
113   /* Child 1 */
114   attributes.x = attributes.y = 0;
115   if (offscreen_box->child1 && GTK_WIDGET_VISIBLE (offscreen_box->child1))
116     {
117       attributes.width = offscreen_box->child1->allocation.width;
118       attributes.height = offscreen_box->child1->allocation.height;
119       start_y += offscreen_box->child1->allocation.height;
120     }
121   offscreen_box->offscreen_window1 = gdk_window_new (NULL,
122                                                      &attributes, attributes_mask);
123   gdk_window_set_user_data (offscreen_box->offscreen_window1, widget);
124   if (offscreen_box->child1)
125     gtk_widget_set_parent_window (offscreen_box->child1, offscreen_box->offscreen_window1);
126
127   /* Child 2 */
128   attributes.y = start_y;
129   child_requisition.width = child_requisition.height = 0;
130   if (offscreen_box->child2 && GTK_WIDGET_VISIBLE (offscreen_box->child2))
131     {
132       attributes.width = offscreen_box->child2->allocation.width;
133       attributes.height = offscreen_box->child2->allocation.height;
134     }
135   offscreen_box->offscreen_window2 = gdk_window_new (NULL,
136                                                      &attributes, attributes_mask);
137   gdk_window_set_user_data (offscreen_box->offscreen_window2, widget);
138   if (offscreen_box->child2)
139     gtk_widget_set_parent_window (offscreen_box->child2, offscreen_box->offscreen_window2);
140
141   widget->style = gtk_style_attach (widget->style, widget->window);
142
143   gtk_style_set_background (widget->style, widget->window, GTK_STATE_NORMAL);
144   gtk_style_set_background (widget->style, offscreen_box->offscreen_window1, GTK_STATE_NORMAL);
145   gtk_style_set_background (widget->style, offscreen_box->offscreen_window2, GTK_STATE_NORMAL);
146
147   gdk_window_show (offscreen_box->offscreen_window1);
148   gdk_window_show (offscreen_box->offscreen_window2);
149 }
150
151 static void
152 gtk_offscreen_box_unrealize (GtkWidget *widget)
153 {
154   GtkOffscreenBox *offscreen_box = GTK_OFFSCREEN_BOX (widget);
155
156   gdk_window_set_user_data (offscreen_box->offscreen_window1, NULL);
157   gdk_window_destroy (offscreen_box->offscreen_window1);
158   offscreen_box->offscreen_window1 = NULL;
159
160   gdk_window_set_user_data (offscreen_box->offscreen_window2, NULL);
161   gdk_window_destroy (offscreen_box->offscreen_window2);
162   offscreen_box->offscreen_window2 = NULL;
163
164   GTK_WIDGET_CLASS (gtk_offscreen_box_parent_class)->unrealize (widget);
165 }
166
167 static GType
168 gtk_offscreen_box_child_type (GtkContainer *container)
169 {
170   GtkOffscreenBox *offscreen_box = GTK_OFFSCREEN_BOX (container);
171
172   if (offscreen_box->child1 && offscreen_box->child2)
173     return G_TYPE_NONE;
174
175   return GTK_TYPE_WIDGET;
176 }
177
178 static void
179 gtk_offscreen_box_add (GtkContainer *container,
180                        GtkWidget    *widget)
181 {
182   GtkOffscreenBox *offscreen_box = GTK_OFFSCREEN_BOX (container);
183
184   if (!offscreen_box->child1)
185     gtk_offscreen_box_add1 (offscreen_box, widget);
186   else if (!offscreen_box->child2)
187     gtk_offscreen_box_add2 (offscreen_box, widget);
188   else
189     g_warning ("GtkOffscreenBox cannot have more than 2 children\n");
190 }
191
192 void
193 gtk_offscreen_box_add1 (GtkOffscreenBox *offscreen_box,
194                         GtkWidget       *child)
195 {
196   g_return_if_fail (GTK_IS_OFFSCREEN_BOX (offscreen_box));
197   g_return_if_fail (GTK_IS_WIDGET (child));
198
199   if (offscreen_box->child1 == NULL)
200     {
201       gtk_widget_set_parent_window (child, offscreen_box->offscreen_window1);
202       gtk_widget_set_parent (child, GTK_WIDGET (offscreen_box));
203       offscreen_box->child1 = child;
204     }
205 }
206
207 void
208 gtk_offscreen_box_add2 (GtkOffscreenBox  *offscreen_box,
209                         GtkWidget    *child)
210 {
211   g_return_if_fail (GTK_IS_OFFSCREEN_BOX (offscreen_box));
212   g_return_if_fail (GTK_IS_WIDGET (child));
213
214   if (offscreen_box->child2 == NULL)
215     {
216       gtk_widget_set_parent_window (child, offscreen_box->offscreen_window2);
217       gtk_widget_set_parent (child, GTK_WIDGET (offscreen_box));
218       offscreen_box->child2 = child;
219     }
220 }
221
222 static void
223 gtk_offscreen_box_remove (GtkContainer *container,
224                           GtkWidget    *widget)
225 {
226   GtkOffscreenBox *offscreen_box = GTK_OFFSCREEN_BOX (container);
227   gboolean was_visible;
228
229   was_visible = GTK_WIDGET_VISIBLE (widget);
230
231   if (offscreen_box->child1 == widget)
232     {
233       gtk_widget_unparent (widget);
234
235       offscreen_box->child1 = NULL;
236
237       if (was_visible && GTK_WIDGET_VISIBLE (container))
238         gtk_widget_queue_resize (GTK_WIDGET (container));
239     }
240   else if (offscreen_box->child2 == widget)
241     {
242       gtk_widget_unparent (widget);
243
244       offscreen_box->child2 = NULL;
245
246       if (was_visible && GTK_WIDGET_VISIBLE (container))
247         gtk_widget_queue_resize (GTK_WIDGET (container));
248     }
249 }
250
251 static void
252 gtk_offscreen_box_forall (GtkContainer *container,
253                           gboolean      include_internals,
254                           GtkCallback   callback,
255                           gpointer      callback_data)
256 {
257   GtkOffscreenBox *offscreen_box = GTK_OFFSCREEN_BOX (container);
258
259   g_return_if_fail (callback != NULL);
260
261   if (offscreen_box->child1)
262     (*callback) (offscreen_box->child1, callback_data);
263   if (offscreen_box->child2)
264     (*callback) (offscreen_box->child2, callback_data);
265 }
266
267 void
268 gtk_offscreen_box_set_angle (GtkOffscreenBox  *offscreen_box,
269                              gdouble           angle)
270 {
271   g_return_if_fail (GTK_IS_OFFSCREEN_BOX (offscreen_box));
272
273   offscreen_box->angle = angle;
274   gtk_widget_queue_draw (GTK_WIDGET (offscreen_box));
275
276   /* TODO: Really needs to resent pointer events if over the rotated window */
277 }
278
279
280 static void
281 gtk_offscreen_box_size_request (GtkWidget      *widget,
282                                 GtkRequisition *requisition)
283 {
284   GtkOffscreenBox *offscreen_box = GTK_OFFSCREEN_BOX (widget);
285   int w, h;
286
287   w = 0;
288   h = 0;
289
290   if (offscreen_box->child1 && GTK_WIDGET_VISIBLE (offscreen_box->child1))
291     {
292       GtkRequisition child_requisition;
293
294       gtk_widget_size_request (offscreen_box->child1, &child_requisition);
295
296       w = MAX (w, CHILD1_SIZE_SCALE * child_requisition.width);
297       h += CHILD1_SIZE_SCALE * child_requisition.height;
298     }
299
300   if (offscreen_box->child2 && GTK_WIDGET_VISIBLE (offscreen_box->child2))
301     {
302       GtkRequisition child_requisition;
303
304       gtk_widget_size_request (offscreen_box->child2, &child_requisition);
305
306       w = MAX (w, CHILD2_SIZE_SCALE * child_requisition.width);
307       h += CHILD2_SIZE_SCALE * child_requisition.height;
308     }
309
310   requisition->width = GTK_CONTAINER (widget)->border_width * 2 + w;
311   requisition->height = GTK_CONTAINER (widget)->border_width * 2 + h;
312 }
313
314 static void
315 gtk_offscreen_box_size_allocate (GtkWidget     *widget,
316                                  GtkAllocation *allocation)
317 {
318   GtkOffscreenBox *offscreen_box;
319   gint border_width;
320   gint start_y;
321
322   widget->allocation = *allocation;
323   offscreen_box = GTK_OFFSCREEN_BOX (widget);
324
325   border_width = GTK_CONTAINER (widget)->border_width;
326
327   if (GTK_WIDGET_REALIZED (widget))
328     gdk_window_move_resize (widget->window,
329                             allocation->x + border_width,
330                             allocation->y + border_width,
331                             allocation->width - border_width * 2,
332                             allocation->height - border_width * 2);
333
334   start_y = 0;
335
336   if (offscreen_box->child1 && GTK_WIDGET_VISIBLE (offscreen_box->child1))
337     {
338       GtkRequisition child_requisition;
339       GtkAllocation child_allocation;
340
341       gtk_widget_get_child_requisition (offscreen_box->child1, &child_requisition);
342       child_allocation.x = child_requisition.width * (CHILD1_SIZE_SCALE - 1.0) / 2;
343       child_allocation.y = start_y + child_requisition.height * (CHILD1_SIZE_SCALE - 1.0) / 2;
344       child_allocation.width = MAX (1, (gint) widget->allocation.width - 2 * border_width);
345       child_allocation.height = child_requisition.height;
346
347       start_y += CHILD1_SIZE_SCALE * child_requisition.height;
348
349       if (GTK_WIDGET_REALIZED (widget))
350         gdk_window_move_resize (offscreen_box->offscreen_window1,
351                                 child_allocation.x,
352                                 child_allocation.y,
353                                 child_allocation.width,
354                                 child_allocation.height);
355
356       child_allocation.x = child_allocation.y = 0;
357       gtk_widget_size_allocate (offscreen_box->child1, &child_allocation);
358     }
359
360   if (offscreen_box->child2 && GTK_WIDGET_VISIBLE (offscreen_box->child2))
361     {
362       GtkRequisition child_requisition;
363       GtkAllocation child_allocation;
364
365       gtk_widget_get_child_requisition (offscreen_box->child2, &child_requisition);
366       child_allocation.x = child_requisition.width * (CHILD2_SIZE_SCALE - 1.0) / 2;
367       child_allocation.y = start_y + child_requisition.height * (CHILD2_SIZE_SCALE - 1.0) / 2;
368       child_allocation.width = MAX (1, (gint) widget->allocation.width - 2 * border_width);
369       child_allocation.height = child_requisition.height;
370
371       start_y += CHILD2_SIZE_SCALE * child_requisition.height;
372
373       if (GTK_WIDGET_REALIZED (widget))
374         gdk_window_move_resize (offscreen_box->offscreen_window2,
375                                 child_allocation.x,
376                                 child_allocation.y,
377                                 child_allocation.width,
378                                 child_allocation.height);
379
380       child_allocation.x = child_allocation.y = 0;
381       gtk_widget_size_allocate (offscreen_box->child2, &child_allocation);
382     }
383 }
384
385 static gboolean
386 gtk_offscreen_box_damage (GtkWidget      *widget,
387                           GdkEventExpose *event)
388 {
389   gdk_window_invalidate_rect (widget->window, NULL, FALSE);
390
391   return TRUE;
392 }
393
394 static gboolean
395 gtk_offscreen_box_expose (GtkWidget      *widget,
396                           GdkEventExpose *event)
397 {
398   GtkOffscreenBox *offscreen_box = GTK_OFFSCREEN_BOX (widget);
399
400   if (GTK_WIDGET_DRAWABLE (widget))
401     {
402       if (event->window == widget->window)
403         {
404           GdkPixmap *pixmap;
405           GtkAllocation child_area;
406           cairo_t *cr;
407           int start_y = 0;
408
409           if (offscreen_box->child1 && GTK_WIDGET_VISIBLE (offscreen_box->child1))
410             {
411               pixmap = gdk_window_get_offscreen_pixmap (offscreen_box->offscreen_window1);
412               child_area = offscreen_box->child1->allocation;
413
414               cr = gdk_cairo_create (widget->window);
415
416               gdk_cairo_set_source_pixmap (cr, pixmap, 0, 0);
417               cairo_paint (cr);
418
419               cairo_destroy (cr);
420
421               start_y += child_area.height;
422             }
423
424           if (offscreen_box->child2 && GTK_WIDGET_VISIBLE (offscreen_box->child2))
425             {
426               gint w, h;
427
428               pixmap = gdk_window_get_offscreen_pixmap (offscreen_box->offscreen_window2);
429               child_area = offscreen_box->child2->allocation;
430
431               cr = gdk_cairo_create (widget->window);
432
433               /* transform */
434               cairo_translate (cr, 0, start_y);
435               cairo_translate (cr, child_area.width / 2, child_area.height / 2);
436               cairo_rotate (cr, offscreen_box->angle);
437               cairo_translate (cr, -child_area.width / 2, -child_area.height / 2);
438
439               /* clip */
440               gdk_drawable_get_size (pixmap, &w, &h);
441               cairo_rectangle (cr, 0, 0, w, h);
442               cairo_clip (cr);
443
444               /* paint */
445               gdk_cairo_set_source_pixmap (cr, pixmap, 0, 0);
446               cairo_paint (cr);
447
448               cairo_destroy (cr);
449             }
450         }
451       else if (event->window == offscreen_box->offscreen_window1)
452         {
453           gtk_paint_flat_box (widget->style, event->window,
454                               GTK_STATE_NORMAL, GTK_SHADOW_NONE,
455                               &event->area, widget, "blah",
456                               0, 0, -1, -1);
457
458           if (offscreen_box->child1)
459             gtk_container_propagate_expose (GTK_CONTAINER (widget),
460                                             offscreen_box->child1,
461                                             event);
462         }
463       else if (event->window == offscreen_box->offscreen_window2)
464         {
465           gtk_paint_flat_box (widget->style, event->window,
466                               GTK_STATE_NORMAL, GTK_SHADOW_NONE,
467                               &event->area, widget, "blah",
468                               0, 0, -1, -1);
469
470           if (offscreen_box->child2)
471             gtk_container_propagate_expose (GTK_CONTAINER (widget),
472                                             offscreen_box->child2,
473                                             event);
474         }
475     }
476
477   return FALSE;
478 }