[CPML] s/cpml_(.*?)_length/cpml_\1_get_length/
[adg.git] / nodist / AdgText / goocanvastext.c
blob2360454b1fefd1175297c2594094c75f83aba9b7
1 /*
2 * GooCanvas. Copyright (C) 2005 Damon Chaplin.
3 * Released under the GNU LGPL license. See COPYING for details.
5 * goocanvastext.c - text item.
6 */
8 /**
9 * SECTION:goocanvastext
10 * @Title: GooCanvasText
11 * @Short_Description: a text item.
13 * GooCanvasText represents a text item.
15 * It is a subclass of #GooCanvasItemSimple and so inherits all of the style
16 * properties such as "fill-color".
18 * It also implements the #GooCanvasItem interface, so you can use the
19 * #GooCanvasItem functions such as goo_canvas_item_raise() and
20 * goo_canvas_item_rotate().
22 * The #GooCanvasText:width and #GooCanvasText:height properties specify the
23 * area of the item. If it exceeds that area because there is too much text,
24 * it is clipped. The properties can be set to -1 to disable clipping.
26 * To create a #GooCanvasText use goo_canvas_text_new().
28 * To get or set the properties of an existing #GooCanvasText, use
29 * g_object_get() and g_object_set().
31 #include <config.h>
32 #include <glib/gi18n-lib.h>
33 #include <gtk/gtk.h>
34 #include "goocanvastext.h"
35 #include "goocanvas.h"
37 typedef struct _GooCanvasTextPrivate GooCanvasTextPrivate;
38 struct _GooCanvasTextPrivate {
39 gdouble height;
42 #define GOO_CANVAS_TEXT_GET_PRIVATE(text) \
43 (G_TYPE_INSTANCE_GET_PRIVATE ((text), GOO_TYPE_CANVAS_TEXT, GooCanvasTextPrivate))
44 #define GOO_CANVAS_TEXT_MODEL_GET_PRIVATE(text) \
45 (G_TYPE_INSTANCE_GET_PRIVATE ((text), GOO_TYPE_CANVAS_TEXT_MODEL, GooCanvasTextPrivate))
47 enum {
48 PROP_0,
50 PROP_X,
51 PROP_Y,
52 PROP_WIDTH,
53 PROP_HEIGHT,
54 PROP_TEXT,
55 PROP_USE_MARKUP,
56 PROP_ANCHOR,
57 PROP_ALIGN,
58 PROP_ELLIPSIZE,
59 PROP_WRAP
62 static PangoLayout*
63 goo_canvas_text_create_layout (GooCanvasItemSimpleData *simple_data,
64 GooCanvasTextData *text_data,
65 gdouble layout_width,
66 cairo_t *cr,
67 GooCanvasBounds *bounds,
68 gdouble *origin_x_return,
69 gdouble *origin_y_return);
71 static void goo_canvas_text_finalize (GObject *object);
72 static void canvas_item_interface_init (GooCanvasItemIface *iface);
73 static void goo_canvas_text_get_property (GObject *object,
74 guint param_id,
75 GValue *value,
76 GParamSpec *pspec);
77 static void goo_canvas_text_set_property (GObject *object,
78 guint param_id,
79 const GValue *value,
80 GParamSpec *pspec);
82 G_DEFINE_TYPE_WITH_CODE (GooCanvasText, goo_canvas_text,
83 GOO_TYPE_CANVAS_ITEM_SIMPLE,
84 G_IMPLEMENT_INTERFACE (GOO_TYPE_CANVAS_ITEM,
85 canvas_item_interface_init))
88 static void
89 goo_canvas_text_install_common_properties (GObjectClass *gobject_class)
91 /* Text */
92 g_object_class_install_property (gobject_class, PROP_TEXT,
93 g_param_spec_string ("text",
94 _("Text"),
95 _("The text to display"),
96 NULL,
97 G_PARAM_READWRITE));
99 g_object_class_install_property (gobject_class, PROP_USE_MARKUP,
100 g_param_spec_boolean ("use-markup",
101 _("Use Markup"),
102 _("Whether to parse PangoMarkup in the text, to support different styles"),
103 FALSE,
104 G_PARAM_READWRITE));
106 g_object_class_install_property (gobject_class, PROP_ELLIPSIZE,
107 g_param_spec_enum ("ellipsize",
108 _("Ellipsize"),
109 _("The preferred place to ellipsize the string, if the label does not have enough room to display the entire string"),
110 PANGO_TYPE_ELLIPSIZE_MODE,
111 PANGO_ELLIPSIZE_NONE,
112 G_PARAM_READWRITE));
114 g_object_class_install_property (gobject_class, PROP_WRAP,
115 g_param_spec_enum ("wrap",
116 _("Wrap"),
117 _("The preferred method of wrapping the string if a width has been set"),
118 PANGO_TYPE_WRAP_MODE,
119 PANGO_WRAP_WORD,
120 G_PARAM_READWRITE));
122 /* Position */
123 g_object_class_install_property (gobject_class, PROP_X,
124 g_param_spec_double ("x",
125 "X",
126 _("The x coordinate of the text"),
127 -G_MAXDOUBLE,
128 G_MAXDOUBLE, 0.0,
129 G_PARAM_READWRITE));
131 g_object_class_install_property (gobject_class, PROP_Y,
132 g_param_spec_double ("y",
133 "Y",
134 _("The y coordinate of the text"),
135 -G_MAXDOUBLE,
136 G_MAXDOUBLE, 0.0,
137 G_PARAM_READWRITE));
139 g_object_class_install_property (gobject_class, PROP_WIDTH,
140 g_param_spec_double ("width",
141 _("Width"),
142 _("The width to use to layout the text"),
143 -G_MAXDOUBLE,
144 G_MAXDOUBLE, -1.0,
145 G_PARAM_READWRITE));
147 g_object_class_install_property (gobject_class, PROP_HEIGHT,
148 g_param_spec_double ("height",
149 _("Height"),
150 _("The height to use to layout the text, or -1 to use the natural height"),
151 -G_MAXDOUBLE,
152 G_MAXDOUBLE, -1.0,
153 G_PARAM_READWRITE));
156 g_object_class_install_property (gobject_class, PROP_ANCHOR,
157 g_param_spec_enum ("anchor",
158 _("Anchor"),
159 _("How to position the text relative to the given x and y coordinates"),
160 GTK_TYPE_ANCHOR_TYPE,
161 GTK_ANCHOR_NW,
162 G_PARAM_READWRITE));
164 g_object_class_install_property (gobject_class, PROP_ALIGN,
165 g_param_spec_enum ("alignment",
166 _("Alignment"),
167 _("How to align the text"),
168 PANGO_TYPE_ALIGNMENT,
169 PANGO_ALIGN_LEFT,
170 G_PARAM_READWRITE));
174 static void
175 goo_canvas_text_init (GooCanvasText *text)
177 GooCanvasTextPrivate *priv = GOO_CANVAS_TEXT_GET_PRIVATE (text);
179 text->text_data = g_slice_new0 (GooCanvasTextData);
180 text->text_data->width = -1.0;
181 text->text_data->anchor = GTK_ANCHOR_NW;
182 text->text_data->ellipsize = PANGO_ELLIPSIZE_NONE;
183 text->text_data->wrap = PANGO_WRAP_WORD;
185 text->layout_width = -1.0;
187 priv->height = -1.0;
192 * goo_canvas_text_new:
193 * @parent: the parent item, or %NULL. If a parent is specified, it will assume
194 * ownership of the item, and the item will automatically be freed when it is
195 * removed from the parent. Otherwise call g_object_unref() to free it.
196 * @string: the text to display.
197 * @x: the x coordinate of the text.
198 * @y: the y coordinate of the text.
199 * @width: the width of the text item, or -1 for unlimited width.
200 * @anchor: the position of the text relative to the given @x and @y
201 * coordinates. For example an anchor of %GDK_ANCHOR_NW will result in the
202 * top-left of the text being placed at the given @x and @y coordinates.
203 * An anchor of %GDK_ANCHOR_CENTER will result in the center of the text being
204 * placed at the @x and @y coordinates.
205 * @...: optional pairs of property names and values, and a terminating %NULL.
207 * Creates a new text item.
209 * <!--PARAMETERS-->
211 * Here's an example showing how to create a text item with the bottom right
212 * of the text box placed at (500,500):
214 * <informalexample><programlisting>
215 * GooCanvasItem *text = goo_canvas_text_new (mygroup, "Hello World", 500.0, 500.0, 200.0, GTK_ANCHOR_SE,
216 * "fill-color", "blue",
217 * NULL);
218 * </programlisting></informalexample>
220 * Returns: a new text item.
222 GooCanvasItem*
223 goo_canvas_text_new (GooCanvasItem *parent,
224 const char *string,
225 gdouble x,
226 gdouble y,
227 gdouble width,
228 GtkAnchorType anchor,
229 ...)
231 GooCanvasItem *item;
232 GooCanvasText *text;
233 GooCanvasTextData *text_data;
234 const char *first_property;
235 va_list var_args;
237 item = g_object_new (GOO_TYPE_CANVAS_TEXT, NULL);
238 text = (GooCanvasText*) item;
240 text_data = text->text_data;
241 text_data->text = g_strdup (string);
242 text_data->x = x;
243 text_data->y = y;
244 text_data->width = width;
245 text_data->anchor = anchor;
247 va_start (var_args, anchor);
248 first_property = va_arg (var_args, char*);
249 if (first_property)
250 g_object_set_valist ((GObject*) item, first_property, var_args);
251 va_end (var_args);
253 if (parent)
255 goo_canvas_item_add_child (parent, item, -1);
256 g_object_unref (item);
259 return item;
263 static void
264 goo_canvas_text_finalize (GObject *object)
266 GooCanvasItemSimple *simple = (GooCanvasItemSimple*) object;
267 GooCanvasText *text = (GooCanvasText*) object;
269 /* Free our data if we didn't have a model. (If we had a model it would
270 have been reset in dispose() and simple_data will be NULL.) */
271 if (simple->simple_data)
273 g_free (text->text_data->text);
274 g_slice_free (GooCanvasTextData, text->text_data);
276 text->text_data = NULL;
278 G_OBJECT_CLASS (goo_canvas_text_parent_class)->finalize (object);
282 /* Gets the private data to use, from the model or from the item itself. */
283 static GooCanvasTextPrivate*
284 goo_canvas_text_get_private (GooCanvasText *text)
286 GooCanvasItemSimple *simple = (GooCanvasItemSimple*) text;
288 if (simple->model)
289 return GOO_CANVAS_TEXT_MODEL_GET_PRIVATE (simple->model);
290 else
291 return GOO_CANVAS_TEXT_GET_PRIVATE (text);
295 static void
296 goo_canvas_text_get_common_property (GObject *object,
297 GooCanvasTextData *text_data,
298 GooCanvasTextPrivate *priv,
299 guint prop_id,
300 GValue *value,
301 GParamSpec *pspec)
303 switch (prop_id)
305 case PROP_X:
306 g_value_set_double (value, text_data->x);
307 break;
308 case PROP_Y:
309 g_value_set_double (value, text_data->y);
310 break;
311 case PROP_WIDTH:
312 g_value_set_double (value, text_data->width);
313 break;
314 case PROP_HEIGHT:
315 g_value_set_double (value, priv->height);
316 break;
317 case PROP_TEXT:
318 g_value_set_string (value, text_data->text);
319 break;
320 case PROP_USE_MARKUP:
321 g_value_set_boolean (value, text_data->use_markup);
322 break;
323 case PROP_ELLIPSIZE:
324 g_value_set_enum (value, text_data->ellipsize);
325 break;
326 case PROP_WRAP:
327 g_value_set_enum (value, text_data->wrap);
328 break;
329 case PROP_ANCHOR:
330 g_value_set_enum (value, text_data->anchor);
331 break;
332 case PROP_ALIGN:
333 g_value_set_enum (value, text_data->alignment);
334 break;
335 default:
336 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
337 break;
342 static void
343 goo_canvas_text_get_property (GObject *object,
344 guint prop_id,
345 GValue *value,
346 GParamSpec *pspec)
348 GooCanvasText *text = (GooCanvasText*) object;
349 GooCanvasTextPrivate *priv = goo_canvas_text_get_private (text);
351 goo_canvas_text_get_common_property (object, text->text_data, priv,
352 prop_id, value, pspec);
356 static void
357 goo_canvas_text_set_common_property (GObject *object,
358 GooCanvasTextData *text_data,
359 GooCanvasTextPrivate *priv,
360 guint prop_id,
361 const GValue *value,
362 GParamSpec *pspec)
364 switch (prop_id)
366 case PROP_X:
367 text_data->x = g_value_get_double (value);
368 break;
369 case PROP_Y:
370 text_data->y = g_value_get_double (value);
371 break;
372 case PROP_WIDTH:
373 text_data->width = g_value_get_double (value);
374 break;
375 case PROP_HEIGHT:
376 priv->height = g_value_get_double (value);
377 break;
378 case PROP_TEXT:
379 g_free (text_data->text);
380 text_data->text = g_value_dup_string (value);
381 break;
382 case PROP_USE_MARKUP:
383 text_data->use_markup = g_value_get_boolean (value);
384 break;
385 case PROP_ELLIPSIZE:
386 text_data->ellipsize = g_value_get_enum (value);
387 break;
388 case PROP_WRAP:
389 text_data->wrap = g_value_get_enum (value);
390 break;
391 case PROP_ANCHOR:
392 text_data->anchor = g_value_get_enum (value);
393 break;
394 case PROP_ALIGN:
395 text_data->alignment = g_value_get_enum (value);
396 break;
397 default:
398 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
399 break;
404 static void
405 goo_canvas_text_set_property (GObject *object,
406 guint prop_id,
407 const GValue *value,
408 GParamSpec *pspec)
410 GooCanvasItemSimple *simple = (GooCanvasItemSimple*) object;
411 GooCanvasText *text = (GooCanvasText*) object;
412 GooCanvasTextPrivate *priv = goo_canvas_text_get_private (text);
414 if (simple->model)
416 g_warning ("Can't set property of a canvas item with a model - set the model property instead");
417 return;
420 goo_canvas_text_set_common_property (object, text->text_data, priv, prop_id,
421 value, pspec);
422 goo_canvas_item_simple_changed (simple, TRUE);
426 static PangoLayout*
427 goo_canvas_text_create_layout (GooCanvasItemSimpleData *simple_data,
428 GooCanvasTextData *text_data,
429 gdouble layout_width,
430 cairo_t *cr,
431 GooCanvasBounds *bounds,
432 gdouble *origin_x_return,
433 gdouble *origin_y_return)
435 GooCanvasStyle *style = simple_data->style;
436 GValue *svalue;
437 PangoLayout *layout;
438 PangoContext *context;
439 PangoRectangle ink_rect, logical_rect;
440 double logical_width, logical_height, align_width, origin_x, origin_y;
441 gchar *string;
442 double x1_extension, x2_extension, y1_extension, y2_extension;
443 cairo_font_options_t *font_options;
444 cairo_hint_metrics_t hint_metrics = CAIRO_HINT_METRICS_OFF;
446 string = text_data->text ? text_data->text : "";
448 layout = pango_cairo_create_layout (cr);
449 context = pango_layout_get_context (layout);
451 if (layout_width > 0)
452 pango_layout_set_width (layout, (double) layout_width * PANGO_SCALE);
454 if (text_data->use_markup)
455 pango_layout_set_markup (layout, string, -1);
456 else
457 pango_layout_set_text (layout, string, -1);
459 svalue = goo_canvas_style_get_property (style,
460 goo_canvas_style_font_desc_id);
461 if (svalue)
462 pango_layout_set_font_description (layout, svalue->data[0].v_pointer);
464 svalue = goo_canvas_style_get_property (style,
465 goo_canvas_style_hint_metrics_id);
466 if (svalue)
467 hint_metrics = svalue->data[0].v_long;
469 font_options = cairo_font_options_create ();
470 cairo_font_options_set_hint_metrics (font_options, hint_metrics);
471 pango_cairo_context_set_font_options (context, font_options);
472 cairo_font_options_destroy (font_options);
474 if (text_data->alignment != PANGO_ALIGN_LEFT)
475 pango_layout_set_alignment (layout, text_data->alignment);
477 pango_layout_set_ellipsize (layout, text_data->ellipsize);
479 pango_layout_set_wrap (layout, text_data->wrap);
481 if (bounds)
483 /* Get size of the text, so we can position it according to anchor. */
484 pango_layout_get_extents (layout, &ink_rect, &logical_rect);
486 logical_width = (double) logical_rect.width / PANGO_SCALE;
487 logical_height = (double) logical_rect.height / PANGO_SCALE;
489 /* If the text width has been set, that width is used to do the alignment
490 positioning. Otherwise the actual width is used. */
491 if (text_data->width > 0)
492 align_width = text_data->width;
493 else
494 align_width = logical_width;
496 /* Now calculate the origin of the text, i.e. where we will tell Pango
497 to draw it. */
498 origin_x = text_data->x;
499 origin_y = text_data->y;
501 switch (text_data->anchor)
503 case GTK_ANCHOR_N:
504 case GTK_ANCHOR_CENTER:
505 case GTK_ANCHOR_S:
506 origin_x -= align_width / 2.0;
507 break;
508 case GTK_ANCHOR_NE:
509 case GTK_ANCHOR_E:
510 case GTK_ANCHOR_SE:
511 origin_x -= align_width;
512 break;
513 default:
514 break;
517 switch (text_data->anchor)
519 case GTK_ANCHOR_W:
520 case GTK_ANCHOR_CENTER:
521 case GTK_ANCHOR_E:
522 origin_y -= logical_height / 2.0;
523 break;
524 case GTK_ANCHOR_SW:
525 case GTK_ANCHOR_S:
526 case GTK_ANCHOR_SE:
527 origin_y -= logical_height;
528 break;
529 default:
530 break;
533 /* Return the origin of the text if required. */
534 if (origin_x_return)
535 *origin_x_return = origin_x;
536 if (origin_y_return)
537 *origin_y_return = origin_y;
539 /* Now calculate the logical bounds. */
540 bounds->x1 = origin_x;
541 bounds->y1 = origin_y;
543 if (text_data->width > 0)
545 /* If the text width has been set, and the alignment isn't
546 PANGO_ALIGN_LEFT, we need to adjust for the difference between
547 the actual width of the text and the width that was used for
548 alignment. */
549 switch (text_data->alignment)
551 case PANGO_ALIGN_CENTER:
552 bounds->x1 += (align_width - logical_width) / 2.0;
553 break;
554 case PANGO_ALIGN_RIGHT:
555 bounds->x1 += align_width - logical_width;
556 break;
557 default:
558 break;
562 bounds->x2 = bounds->x1 + logical_width;
563 bounds->y2 = bounds->y1 + logical_height;
565 /* Now adjust it to take into account the ink bounds. Calculate how far
566 the ink rect extends outside each edge of the logical rect and adjust
567 the bounds as necessary. */
568 x1_extension = logical_rect.x - ink_rect.x;
569 if (x1_extension > 0)
570 bounds->x1 -= x1_extension / PANGO_SCALE;
572 x2_extension = (ink_rect.x + ink_rect.width)
573 - (logical_rect.x + logical_rect.width);
574 if (x2_extension > 0)
575 bounds->x2 += x2_extension / PANGO_SCALE;
577 y1_extension = logical_rect.y - ink_rect.y;
578 if (y1_extension > 0)
579 bounds->y1 -= y1_extension / PANGO_SCALE;
581 y2_extension = (ink_rect.y + ink_rect.height)
582 - (logical_rect.y + logical_rect.height);
583 if (y2_extension > 0)
584 bounds->y2 += y2_extension / PANGO_SCALE;
587 return layout;
591 static void
592 goo_canvas_text_update (GooCanvasItemSimple *simple,
593 cairo_t *cr)
595 GooCanvasText *text = (GooCanvasText*) simple;
596 GooCanvasTextPrivate *priv = goo_canvas_text_get_private (text);
597 PangoLayout *layout;
599 /* Initialize the layout width to the text item's specified width property.
600 It may get changed later in get_requested_height() according to the
601 layout container and settings. */
602 text->layout_width = text->text_data->width;
604 /* Compute the new bounds. */
605 layout = goo_canvas_text_create_layout (simple->simple_data, text->text_data,
606 text->layout_width, cr,
607 &simple->bounds, NULL, NULL);
608 g_object_unref (layout);
610 /* If the height is set, use that. */
611 if (priv->height > 0.0)
612 simple->bounds.y2 = simple->bounds.y1 + priv->height;
616 static gboolean
617 goo_canvas_text_is_unpainted (GooCanvasStyle *style)
619 GValue *value;
621 value = goo_canvas_style_get_property (style,
622 goo_canvas_style_fill_pattern_id);
624 /* We only return TRUE if the value is explicitly set to NULL. */
625 if (value && !value->data[0].v_pointer)
626 return TRUE;
627 return FALSE;
631 static gboolean
632 goo_canvas_text_is_item_at (GooCanvasItemSimple *simple,
633 gdouble x,
634 gdouble y,
635 cairo_t *cr,
636 gboolean is_pointer_event)
638 GooCanvasItemSimpleData *simple_data = simple->simple_data;
639 GooCanvasText *text = (GooCanvasText*) simple;
640 GooCanvasTextPrivate *priv = goo_canvas_text_get_private (text);
641 PangoLayout *layout;
642 GooCanvasBounds bounds;
643 PangoLayoutIter *iter;
644 PangoRectangle ink_rect, log_rect;
645 int px, py, x1, y1, x2, y2;
646 int log_x2, ink_x2, log_y2, ink_y2;
647 gdouble origin_x, origin_y;
648 gboolean in_item = FALSE;
650 /* If there is no text just return. */
651 if (!text->text_data->text || !text->text_data->text[0])
652 return FALSE;
654 if (is_pointer_event
655 && simple_data->pointer_events & GOO_CANVAS_EVENTS_PAINTED_MASK
656 && goo_canvas_text_is_unpainted (simple_data->style))
657 return FALSE;
659 /* Check if the point is outside the clipped height. */
660 if (priv->height > 0.0 && y > priv->height)
661 return FALSE;
663 layout = goo_canvas_text_create_layout (simple_data, text->text_data,
664 text->layout_width, cr, &bounds,
665 &origin_x, &origin_y);
667 /* Convert the coordinates into Pango units. */
668 px = (x - origin_x) * PANGO_SCALE;
669 py = (y - origin_y) * PANGO_SCALE;
671 /* We use line extents here. Note that SVG uses character cells to determine
672 hits so we have slightly different behavior. */
673 iter = pango_layout_get_iter (layout);
676 pango_layout_iter_get_line_extents (iter, &ink_rect, &log_rect);
678 /* We use a union of the ink rect and the logical rect, as we want to
679 let the user click on any part of the ink, even if it extends outside
680 the character cell (i.e. the ink rect), or click on the space in the
681 character cell (i.e. the logical rect). */
682 x1 = MIN (log_rect.x, ink_rect.x);
683 y1 = MIN (log_rect.y, ink_rect.y);
685 log_x2 = log_rect.x + log_rect.width;
686 ink_x2 = ink_rect.x + ink_rect.width;
687 x2 = MAX (log_x2, ink_x2);
689 log_y2 = log_rect.y + log_rect.height;
690 ink_y2 = ink_rect.y + ink_rect.height;
691 y2 = MAX (log_y2, ink_y2);
693 if (px >= x1 && px < x2 && py >= y1 && py < y2)
695 in_item = TRUE;
696 break;
699 } while (pango_layout_iter_next_line (iter));
701 pango_layout_iter_free (iter);
703 g_object_unref (layout);
705 return in_item;
709 static void
710 goo_canvas_text_paint (GooCanvasItemSimple *simple,
711 cairo_t *cr,
712 const GooCanvasBounds *bounds)
714 GooCanvasText *text = (GooCanvasText*) simple;
715 GooCanvasTextPrivate *priv = goo_canvas_text_get_private (text);
716 PangoLayout *layout;
717 GooCanvasBounds layout_bounds;
718 gdouble origin_x, origin_y;
720 /* If there is no text just return. */
721 if (!text->text_data->text || !text->text_data->text[0])
722 return;
724 goo_canvas_style_set_fill_options (simple->simple_data->style, cr);
726 cairo_new_path (cr);
727 layout = goo_canvas_text_create_layout (simple->simple_data, text->text_data,
728 text->layout_width, cr,
729 &layout_bounds,
730 &origin_x, &origin_y);
731 cairo_save (cr);
733 if (priv->height > 0.0)
735 cairo_rectangle (cr, origin_x, origin_y,
736 text->layout_width, priv->height);
737 cairo_clip (cr);
739 cairo_move_to (cr, origin_x, origin_y);
740 pango_cairo_show_layout (cr, layout);
742 cairo_restore (cr);
743 g_object_unref (layout);
747 static gdouble
748 goo_canvas_text_get_requested_height (GooCanvasItem *item,
749 cairo_t *cr,
750 gdouble width)
752 GooCanvasItemSimple *simple = (GooCanvasItemSimple*) item;
753 GooCanvasItemSimpleData *simple_data = simple->simple_data;
754 GooCanvasText *text = (GooCanvasText*) item;
755 GooCanvasTextPrivate *priv = goo_canvas_text_get_private (text);
756 PangoLayout *layout;
757 gdouble height;
759 /* If we have a transformation besides a simple scale & translation, just
760 return -1 as we can't adjust the height in that case. */
761 if (simple_data->clip_path_commands
762 || (simple_data->transform && (simple_data->transform->xy != 0.0
763 || simple_data->transform->yx != 0.0)))
764 return -1;
766 cairo_save (cr);
767 if (simple_data->transform)
768 cairo_transform (cr, simple_data->transform);
770 /* Convert the width from the parent's coordinate space. Note that we only
771 need to support a simple scale operation here. */
772 text->layout_width = width;
773 if (simple_data->transform)
774 text->layout_width /= simple_data->transform->xx;
776 if (priv->height < 0.0)
778 /* Create layout with given width. */
779 layout = goo_canvas_text_create_layout (simple_data, text->text_data,
780 text->layout_width, cr,
781 &simple->bounds, NULL, NULL);
782 g_object_unref (layout);
784 height = simple->bounds.y2 - simple->bounds.y1;
786 else
788 height = priv->height;
791 /* Convert to the parent's coordinate space. As above, we only need to
792 support a simple scale operation here. */
793 if (simple_data->transform)
794 height *= simple_data->transform->yy;
796 /* Convert the item's bounds to device space. */
797 goo_canvas_item_simple_user_bounds_to_device (simple, cr, &simple->bounds);
799 cairo_restore (cr);
801 /* Return the new requested height of the text. */
802 return height;
807 * goo_canvas_text_get_natural_extents:
808 * @text: a #GooCanvasText.
809 * @ink_rect: the location to return the ink rect, or %NULL.
810 * @logical_rect: the location to return the logical rect, or %NULL.
812 * Gets the natural extents of the text, in the text item's coordinate space.
814 * The final extents of the text may be different, if the text item is placed
815 * in a layout container such as #GooCanvasTable.
817 void
818 goo_canvas_text_get_natural_extents (GooCanvasText *text,
819 PangoRectangle *ink_rect,
820 PangoRectangle *logical_rect)
822 GooCanvasItem *item = (GooCanvasItem*) text;
823 GooCanvasItemSimple *simple = (GooCanvasItemSimple*) text;
824 PangoLayout *layout;
825 cairo_t *cr;
827 if (simple->need_update)
828 goo_canvas_item_ensure_updated (item);
830 cr = goo_canvas_create_cairo_context (simple->canvas);
831 layout = goo_canvas_text_create_layout (simple->simple_data, text->text_data,
832 text->text_data->width, cr, NULL,
833 NULL, NULL);
834 pango_layout_get_extents (layout, ink_rect, logical_rect);
835 g_object_unref (layout);
836 cairo_destroy (cr);
840 static void
841 goo_canvas_text_set_model (GooCanvasItem *item,
842 GooCanvasItemModel *model)
844 GooCanvasItemSimple *simple = (GooCanvasItemSimple*) item;
845 GooCanvasText *text = (GooCanvasText*) item;
846 GooCanvasTextModel *tmodel = (GooCanvasTextModel*) model;
848 /* If our text_data was allocated, free it. */
849 if (!simple->model)
850 g_slice_free (GooCanvasTextData, text->text_data);
852 /* Now use the new model's text_data instead. */
853 text->text_data = &tmodel->text_data;
855 /* Let the parent GooCanvasItemSimple code do the rest. */
856 goo_canvas_item_simple_set_model (simple, model);
860 static void
861 canvas_item_interface_init (GooCanvasItemIface *iface)
863 iface->get_requested_height = goo_canvas_text_get_requested_height;
864 iface->set_model = goo_canvas_text_set_model;
868 static void
869 goo_canvas_text_class_init (GooCanvasTextClass *klass)
871 GObjectClass *gobject_class = (GObjectClass*) klass;
872 GooCanvasItemSimpleClass *simple_class = (GooCanvasItemSimpleClass*) klass;
874 g_type_class_add_private (gobject_class, sizeof (GooCanvasTextPrivate));
876 gobject_class->finalize = goo_canvas_text_finalize;
878 gobject_class->get_property = goo_canvas_text_get_property;
879 gobject_class->set_property = goo_canvas_text_set_property;
881 simple_class->simple_update = goo_canvas_text_update;
882 simple_class->simple_paint = goo_canvas_text_paint;
883 simple_class->simple_is_item_at = goo_canvas_text_is_item_at;
885 goo_canvas_text_install_common_properties (gobject_class);
891 * SECTION:goocanvastextmodel
892 * @Title: GooCanvasTextModel
893 * @Short_Description: a model for text items.
895 * GooCanvasTextModel represents a model for text items.
897 * It is a subclass of #GooCanvasItemModelSimple and so inherits all of the
898 * style properties such as "fill-color".
900 * It also implements the #GooCanvasItemModel interface, so you can use the
901 * #GooCanvasItemModel functions such as goo_canvas_item_model_raise() and
902 * goo_canvas_item_model_rotate().
904 * To create a #GooCanvasTextModel use goo_canvas_text_model_new().
906 * To get or set the properties of an existing #GooCanvasTextModel, use
907 * g_object_get() and g_object_set().
909 * To respond to events such as mouse clicks on the text item you must connect
910 * to the signal handlers of the corresponding #GooCanvasText objects.
911 * (See goo_canvas_get_item() and #GooCanvas::item-created.)
914 static void item_model_interface_init (GooCanvasItemModelIface *iface);
915 static void goo_canvas_text_model_finalize (GObject *object);
916 static void goo_canvas_text_model_get_property (GObject *object,
917 guint param_id,
918 GValue *value,
919 GParamSpec *pspec);
920 static void goo_canvas_text_model_set_property (GObject *object,
921 guint param_id,
922 const GValue *value,
923 GParamSpec *pspec);
925 G_DEFINE_TYPE_WITH_CODE (GooCanvasTextModel, goo_canvas_text_model,
926 GOO_TYPE_CANVAS_ITEM_MODEL_SIMPLE,
927 G_IMPLEMENT_INTERFACE (GOO_TYPE_CANVAS_ITEM_MODEL,
928 item_model_interface_init))
931 static void
932 goo_canvas_text_model_class_init (GooCanvasTextModelClass *klass)
934 GObjectClass *gobject_class = (GObjectClass*) klass;
936 g_type_class_add_private (gobject_class, sizeof (GooCanvasTextPrivate));
938 gobject_class->finalize = goo_canvas_text_model_finalize;
940 gobject_class->get_property = goo_canvas_text_model_get_property;
941 gobject_class->set_property = goo_canvas_text_model_set_property;
943 goo_canvas_text_install_common_properties (gobject_class);
947 static void
948 goo_canvas_text_model_init (GooCanvasTextModel *tmodel)
950 GooCanvasTextPrivate *priv = GOO_CANVAS_TEXT_MODEL_GET_PRIVATE (tmodel);
952 tmodel->text_data.width = -1.0;
953 tmodel->text_data.anchor = GTK_ANCHOR_NW;
954 tmodel->text_data.ellipsize = PANGO_ELLIPSIZE_NONE;
955 tmodel->text_data.wrap = PANGO_WRAP_WORD;
957 priv->height = -1.0;
962 * goo_canvas_text_model_new:
963 * @parent: the parent model, or %NULL. If a parent is specified, it will
964 * assume ownership of the item, and the item will automatically be freed when
965 * it is removed from the parent. Otherwise call g_object_unref() to free it.
966 * @string: the text to display.
967 * @x: the x coordinate of the text.
968 * @y: the y coordinate of the text.
969 * @width: the width of the text item, or -1 for unlimited width.
970 * @anchor: the position of the text relative to the given @x and @y
971 * coordinates. For example an anchor of %GDK_ANCHOR_NW will result in the
972 * top-left of the text being placed at the given @x and @y coordinates.
973 * An anchor of %GDK_ANCHOR_CENTER will result in the center of the text being
974 * placed at the @x and @y coordinates.
975 * @...: optional pairs of property names and values, and a terminating %NULL.
977 * Creates a new text model.
979 * <!--PARAMETERS-->
981 * Here's an example showing how to create a text item with the bottom right
982 * of the text box placed at (500,500):
984 * <informalexample><programlisting>
985 * GooCanvasItemModel *text = goo_canvas_text_model_new (mygroup, "Hello World", 500.0, 500.0, 200.0, GTK_ANCHOR_SE,
986 * "fill-color", "blue",
987 * NULL);
988 * </programlisting></informalexample>
990 * Returns: a new text model.
992 GooCanvasItemModel*
993 goo_canvas_text_model_new (GooCanvasItemModel *parent,
994 const char *string,
995 gdouble x,
996 gdouble y,
997 gdouble width,
998 GtkAnchorType anchor,
999 ...)
1001 GooCanvasItemModel *model;
1002 GooCanvasTextModel *tmodel;
1003 GooCanvasTextData *text_data;
1004 const char *first_property;
1005 va_list var_args;
1007 model = g_object_new (GOO_TYPE_CANVAS_TEXT_MODEL, NULL);
1008 tmodel = (GooCanvasTextModel*) model;
1010 text_data = &tmodel->text_data;
1011 text_data->text = g_strdup (string);
1012 text_data->x = x;
1013 text_data->y = y;
1014 text_data->width = width;
1015 text_data->anchor = anchor;
1017 va_start (var_args, anchor);
1018 first_property = va_arg (var_args, char*);
1019 if (first_property)
1020 g_object_set_valist ((GObject*) model, first_property, var_args);
1021 va_end (var_args);
1023 if (parent)
1025 goo_canvas_item_model_add_child (parent, model, -1);
1026 g_object_unref (model);
1029 return model;
1033 static void
1034 goo_canvas_text_model_finalize (GObject *object)
1036 GooCanvasTextModel *tmodel = (GooCanvasTextModel*) object;
1038 g_free (tmodel->text_data.text);
1040 G_OBJECT_CLASS (goo_canvas_text_model_parent_class)->finalize (object);
1044 static void
1045 goo_canvas_text_model_get_property (GObject *object,
1046 guint prop_id,
1047 GValue *value,
1048 GParamSpec *pspec)
1050 GooCanvasTextModel *tmodel = (GooCanvasTextModel*) object;
1051 GooCanvasTextPrivate *priv = GOO_CANVAS_TEXT_MODEL_GET_PRIVATE (tmodel);
1053 goo_canvas_text_get_common_property (object, &tmodel->text_data, priv,
1054 prop_id, value, pspec);
1058 static void
1059 goo_canvas_text_model_set_property (GObject *object,
1060 guint prop_id,
1061 const GValue *value,
1062 GParamSpec *pspec)
1064 GooCanvasTextModel *tmodel = (GooCanvasTextModel*) object;
1065 GooCanvasTextPrivate *priv = GOO_CANVAS_TEXT_MODEL_GET_PRIVATE (tmodel);
1067 goo_canvas_text_set_common_property (object, &tmodel->text_data, priv,
1068 prop_id, value, pspec);
1069 g_signal_emit_by_name (tmodel, "changed", TRUE);
1073 static GooCanvasItem*
1074 goo_canvas_text_model_create_item (GooCanvasItemModel *model,
1075 GooCanvas *canvas)
1077 GooCanvasItem *item;
1079 item = g_object_new (GOO_TYPE_CANVAS_TEXT, NULL);
1080 goo_canvas_item_set_model (item, model);
1082 return item;
1086 static void
1087 item_model_interface_init (GooCanvasItemModelIface *iface)
1089 iface->create_item = goo_canvas_text_model_create_item;