/* * GooCanvas. Copyright (C) 2005-6 Damon Chaplin. * Released under the GNU LGPL license. See COPYING for details. * * goocanvaswidget.c - wrapper item for an embedded GtkWidget. */ /** * SECTION:goocanvaswidget * @Title: GooCanvasWidget * @Short_Description: an embedded widget item. * * GooCanvasWidget provides support for placing any GtkWidget in the canvas. * * Note that there are a number of limitations in the use of #GooCanvasWidget: * * * It doesn't support any transformation besides simple translation. * This means you can't scale a canvas with a #GooCanvasWidget in it. * * It doesn't support layering, so you can't place other items beneath * or above the #GooCanvasWidget. * * It doesn't support rendering of widgets to a given cairo_t, which * means you can't output the widget to a pdf or postscript file. * * It doesn't have a model/view variant like the other standard items, * so it can only be used in a simple canvas without a model. * */ #include #include #include #include "goocanvas.h" #include "goocanvasatk.h" enum { PROP_0, PROP_WIDGET, PROP_X, PROP_Y, PROP_WIDTH, PROP_HEIGHT, PROP_ANCHOR, PROP_VISIBILITY }; static void canvas_item_interface_init (GooCanvasItemIface *iface); static void goo_canvas_widget_dispose (GObject *object); static void goo_canvas_widget_get_property (GObject *object, guint param_id, GValue *value, GParamSpec *pspec); static void goo_canvas_widget_set_property (GObject *object, guint param_id, const GValue *value, GParamSpec *pspec); 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)) static void goo_canvas_widget_init (GooCanvasWidget *witem) { /* By default we place the widget at the top-left of the canvas at its requested size. */ witem->x = 0.0; witem->y = 0.0; witem->width = -1.0; witem->height = -1.0; witem->anchor = GTK_ANCHOR_NW; } /** * goo_canvas_widget_new: * @parent: the parent item, or %NULL. If a parent is specified, it will assume * ownership of the item, and the item will automatically be freed when it is * removed from the parent. Otherwise call g_object_unref() to free it. * @widget: the widget. * @x: the x coordinate of the item. * @y: the y coordinate of the item. * @width: the width of the item, or -1 to use the widget's requested width. * @height: the height of the item, or -1 to use the widget's requested height. * @...: optional pairs of property names and values, and a terminating %NULL. * * Creates a new widget item. * * * * Here's an example showing how to create an entry widget centered at (100.0, * 100.0): * * * GtkWidget *entry = gtk_entry_new (); * GooCanvasItem *witem = goo_canvas_widget_new (mygroup, entry, * 100, 100, -1, -1, * "anchor", GTK_ANCHOR_CENTER, * NULL); * * * Returns: a new widget item. **/ GooCanvasItem* goo_canvas_widget_new (GooCanvasItem *parent, GtkWidget *widget, gdouble x, gdouble y, gdouble width, gdouble height, ...) { GooCanvasItem *item; GooCanvasWidget *witem; const char *first_property; va_list var_args; item = g_object_new (GOO_TYPE_CANVAS_WIDGET, NULL); witem = (GooCanvasWidget*) item; witem->widget = widget; g_object_ref (witem->widget); g_object_set_data (G_OBJECT (witem->widget), "goo-canvas-item", witem); witem->x = x; witem->y = y; witem->width = width; witem->height = height; /* The widget defaults to being visible, like the canvas item, but this can be overridden by the object property below. */ if (widget) gtk_widget_show (widget); va_start (var_args, height); first_property = va_arg (var_args, char*); if (first_property) g_object_set_valist ((GObject*) item, first_property, var_args); va_end (var_args); if (parent) { goo_canvas_item_add_child (parent, item, -1); g_object_unref (item); } return item; } static void goo_canvas_widget_set_widget (GooCanvasWidget *witem, GtkWidget *widget) { GooCanvasItemSimple *simple = (GooCanvasItemSimple*) witem; if (witem->widget) { g_object_set_data (G_OBJECT (witem->widget), "goo-canvas-item", NULL); gtk_widget_unparent (witem->widget); g_object_unref (witem->widget); witem->widget = NULL; } if (widget) { witem->widget = widget; g_object_ref (witem->widget); g_object_set_data (G_OBJECT (witem->widget), "goo-canvas-item", witem); if (simple->simple_data->visibility <= GOO_CANVAS_ITEM_INVISIBLE) gtk_widget_hide (widget); else gtk_widget_show (widget); if (simple->canvas) { if (GTK_WIDGET_REALIZED (simple->canvas)) gtk_widget_set_parent_window (widget, simple->canvas->canvas_window); gtk_widget_set_parent (widget, GTK_WIDGET (simple->canvas)); } } } static void goo_canvas_widget_dispose (GObject *object) { GooCanvasItemSimple *simple = (GooCanvasItemSimple*) object; GooCanvasWidget *witem = (GooCanvasWidget*) object; if (simple->canvas) goo_canvas_unregister_widget_item (simple->canvas, witem); goo_canvas_widget_set_widget (witem, NULL); G_OBJECT_CLASS (goo_canvas_widget_parent_class)->dispose (object); } static void goo_canvas_widget_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { GooCanvasItemSimple *simple = (GooCanvasItemSimple*) object; GooCanvasWidget *witem = (GooCanvasWidget*) object; switch (prop_id) { case PROP_WIDGET: g_value_set_object (value, witem->widget); break; case PROP_X: g_value_set_double (value, witem->x); break; case PROP_Y: g_value_set_double (value, witem->y); break; case PROP_WIDTH: g_value_set_double (value, witem->width); break; case PROP_HEIGHT: g_value_set_double (value, witem->height); break; case PROP_ANCHOR: g_value_set_enum (value, witem->anchor); break; case PROP_VISIBILITY: g_value_set_enum (value, simple->simple_data->visibility); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void goo_canvas_widget_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { GooCanvasItemSimple *simple = (GooCanvasItemSimple*) object; GooCanvasWidget *witem = (GooCanvasWidget*) object; switch (prop_id) { case PROP_WIDGET: goo_canvas_widget_set_widget (witem, g_value_get_object (value)); break; case PROP_X: witem->x = g_value_get_double (value); break; case PROP_Y: witem->y = g_value_get_double (value); break; case PROP_WIDTH: witem->width = g_value_get_double (value); break; case PROP_HEIGHT: witem->height = g_value_get_double (value); break; case PROP_ANCHOR: witem->anchor = g_value_get_enum (value); break; case PROP_VISIBILITY: simple->simple_data->visibility = g_value_get_enum (value); if (simple->simple_data->visibility <= GOO_CANVAS_ITEM_INVISIBLE) gtk_widget_hide (witem->widget); else gtk_widget_show (witem->widget); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } goo_canvas_item_simple_changed (simple, TRUE); } static void goo_canvas_widget_set_canvas (GooCanvasItem *item, GooCanvas *canvas) { GooCanvasItemSimple *simple = (GooCanvasItemSimple*) item; GooCanvasWidget *witem = (GooCanvasWidget*) item; if (simple->canvas != canvas) { if (simple->canvas) goo_canvas_unregister_widget_item (simple->canvas, witem); simple->canvas = canvas; if (simple->canvas) { goo_canvas_register_widget_item (simple->canvas, witem); if (witem->widget) { if (GTK_WIDGET_REALIZED (simple->canvas)) gtk_widget_set_parent_window (witem->widget, simple->canvas->canvas_window); gtk_widget_set_parent (witem->widget, GTK_WIDGET (simple->canvas)); } } else { if (witem->widget) gtk_widget_unparent (witem->widget); } } } static void goo_canvas_widget_set_parent (GooCanvasItem *item, GooCanvasItem *parent) { GooCanvasItemSimple *simple = (GooCanvasItemSimple*) item; GooCanvas *canvas; simple->parent = parent; simple->need_update = TRUE; simple->need_entire_subtree_update = TRUE; canvas = parent ? goo_canvas_item_get_canvas (parent) : NULL; goo_canvas_widget_set_canvas (item, canvas); } static void goo_canvas_widget_update (GooCanvasItemSimple *simple, cairo_t *cr) { GooCanvasWidget *witem = (GooCanvasWidget*) simple; GtkRequisition requisition; gdouble width, height; if (witem->widget) { /* Compute the new bounds. */ if (witem->width < 0 || witem->height < 0) { gtk_widget_size_request (witem->widget, &requisition); } simple->bounds.x1 = witem->x; simple->bounds.y1 = witem->y; width = witem->width < 0 ? requisition.width : witem->width; height = witem->height < 0 ? requisition.height : witem->height; switch (witem->anchor) { case GTK_ANCHOR_N: case GTK_ANCHOR_CENTER: case GTK_ANCHOR_S: simple->bounds.x1 -= width / 2.0; break; case GTK_ANCHOR_NE: case GTK_ANCHOR_E: case GTK_ANCHOR_SE: simple->bounds.x1 -= width; break; default: break; } switch (witem->anchor) { case GTK_ANCHOR_W: case GTK_ANCHOR_CENTER: case GTK_ANCHOR_E: simple->bounds.y1 -= height / 2.0; break; case GTK_ANCHOR_SW: case GTK_ANCHOR_S: case GTK_ANCHOR_SE: simple->bounds.y1 -= height; break; default: break; } simple->bounds.x2 = simple->bounds.x1 + width; simple->bounds.y2 = simple->bounds.y1 + height; /* Queue a resize of the widget so it gets moved. Note that the widget is moved by goo_canvas_size_allocate(). */ gtk_widget_queue_resize (witem->widget); } else { simple->bounds.x1 = simple->bounds.y1 = 0.0; simple->bounds.x2 = simple->bounds.y2 = 0.0; } } 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) { GooCanvasItemSimple *simple = (GooCanvasItemSimple*) item; GooCanvasWidget *witem = (GooCanvasWidget*) item; gdouble requested_width, requested_height, allocated_width, allocated_height; gdouble width_proportion, height_proportion; gdouble width, height; width = simple->bounds.x2 - simple->bounds.x1; height = simple->bounds.y2 - simple->bounds.y1; simple->bounds.x1 += x_offset; simple->bounds.y1 += y_offset; requested_width = requested_area->x2 - requested_area->x1; requested_height = requested_area->y2 - requested_area->y1; allocated_width = allocated_area->x2 - allocated_area->x1; allocated_height = allocated_area->y2 - allocated_area->y1; width_proportion = allocated_width / requested_width; height_proportion = allocated_height / requested_height; width *= width_proportion; height *= height_proportion; simple->bounds.x2 = simple->bounds.x1 + width; simple->bounds.y2 = simple->bounds.y1 + height; /* Queue a resize of the widget so it gets moved. Note that the widget is moved by goo_canvas_size_allocate(). */ gtk_widget_queue_resize (witem->widget); } static void goo_canvas_widget_paint (GooCanvasItemSimple *simple, cairo_t *cr, const GooCanvasBounds *bounds) { /* Do nothing for now. Maybe render for printing in future. */ } static gboolean goo_canvas_widget_is_item_at (GooCanvasItemSimple *simple, gdouble x, gdouble y, cairo_t *cr, gboolean is_pointer_event) { /* For now we just assume that the widget covers its entire bounds so we just return TRUE. In future if widget items support transforms we'll need to modify this. */ return TRUE; } static void canvas_item_interface_init (GooCanvasItemIface *iface) { iface->set_canvas = goo_canvas_widget_set_canvas; iface->set_parent = goo_canvas_widget_set_parent; iface->allocate_area = goo_canvas_widget_allocate_area; } static void goo_canvas_widget_class_init (GooCanvasWidgetClass *klass) { GObjectClass *gobject_class = (GObjectClass*) klass; GooCanvasItemSimpleClass *simple_class = (GooCanvasItemSimpleClass*) klass; gobject_class->dispose = goo_canvas_widget_dispose; gobject_class->get_property = goo_canvas_widget_get_property; gobject_class->set_property = goo_canvas_widget_set_property; simple_class->simple_update = goo_canvas_widget_update; simple_class->simple_paint = goo_canvas_widget_paint; simple_class->simple_is_item_at = goo_canvas_widget_is_item_at; /* Register our accessible factory, but only if accessibility is enabled. */ if (!ATK_IS_NO_OP_OBJECT_FACTORY (atk_registry_get_factory (atk_get_default_registry (), GTK_TYPE_WIDGET))) { atk_registry_set_factory_type (atk_get_default_registry (), GOO_TYPE_CANVAS_WIDGET, goo_canvas_widget_accessible_factory_get_type ()); } g_object_class_install_property (gobject_class, PROP_WIDGET, g_param_spec_object ("widget", _("Widget"), _("The widget to place in the canvas"), GTK_TYPE_WIDGET, G_PARAM_READWRITE)); g_object_class_install_property (gobject_class, PROP_X, g_param_spec_double ("x", "X", _("The x coordinate of the widget"), -G_MAXDOUBLE, G_MAXDOUBLE, 0.0, G_PARAM_READWRITE)); g_object_class_install_property (gobject_class, PROP_Y, g_param_spec_double ("y", "Y", _("The y coordinate of the widget"), -G_MAXDOUBLE, G_MAXDOUBLE, 0.0, G_PARAM_READWRITE)); g_object_class_install_property (gobject_class, PROP_WIDTH, g_param_spec_double ("width", _("Width"), _("The width of the widget, or -1 to use its requested width"), -G_MAXDOUBLE, G_MAXDOUBLE, -1.0, G_PARAM_READWRITE)); g_object_class_install_property (gobject_class, PROP_HEIGHT, g_param_spec_double ("height", _("Height"), _("The height of the widget, or -1 to use its requested height"), -G_MAXDOUBLE, G_MAXDOUBLE, -1.0, G_PARAM_READWRITE)); g_object_class_install_property (gobject_class, PROP_ANCHOR, g_param_spec_enum ("anchor", _("Anchor"), _("How to position the widget relative to the item's x and y coordinate settings"), GTK_TYPE_ANCHOR_TYPE, GTK_ANCHOR_NW, G_PARAM_READWRITE)); g_object_class_override_property (gobject_class, PROP_VISIBILITY, "visibility"); }