1 /*
2 * Copyright (C) 2010 David King <davidk@openismus.com>
3 * Copyright (C) 2010 - 2012 Vivien Malerba <malerba@gnome-db.org>
4 * Copyright (C) 2011 Murray Cumming <murrayc@murrayc.com>
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the
18 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
19 * Boston, MA 02110-1301, USA.
20 */
21 #include "widget-embedder.h"
22 #include <gdaui-decl.h>
23
24 static void widget_embedder_realize (GtkWidget *widget);
25 static void widget_embedder_unrealize (GtkWidget *widget);
26 static void widget_embedder_get_preferred_width (GtkWidget *widget,
27 gint *minimum,
28 gint *natural);
29 static void widget_embedder_get_preferred_height (GtkWidget *widget,
30 gint *minimum,
31 gint *natural);
32 static void widget_embedder_size_allocate (GtkWidget *widget,
33 GtkAllocation *allocation);
34 static gboolean widget_embedder_damage (GtkWidget *widget,
35 GdkEventExpose *event);
36 static gboolean widget_embedder_draw (GtkWidget *widget,
37 cairo_t *cr);
38 static void widget_embedder_add (GtkContainer *container,
39 GtkWidget *child);
40 static void widget_embedder_remove (GtkContainer *container,
41 GtkWidget *widget);
42 static void widget_embedder_forall (GtkContainer *container,
43 gboolean include_internals,
44 GtkCallback callback,
45 gpointer callback_data);
46 static GType widget_embedder_child_type (GtkContainer *container);
47
48 G_DEFINE_TYPE (WidgetEmbedder, widget_embedder, GTK_TYPE_CONTAINER);
49
50 static void
to_child(G_GNUC_UNUSED WidgetEmbedder * bin,double widget_x,double widget_y,double * x_out,double * y_out)51 to_child (G_GNUC_UNUSED WidgetEmbedder *bin,
52 double widget_x,
53 double widget_y,
54 double *x_out,
55 double *y_out)
56 {
57 *x_out = widget_x;
58 *y_out = widget_y;
59 }
60
61 static void
to_parent(G_GNUC_UNUSED WidgetEmbedder * bin,double offscreen_x,double offscreen_y,double * x_out,double * y_out)62 to_parent (G_GNUC_UNUSED WidgetEmbedder *bin,
63 double offscreen_x,
64 double offscreen_y,
65 double *x_out,
66 double *y_out)
67 {
68 *x_out = offscreen_x;
69 *y_out = offscreen_y;
70 }
71
72 static void
widget_embedder_class_init(WidgetEmbedderClass * klass)73 widget_embedder_class_init (WidgetEmbedderClass *klass)
74 {
75 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
76 GtkContainerClass *container_class = GTK_CONTAINER_CLASS (klass);
77
78 widget_class->realize = widget_embedder_realize;
79 widget_class->unrealize = widget_embedder_unrealize;
80 widget_class->get_preferred_width = widget_embedder_get_preferred_width;
81 widget_class->get_preferred_height = widget_embedder_get_preferred_height;
82 widget_class->size_allocate = widget_embedder_size_allocate;
83 widget_class->draw = widget_embedder_draw;
84
85 g_signal_override_class_closure (g_signal_lookup ("damage-event", GTK_TYPE_WIDGET),
86 WIDGET_EMBEDDER_TYPE,
87 g_cclosure_new (G_CALLBACK (widget_embedder_damage),
88 NULL, NULL));
89
90 container_class->add = widget_embedder_add;
91 container_class->remove = widget_embedder_remove;
92 container_class->forall = widget_embedder_forall;
93 container_class->child_type = widget_embedder_child_type;
94 }
95
96 static void
widget_embedder_init(WidgetEmbedder * bin)97 widget_embedder_init (WidgetEmbedder *bin)
98 {
99 gtk_widget_set_has_window (GTK_WIDGET (bin), TRUE);
100 bin->valid = TRUE;
101 bin->red = -1.;
102 bin->green = -1.;
103 bin->blue = -1.;
104 bin->alpha = -1.;
105 }
106
107 GtkWidget *
widget_embedder_new(void)108 widget_embedder_new (void)
109 {
110 return g_object_new (WIDGET_EMBEDDER_TYPE, NULL);
111 }
112
113 static GdkWindow *
pick_offscreen_child(G_GNUC_UNUSED GdkWindow * offscreen_window,double widget_x,double widget_y,WidgetEmbedder * bin)114 pick_offscreen_child (G_GNUC_UNUSED GdkWindow *offscreen_window,
115 double widget_x,
116 double widget_y,
117 WidgetEmbedder *bin)
118 {
119 GtkAllocation child_area;
120 double x, y;
121
122 if (bin->child && gtk_widget_get_visible (bin->child)) {
123 to_child (bin, widget_x, widget_y, &x, &y);
124
125 gtk_widget_get_allocation ((GtkWidget*) bin, &child_area);
126 if (x >= 0 && x < child_area.width &&
127 y >= 0 && y < child_area.height)
128 return bin->offscreen_window;
129 }
130
131 return NULL;
132 }
133
134 static void
offscreen_window_to_parent(G_GNUC_UNUSED GdkWindow * offscreen_window,double offscreen_x,double offscreen_y,double * parent_x,double * parent_y,WidgetEmbedder * bin)135 offscreen_window_to_parent (G_GNUC_UNUSED GdkWindow *offscreen_window,
136 double offscreen_x,
137 double offscreen_y,
138 double *parent_x,
139 double *parent_y,
140 WidgetEmbedder *bin)
141 {
142 to_parent (bin, offscreen_x, offscreen_y, parent_x, parent_y);
143 }
144
145 static void
offscreen_window_from_parent(G_GNUC_UNUSED GdkWindow * window,double parent_x,double parent_y,double * offscreen_x,double * offscreen_y,WidgetEmbedder * bin)146 offscreen_window_from_parent (G_GNUC_UNUSED GdkWindow *window,
147 double parent_x,
148 double parent_y,
149 double *offscreen_x,
150 double *offscreen_y,
151 WidgetEmbedder *bin)
152 {
153 to_child (bin, parent_x, parent_y, offscreen_x, offscreen_y);
154 }
155
156 static void
widget_embedder_realize(GtkWidget * widget)157 widget_embedder_realize (GtkWidget *widget)
158 {
159 WidgetEmbedder *bin = WIDGET_EMBEDDER (widget);
160 GdkWindowAttr attributes;
161 gint attributes_mask;
162 gint border_width;
163 GtkRequisition child_requisition;
164 GtkAllocation allocation;
165
166 gtk_widget_set_realized (widget, TRUE);
167
168 border_width = gtk_container_get_border_width (GTK_CONTAINER (widget));
169
170 gtk_widget_get_allocation (widget, &allocation);
171 attributes.x = allocation.x + border_width;
172 attributes.y = allocation.y + border_width;
173 attributes.width = allocation.width - 2 * border_width;
174 attributes.height = allocation.height - 2 * border_width;
175 attributes.window_type = GDK_WINDOW_CHILD;
176 attributes.event_mask = gtk_widget_get_events (widget)
177 | GDK_EXPOSURE_MASK
178 | GDK_POINTER_MOTION_MASK
179 | GDK_BUTTON_PRESS_MASK
180 | GDK_BUTTON_RELEASE_MASK
181 | GDK_SCROLL_MASK
182 | GDK_ENTER_NOTIFY_MASK
183 | GDK_LEAVE_NOTIFY_MASK;
184
185 attributes.visual = gtk_widget_get_visual (widget);
186 attributes.wclass = GDK_INPUT_OUTPUT;
187
188 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL;
189
190 GdkWindow *win;
191 win = gdk_window_new (gtk_widget_get_parent_window (widget),
192 &attributes, attributes_mask);
193 gtk_widget_set_window (widget, win);
194 gdk_window_set_user_data (win, widget);
195 g_signal_connect (win, "pick-embedded-child",
196 G_CALLBACK (pick_offscreen_child), bin);
197
198 attributes.window_type = GDK_WINDOW_OFFSCREEN;
199
200 child_requisition.width = child_requisition.height = 0;
201 if (bin->child && gtk_widget_get_visible (bin->child)) {
202 GtkAllocation allocation;
203 gtk_widget_get_allocation (bin->child, &allocation);
204 attributes.width = allocation.width;
205 attributes.height = allocation.height;
206 }
207 bin->offscreen_window = gdk_window_new (gtk_widget_get_root_window (widget),
208 &attributes, attributes_mask);
209 gdk_window_set_user_data (bin->offscreen_window, widget);
210 if (bin->child)
211 gtk_widget_set_parent_window (bin->child, bin->offscreen_window);
212 gdk_offscreen_window_set_embedder (bin->offscreen_window, win);
213 g_signal_connect (bin->offscreen_window, "to-embedder",
214 G_CALLBACK (offscreen_window_to_parent), bin);
215 g_signal_connect (bin->offscreen_window, "from-embedder",
216 G_CALLBACK (offscreen_window_from_parent), bin);
217
218 GtkStyleContext *style;
219 style = gtk_widget_get_style_context (widget);
220
221 gtk_style_context_set_background (style, win);
222 gtk_style_context_set_background (style, bin->offscreen_window);
223 gdk_window_show (bin->offscreen_window);
224 }
225
226 static void
widget_embedder_unrealize(GtkWidget * widget)227 widget_embedder_unrealize (GtkWidget *widget)
228 {
229 WidgetEmbedder *bin = WIDGET_EMBEDDER (widget);
230
231 gdk_window_set_user_data (bin->offscreen_window, NULL);
232 gdk_window_destroy (bin->offscreen_window);
233 bin->offscreen_window = NULL;
234
235 GTK_WIDGET_CLASS (widget_embedder_parent_class)->unrealize (widget);
236 }
237
238 static GType
widget_embedder_child_type(GtkContainer * container)239 widget_embedder_child_type (GtkContainer *container)
240 {
241 WidgetEmbedder *bin = WIDGET_EMBEDDER (container);
242
243 if (bin->child)
244 return G_TYPE_NONE;
245
246 return GTK_TYPE_WIDGET;
247 }
248
249 static void
widget_embedder_add(GtkContainer * container,GtkWidget * widget)250 widget_embedder_add (GtkContainer *container,
251 GtkWidget *widget)
252 {
253 WidgetEmbedder *bin = WIDGET_EMBEDDER (container);
254
255 if (!bin->child) {
256 gtk_widget_set_parent_window (widget, bin->offscreen_window);
257 gtk_widget_set_parent (widget, GTK_WIDGET (bin));
258 bin->child = widget;
259 }
260 else
261 g_warning ("WidgetEmbedder cannot have more than one child\n");
262 }
263
264 static void
widget_embedder_remove(GtkContainer * container,GtkWidget * widget)265 widget_embedder_remove (GtkContainer *container,
266 GtkWidget *widget)
267 {
268 WidgetEmbedder *bin = WIDGET_EMBEDDER (container);
269 gboolean was_visible;
270
271 was_visible = gtk_widget_get_visible (widget);
272
273 if (bin->child == widget) {
274 gtk_widget_unparent (widget);
275
276 bin->child = NULL;
277
278 if (was_visible && gtk_widget_get_visible (GTK_WIDGET (container)))
279 gtk_widget_queue_resize (GTK_WIDGET (container));
280 }
281 }
282
283 static void
widget_embedder_forall(GtkContainer * container,G_GNUC_UNUSED gboolean include_internals,GtkCallback callback,gpointer callback_data)284 widget_embedder_forall (GtkContainer *container,
285 G_GNUC_UNUSED gboolean include_internals,
286 GtkCallback callback,
287 gpointer callback_data)
288 {
289 WidgetEmbedder *bin = WIDGET_EMBEDDER (container);
290
291 g_return_if_fail (callback != NULL);
292
293 if (bin->child)
294 (*callback) (bin->child, callback_data);
295 }
296
297 static void
widget_embedder_size_request(GtkWidget * widget,GtkRequisition * requisition)298 widget_embedder_size_request (GtkWidget *widget,
299 GtkRequisition *requisition)
300 {
301 WidgetEmbedder *bin = WIDGET_EMBEDDER (widget);
302 GtkRequisition child_requisition;
303
304 child_requisition.width = 0;
305 child_requisition.height = 0;
306
307 if (bin->child && gtk_widget_get_visible (bin->child))
308 gtk_widget_get_preferred_size (bin->child, &child_requisition, NULL);
309
310 guint border_width;
311 border_width = gtk_container_get_border_width (GTK_CONTAINER (widget));
312 requisition->width = border_width * 2 + child_requisition.width;
313 requisition->height = border_width * 2 + child_requisition.height;
314 }
315
316 static void
widget_embedder_get_preferred_width(GtkWidget * widget,gint * minimum,gint * natural)317 widget_embedder_get_preferred_width (GtkWidget *widget,
318 gint *minimum,
319 gint *natural)
320 {
321 GtkRequisition requisition;
322 widget_embedder_size_request (widget, &requisition);
323 *minimum = *natural = requisition.width;
324 }
325
326 static void
widget_embedder_get_preferred_height(GtkWidget * widget,gint * minimum,gint * natural)327 widget_embedder_get_preferred_height (GtkWidget *widget,
328 gint *minimum,
329 gint *natural)
330 {
331 GtkRequisition requisition;
332 widget_embedder_size_request (widget, &requisition);
333 *minimum = *natural = requisition.height;
334 }
335
336 static void
widget_embedder_size_allocate(GtkWidget * widget,GtkAllocation * allocation)337 widget_embedder_size_allocate (GtkWidget *widget,
338 GtkAllocation *allocation)
339 {
340 WidgetEmbedder *bin = WIDGET_EMBEDDER (widget);
341 gint border_width;
342 gint w, h;
343
344 gtk_widget_set_allocation (widget, allocation);
345
346 border_width = gtk_container_get_border_width (GTK_CONTAINER (widget));
347
348 w = allocation->width - border_width * 2;
349 h = allocation->height - border_width * 2;
350
351 if (gtk_widget_get_realized (widget)) {
352 GdkWindow *win;
353 win = gtk_widget_get_window (widget);
354 gdk_window_move_resize (win,
355 allocation->x + border_width,
356 allocation->y + border_width,
357 w, h);
358 }
359
360 if (bin->child && gtk_widget_get_visible (bin->child)){
361 GtkAllocation child_allocation;
362
363 child_allocation.x = 0;
364 child_allocation.y = 0;
365 child_allocation.height = h;
366 child_allocation.width = w;
367
368 if (gtk_widget_get_realized (widget))
369 gdk_window_move_resize (bin->offscreen_window,
370 child_allocation.x,
371 child_allocation.y,
372 child_allocation.width,
373 child_allocation.height);
374
375 child_allocation.x = child_allocation.y = 0;
376 gtk_widget_size_allocate (bin->child, &child_allocation);
377 }
378 }
379
380 static gboolean
widget_embedder_damage(GtkWidget * widget,G_GNUC_UNUSED GdkEventExpose * event)381 widget_embedder_damage (GtkWidget *widget,
382 G_GNUC_UNUSED GdkEventExpose *event)
383 {
384 gdk_window_invalidate_rect (gtk_widget_get_window (widget), NULL, FALSE);
385
386 return TRUE;
387 }
388
389 void
widget_embedder_set_ucolor(WidgetEmbedder * bin,gdouble red,gdouble green,gdouble blue,gdouble alpha)390 widget_embedder_set_ucolor (WidgetEmbedder *bin, gdouble red, gdouble green,
391 gdouble blue, gdouble alpha)
392 {
393 bin->red = red;
394 bin->green = green;
395 bin->blue = blue;
396 bin->alpha = alpha;
397 gtk_widget_queue_draw (GTK_WIDGET (bin));
398 }
399
400
401 static gboolean
widget_embedder_draw(GtkWidget * widget,cairo_t * cr)402 widget_embedder_draw (GtkWidget *widget, cairo_t *cr)
403 {
404 WidgetEmbedder *bin = WIDGET_EMBEDDER (widget);
405 #define MARGIN 1.5
406 GdkWindow *window;
407
408 window = gtk_widget_get_window (widget);
409 if (gtk_cairo_should_draw_window (cr, window)) {
410 cairo_surface_t *surface;
411 GtkAllocation child_area;
412
413 if (bin->child && gtk_widget_get_visible (bin->child)) {
414 surface = gdk_offscreen_window_get_surface (bin->offscreen_window);
415 gtk_widget_get_allocation (bin->child, &child_area);
416 cairo_set_source_surface (cr, surface, 0, 0);
417 cairo_paint (cr);
418
419 if (! bin->valid) {
420 if ((bin->red >= 0.) && (bin->red <= 1.) &&
421 (bin->green >= 0.) && (bin->green <= 1.) &&
422 (bin->blue >= 0.) && (bin->blue <= 1.) &&
423 (bin->alpha >= 0.) && (bin->alpha <= 1.))
424 cairo_set_source_rgba (cr, bin->red, bin->green,
425 bin->blue, bin->alpha);
426 else
427 cairo_set_source_rgba (cr, GDAUI_COLOR_UNKNOWN_MASK);
428 cairo_rectangle (cr, child_area.x + MARGIN, child_area.y + MARGIN,
429 child_area.width - 2. * MARGIN,
430 child_area.height - 2. * MARGIN);
431 cairo_fill (cr);
432 }
433 }
434 }
435
436 if (gtk_cairo_should_draw_window (cr, bin->offscreen_window)) {
437 if (bin->child)
438 gtk_container_propagate_draw (GTK_CONTAINER (widget),
439 bin->child,
440 cr);
441 }
442
443 return FALSE;
444 }
445
446 /**
447 * widget_embedder_set_valid
448 * @bin: a #WidgetEmbedder
449 * @valid: set to %TRUE for a valid entry
450 *
451 * Changes the validity aspect of @bin
452 */
453 void
widget_embedder_set_valid(WidgetEmbedder * bin,gboolean valid)454 widget_embedder_set_valid (WidgetEmbedder *bin, gboolean valid)
455 {
456 bin->valid = valid;
457 gtk_widget_queue_draw (GTK_WIDGET (bin));
458 }
459