1 /*
2 * GooCanvas. Copyright (C) 2005-6 Damon Chaplin.
3 * Released under the GNU LGPL license. See COPYING for details.
4 *
5 * goocanvaswidget.c - wrapper item for an embedded GtkWidget.
6 */
7
8 /**
9 * SECTION:goocanvaswidget
10 * @Title: GooCanvasWidget
11 * @Short_Description: an embedded widget item.
12 *
13 * GooCanvasWidget provides support for placing any GtkWidget in the canvas.
14 *
15 * The #GooCanvasWidget:width and #GooCanvasWidget:height properties specify
16 * the widget's size. If either of them is -1, then the requested size of the
17 * widget is used instead, which is the default for both width and height.
18 *
19 * Note that there are a number of limitations in the use of #GooCanvasWidget:
20 *
21 * <itemizedlist><listitem><para>
22 * It doesn't support any transformation besides simple translation.
23 * This means you can't scale a canvas with a #GooCanvasWidget in it.
24 * </para></listitem><listitem><para>
25 * It doesn't support layering, so you can't place other items beneath
26 * or above the #GooCanvasWidget.
27 * </para></listitem><listitem><para>
28 * It doesn't support rendering of widgets to a given cairo_t, which
29 * means you can't output the widget to a pdf or postscript file.
30 * </para></listitem><listitem><para>
31 * It doesn't have a model/view variant like the other standard items,
32 * so it can only be used in a simple canvas without a model.
33 * </para></listitem><listitem><para>
34 * It can't be made a static item.
35 * </para></listitem></itemizedlist>
36 */
37 #include <config.h>
38 #include <glib/gi18n-lib.h>
39 #include <gtk/gtk.h>
40 #include "goocanvas.h"
41 #include "goocanvasatk.h"
42
43 enum {
44 PROP_0,
45
46 PROP_WIDGET,
47 PROP_X,
48 PROP_Y,
49 PROP_WIDTH,
50 PROP_HEIGHT,
51 PROP_ANCHOR,
52 PROP_VISIBILITY
53 };
54
55
56 static void canvas_item_interface_init (GooCanvasItemIface *iface);
57 static void goo_canvas_widget_dispose (GObject *object);
58 static void goo_canvas_widget_get_property (GObject *object,
59 guint param_id,
60 GValue *value,
61 GParamSpec *pspec);
62 static void goo_canvas_widget_set_property (GObject *object,
63 guint param_id,
64 const GValue *value,
65 GParamSpec *pspec);
66
G_DEFINE_TYPE_WITH_CODE(GooCanvasWidget,goo_canvas_widget,GOO_TYPE_CANVAS_ITEM_SIMPLE,G_IMPLEMENT_INTERFACE (GOO_TYPE_CANVAS_ITEM,canvas_item_interface_init))67 G_DEFINE_TYPE_WITH_CODE (GooCanvasWidget, goo_canvas_widget,
68 GOO_TYPE_CANVAS_ITEM_SIMPLE,
69 G_IMPLEMENT_INTERFACE (GOO_TYPE_CANVAS_ITEM,
70 canvas_item_interface_init))
71
72
73 static void
74 goo_canvas_widget_init (GooCanvasWidget *witem)
75 {
76 /* By default we place the widget at the top-left of the canvas at its
77 requested size. */
78 witem->x = 0.0;
79 witem->y = 0.0;
80 witem->width = -1.0;
81 witem->height = -1.0;
82 witem->anchor = GOO_CANVAS_ANCHOR_NW;
83 }
84
85
86 /**
87 * goo_canvas_widget_new:
88 * @parent: (skip): the parent item, or %NULL. If a parent is specified, it will assume
89 * ownership of the item, and the item will automatically be freed when it is
90 * removed from the parent. Otherwise call g_object_unref() to free it.
91 * @widget: the widget.
92 * @x: the x coordinate of the item.
93 * @y: the y coordinate of the item.
94 * @width: the width of the item, or -1 to use the widget's requested width.
95 * @height: the height of the item, or -1 to use the widget's requested height.
96 * @...: optional pairs of property names and values, and a terminating %NULL.
97 *
98 * Creates a new widget item.
99 *
100 * Here's an example showing how to create an entry widget centered at (100.0,
101 * 100.0):
102 *
103 * <informalexample><programlisting>
104 * GtkWidget *entry = gtk_entry_new ();
105 * GooCanvasItem *witem = goo_canvas_widget_new (mygroup, entry,
106 * 100, 100, -1, -1,
107 * "anchor", GOO_CANVAS_ANCHOR_CENTER,
108 * NULL);
109 * </programlisting></informalexample>
110 *
111 * Returns: (transfer full): a new widget item.
112 **/
113 GooCanvasItem*
goo_canvas_widget_new(GooCanvasItem * parent,GtkWidget * widget,gdouble x,gdouble y,gdouble width,gdouble height,...)114 goo_canvas_widget_new (GooCanvasItem *parent,
115 GtkWidget *widget,
116 gdouble x,
117 gdouble y,
118 gdouble width,
119 gdouble height,
120 ...)
121 {
122 GooCanvasItem *item;
123 GooCanvasWidget *witem;
124 const char *first_property;
125 va_list var_args;
126
127 item = g_object_new (GOO_TYPE_CANVAS_WIDGET, NULL);
128 witem = (GooCanvasWidget*) item;
129
130 witem->widget = widget;
131 g_object_ref (witem->widget);
132 g_object_set_data (G_OBJECT (witem->widget), "goo-canvas-item", witem);
133
134 witem->x = x;
135 witem->y = y;
136 witem->width = width;
137 witem->height = height;
138
139 /* The widget defaults to being visible, like the canvas item, but this
140 can be overridden by the object property below. */
141 if (widget)
142 gtk_widget_show (widget);
143
144 va_start (var_args, height);
145 first_property = va_arg (var_args, char*);
146 if (first_property)
147 g_object_set_valist ((GObject*) item, first_property, var_args);
148 va_end (var_args);
149
150 if (parent)
151 {
152 goo_canvas_item_add_child (parent, item, -1);
153 g_object_unref (item);
154 }
155
156 return item;
157 }
158
159
160 /* Returns the anchor position, within the given width. */
161 static gdouble
goo_canvas_widget_anchor_horizontal_pos(GooCanvasAnchorType anchor,gdouble width)162 goo_canvas_widget_anchor_horizontal_pos (GooCanvasAnchorType anchor,
163 gdouble width)
164 {
165 switch(anchor)
166 {
167 case GOO_CANVAS_ANCHOR_N:
168 case GOO_CANVAS_ANCHOR_CENTER:
169 case GOO_CANVAS_ANCHOR_S:
170 return width / 2.0;
171 case GOO_CANVAS_ANCHOR_NE:
172 case GOO_CANVAS_ANCHOR_E:
173 case GOO_CANVAS_ANCHOR_SE:
174 return width;
175 default:
176 return 0.0;
177 }
178 }
179
180
181 /* Returns the anchor position, within the given height. */
182 static gdouble
goo_canvas_widget_anchor_vertical_pos(GooCanvasAnchorType anchor,gdouble height)183 goo_canvas_widget_anchor_vertical_pos (GooCanvasAnchorType anchor,
184 gdouble height)
185 {
186 switch (anchor)
187 {
188 case GOO_CANVAS_ANCHOR_W:
189 case GOO_CANVAS_ANCHOR_CENTER:
190 case GOO_CANVAS_ANCHOR_E:
191 return height / 2.0;
192 case GOO_CANVAS_ANCHOR_SW:
193 case GOO_CANVAS_ANCHOR_S:
194 case GOO_CANVAS_ANCHOR_SE:
195 return height;
196 default:
197 return 0.0;
198 }
199 }
200
201
202 /* Returns the size to use for the widget, either the item's width & height
203 properties or the widget's own requested width & height. */
204 static void
goo_canvas_widget_get_widget_size(GooCanvasWidget * witem,gdouble * width,gdouble * height)205 goo_canvas_widget_get_widget_size (GooCanvasWidget *witem,
206 gdouble *width,
207 gdouble *height)
208 {
209 GtkRequisition requisition;
210
211 if (witem->widget)
212 {
213 /* Get the widget's requested size, if we need it. */
214 if (witem->width < 0 || witem->height < 0)
215 gtk_widget_get_preferred_size (witem->widget, NULL, &requisition);
216
217 *width = witem->width < 0 ? requisition.width : witem->width;
218 *height = witem->height < 0 ? requisition.height : witem->height;
219 }
220 else
221 {
222 *width = *height = 0.0;
223 }
224 }
225
226
227 static void
goo_canvas_widget_set_widget(GooCanvasWidget * witem,GtkWidget * widget)228 goo_canvas_widget_set_widget (GooCanvasWidget *witem,
229 GtkWidget *widget)
230 {
231 GooCanvasItemSimple *simple = (GooCanvasItemSimple*) witem;
232
233 if (witem->widget)
234 {
235 g_object_set_data (G_OBJECT (witem->widget), "goo-canvas-item", NULL);
236 gtk_widget_unparent (witem->widget);
237 g_object_unref (witem->widget);
238 witem->widget = NULL;
239 }
240
241 if (widget)
242 {
243 witem->widget = widget;
244 g_object_ref (witem->widget);
245 g_object_set_data (G_OBJECT (witem->widget), "goo-canvas-item", witem);
246
247 if (simple->simple_data->visibility <= GOO_CANVAS_ITEM_INVISIBLE)
248 gtk_widget_hide (widget);
249 else
250 gtk_widget_show (widget);
251
252 if (simple->canvas)
253 {
254 if (gtk_widget_get_realized (GTK_WIDGET (simple->canvas)))
255 gtk_widget_set_parent_window (widget,
256 simple->canvas->canvas_window);
257
258 gtk_widget_set_parent (widget, GTK_WIDGET (simple->canvas));
259 }
260 }
261 }
262
263
264 static void
goo_canvas_widget_dispose(GObject * object)265 goo_canvas_widget_dispose (GObject *object)
266 {
267 GooCanvasItemSimple *simple = (GooCanvasItemSimple*) object;
268 GooCanvasWidget *witem = (GooCanvasWidget*) object;
269
270 if (simple->canvas)
271 goo_canvas_unregister_widget_item (simple->canvas, witem);
272
273 goo_canvas_widget_set_widget (witem, NULL);
274
275 G_OBJECT_CLASS (goo_canvas_widget_parent_class)->dispose (object);
276 }
277
278
279 static void
goo_canvas_widget_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)280 goo_canvas_widget_get_property (GObject *object,
281 guint prop_id,
282 GValue *value,
283 GParamSpec *pspec)
284 {
285 GooCanvasItemSimple *simple = (GooCanvasItemSimple*) object;
286 GooCanvasWidget *witem = (GooCanvasWidget*) object;
287
288 switch (prop_id)
289 {
290 case PROP_WIDGET:
291 g_value_set_object (value, witem->widget);
292 break;
293 case PROP_X:
294 g_value_set_double (value, witem->x);
295 break;
296 case PROP_Y:
297 g_value_set_double (value, witem->y);
298 break;
299 case PROP_WIDTH:
300 g_value_set_double (value, witem->width);
301 break;
302 case PROP_HEIGHT:
303 g_value_set_double (value, witem->height);
304 break;
305 case PROP_ANCHOR:
306 g_value_set_enum (value, witem->anchor);
307 break;
308 case PROP_VISIBILITY:
309 g_value_set_enum (value, simple->simple_data->visibility);
310 break;
311 default:
312 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
313 break;
314 }
315 }
316
317
318 static void
goo_canvas_widget_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)319 goo_canvas_widget_set_property (GObject *object,
320 guint prop_id,
321 const GValue *value,
322 GParamSpec *pspec)
323 {
324 GooCanvasItemSimple *simple = (GooCanvasItemSimple*) object;
325 GooCanvasWidget *witem = (GooCanvasWidget*) object;
326
327 switch (prop_id)
328 {
329 case PROP_WIDGET:
330 goo_canvas_widget_set_widget (witem, g_value_get_object (value));
331 break;
332 case PROP_X:
333 witem->x = g_value_get_double (value);
334 break;
335 case PROP_Y:
336 witem->y = g_value_get_double (value);
337 break;
338 case PROP_WIDTH:
339 witem->width = g_value_get_double (value);
340 break;
341 case PROP_HEIGHT:
342 witem->height = g_value_get_double (value);
343 break;
344 case PROP_ANCHOR:
345 witem->anchor = g_value_get_enum (value);
346 break;
347 case PROP_VISIBILITY:
348 simple->simple_data->visibility = g_value_get_enum (value);
349 if (simple->simple_data->visibility <= GOO_CANVAS_ITEM_INVISIBLE)
350 gtk_widget_hide (witem->widget);
351 else
352 gtk_widget_show (witem->widget);
353 break;
354 default:
355 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
356 break;
357 }
358
359 goo_canvas_item_simple_changed (simple, TRUE);
360 }
361
362
363 static void
goo_canvas_widget_set_canvas(GooCanvasItem * item,GooCanvas * canvas)364 goo_canvas_widget_set_canvas (GooCanvasItem *item,
365 GooCanvas *canvas)
366 {
367 GooCanvasItemSimple *simple = (GooCanvasItemSimple*) item;
368 GooCanvasWidget *witem = (GooCanvasWidget*) item;
369
370 if (simple->canvas != canvas)
371 {
372 if (simple->canvas)
373 goo_canvas_unregister_widget_item (simple->canvas, witem);
374
375 simple->canvas = canvas;
376
377 if (simple->canvas)
378 {
379 goo_canvas_register_widget_item (simple->canvas, witem);
380
381 if (witem->widget)
382 {
383 if (gtk_widget_get_realized (GTK_WIDGET (simple->canvas)))
384 gtk_widget_set_parent_window (witem->widget,
385 simple->canvas->canvas_window);
386
387 gtk_widget_set_parent (witem->widget,
388 GTK_WIDGET (simple->canvas));
389 }
390 }
391 else
392 {
393 if (witem->widget)
394 gtk_widget_unparent (witem->widget);
395 }
396 }
397 }
398
399
400 static void
goo_canvas_widget_set_parent(GooCanvasItem * item,GooCanvasItem * parent)401 goo_canvas_widget_set_parent (GooCanvasItem *item,
402 GooCanvasItem *parent)
403 {
404 GooCanvasItemSimple *simple = (GooCanvasItemSimple*) item;
405 GooCanvas *canvas;
406
407 simple->parent = parent;
408 simple->need_update = TRUE;
409 simple->need_entire_subtree_update = TRUE;
410
411 canvas = parent ? goo_canvas_item_get_canvas (parent) : NULL;
412 goo_canvas_widget_set_canvas (item, canvas);
413 }
414
415
416 static void
goo_canvas_widget_update(GooCanvasItemSimple * simple,cairo_t * cr)417 goo_canvas_widget_update (GooCanvasItemSimple *simple,
418 cairo_t *cr)
419 {
420 GooCanvasWidget *witem = (GooCanvasWidget*) simple;
421 gdouble width, height;
422
423 if (witem->widget)
424 {
425 goo_canvas_widget_get_widget_size (witem, &width, &height);
426
427 simple->bounds.x1 = witem->x;
428 simple->bounds.y1 = witem->y;
429
430 simple->bounds.x1 -=
431 goo_canvas_widget_anchor_horizontal_pos (witem->anchor, width);
432 simple->bounds.y1 -=
433 goo_canvas_widget_anchor_vertical_pos (witem->anchor, height);
434
435 simple->bounds.x2 = simple->bounds.x1 + width;
436 simple->bounds.y2 = simple->bounds.y1 + height;
437
438 /* Queue a resize of the widget so it gets moved. Note that the widget
439 is moved by goo_canvas_size_allocate(). */
440 gtk_widget_queue_resize (witem->widget);
441 }
442 else
443 {
444 simple->bounds.x1 = simple->bounds.y1 = 0.0;
445 simple->bounds.x2 = simple->bounds.y2 = 0.0;
446 }
447 }
448
449
450 static void
goo_canvas_widget_allocate_area(GooCanvasItem * item,cairo_t * cr,const GooCanvasBounds * requested_area,const GooCanvasBounds * allocated_area,gdouble x_offset,gdouble y_offset)451 goo_canvas_widget_allocate_area (GooCanvasItem *item,
452 cairo_t *cr,
453 const GooCanvasBounds *requested_area,
454 const GooCanvasBounds *allocated_area,
455 gdouble x_offset,
456 gdouble y_offset)
457 {
458 GooCanvasItemSimple *simple = (GooCanvasItemSimple*) item;
459 GooCanvasWidget *witem = (GooCanvasWidget*) item;
460 gdouble requested_width, requested_height, allocated_width, allocated_height;
461 gdouble width_proportion, height_proportion;
462 gdouble width, height;
463
464 width = simple->bounds.x2 - simple->bounds.x1;
465 height = simple->bounds.y2 - simple->bounds.y1;
466
467 simple->bounds.x1 += x_offset;
468 simple->bounds.y1 += y_offset;
469
470 requested_width = requested_area->x2 - requested_area->x1;
471 requested_height = requested_area->y2 - requested_area->y1;
472 allocated_width = allocated_area->x2 - allocated_area->x1;
473 allocated_height = allocated_area->y2 - allocated_area->y1;
474
475 width_proportion = allocated_width / requested_width;
476 height_proportion = allocated_height / requested_height;
477
478 width *= width_proportion;
479 height *= height_proportion;
480
481 simple->bounds.x2 = simple->bounds.x1 + width;
482 simple->bounds.y2 = simple->bounds.y1 + height;
483
484 /* Queue a resize of the widget so it gets moved. Note that the widget
485 is moved by goo_canvas_size_allocate(). */
486 gtk_widget_queue_resize (witem->widget);
487 }
488
489
490 static void
goo_canvas_widget_paint(GooCanvasItemSimple * simple,cairo_t * cr,const GooCanvasBounds * bounds)491 goo_canvas_widget_paint (GooCanvasItemSimple *simple,
492 cairo_t *cr,
493 const GooCanvasBounds *bounds)
494 {
495 /* Do nothing for now. Maybe render for printing in future. */
496 }
497
498
499 static gboolean
goo_canvas_widget_is_item_at(GooCanvasItemSimple * simple,gdouble x,gdouble y,cairo_t * cr,gboolean is_pointer_event)500 goo_canvas_widget_is_item_at (GooCanvasItemSimple *simple,
501 gdouble x,
502 gdouble y,
503 cairo_t *cr,
504 gboolean is_pointer_event)
505 {
506 /* For now we just assume that the widget covers its entire bounds so we just
507 return TRUE. In future if widget items support transforms we'll need to
508 modify this. */
509 return TRUE;
510 }
511
512
513 static void
canvas_item_interface_init(GooCanvasItemIface * iface)514 canvas_item_interface_init (GooCanvasItemIface *iface)
515 {
516 iface->set_canvas = goo_canvas_widget_set_canvas;
517 iface->set_parent = goo_canvas_widget_set_parent;
518 iface->allocate_area = goo_canvas_widget_allocate_area;
519 }
520
521
522 static void
goo_canvas_widget_class_init(GooCanvasWidgetClass * klass)523 goo_canvas_widget_class_init (GooCanvasWidgetClass *klass)
524 {
525 GObjectClass *gobject_class = (GObjectClass*) klass;
526 GooCanvasItemSimpleClass *simple_class = (GooCanvasItemSimpleClass*) klass;
527
528 gobject_class->dispose = goo_canvas_widget_dispose;
529
530 gobject_class->get_property = goo_canvas_widget_get_property;
531 gobject_class->set_property = goo_canvas_widget_set_property;
532
533 simple_class->simple_update = goo_canvas_widget_update;
534 simple_class->simple_paint = goo_canvas_widget_paint;
535 simple_class->simple_is_item_at = goo_canvas_widget_is_item_at;
536
537 /* Register our accessible factory, but only if accessibility is enabled. */
538 if (!ATK_IS_NO_OP_OBJECT_FACTORY (atk_registry_get_factory (atk_get_default_registry (), GTK_TYPE_WIDGET)))
539 {
540 atk_registry_set_factory_type (atk_get_default_registry (),
541 GOO_TYPE_CANVAS_WIDGET,
542 goo_canvas_widget_accessible_factory_get_type ());
543 }
544
545 g_object_class_install_property (gobject_class, PROP_WIDGET,
546 g_param_spec_object ("widget",
547 _("Widget"),
548 _("The widget to place in the canvas"),
549 GTK_TYPE_WIDGET,
550 G_PARAM_READWRITE));
551
552 g_object_class_install_property (gobject_class, PROP_X,
553 g_param_spec_double ("x",
554 "X",
555 _("The x coordinate of the widget"),
556 -G_MAXDOUBLE,
557 G_MAXDOUBLE, 0.0,
558 G_PARAM_READWRITE));
559
560 g_object_class_install_property (gobject_class, PROP_Y,
561 g_param_spec_double ("y",
562 "Y",
563 _("The y coordinate of the widget"),
564 -G_MAXDOUBLE,
565 G_MAXDOUBLE, 0.0,
566 G_PARAM_READWRITE));
567
568 g_object_class_install_property (gobject_class, PROP_WIDTH,
569 g_param_spec_double ("width",
570 _("Width"),
571 _("The width of the widget, or -1 to use its requested width"),
572 -G_MAXDOUBLE,
573 G_MAXDOUBLE, -1.0,
574 G_PARAM_READWRITE));
575
576 g_object_class_install_property (gobject_class, PROP_HEIGHT,
577 g_param_spec_double ("height",
578 _("Height"),
579 _("The height of the widget, or -1 to use its requested height"),
580 -G_MAXDOUBLE,
581 G_MAXDOUBLE, -1.0,
582 G_PARAM_READWRITE));
583
584
585 g_object_class_install_property (gobject_class, PROP_ANCHOR,
586 g_param_spec_enum ("anchor",
587 _("Anchor"),
588 _("How to position the widget relative to the item's x and y coordinate settings"),
589 GOO_TYPE_CANVAS_ANCHOR_TYPE,
590 GOO_CANVAS_ANCHOR_NW,
591 G_PARAM_READWRITE));
592
593 g_object_class_override_property (gobject_class, PROP_VISIBILITY,
594 "visibility");
595 }
596