[CPML] s/cpml_(.*?)_length/cpml_\1_get_length/
[adg.git] / nodist / AdgText / cr-text.c
blob533d6a31098b7bbd62a782b04ee7ba45fab9da32
1 /* cr-text.c
2 * Copyright (C) 2006 Robert Gibbs <bgibbs@users.sourceforge.net>
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Library General Public License for more details.
14 * You should have received a copy of the GNU Library General Public
15 * License along with this library; if not, write to the
16 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17 * Boston, MA 02111-1307, USA.
19 #include <math.h>
20 #include <gtk/gtk.h>
21 #include "cr-text.h"
23 /**
24 * SECTION:cr-text
25 * @title: CrText
26 * @short_description: A text canvas item that uses #PangoLayout.
28 * This canvas item can render #PangoLayout.
31 static GObjectClass *parent_class = NULL;
33 enum {
34 ARG_0,
35 PROP_X_OFFSET,
36 PROP_Y_OFFSET,
37 PROP_WIDTH,
38 PROP_SCALEABLE,
39 PROP_FONT,
40 PROP_TEXT,
41 PROP_ANCHOR,
42 PROP_FILL_COLOR_RGBA,
43 PROP_FILL_COLOR,
44 PROP_USE_MARKUP
47 static void
48 cr_text_dispose(GObject *object)
50 parent_class->dispose(object);
53 static void
54 cr_text_finalize(GObject *object)
56 CrText *text;
58 text = CR_TEXT(object);
60 if (text->text) g_free(text->text);
61 if (text->font_desc) pango_font_description_free(text->font_desc);
63 parent_class->finalize(object);
66 static void
67 cr_text_set_property(GObject *object, guint property_id,
68 const GValue *value, GParamSpec *pspec)
70 const char *str;
71 GdkColor color = { 0, 0, 0, 0, };
72 CrText *text = (CrText*) object;
74 switch (property_id) {
75 case PROP_X_OFFSET:
76 text->x_offset = g_value_get_double (value);
77 cr_item_request_update(CR_ITEM(text));
78 break;
79 case PROP_Y_OFFSET:
80 text->y_offset = g_value_get_double (value);
81 cr_item_request_update(CR_ITEM(text));
82 break;
83 case PROP_WIDTH:
84 text->width = g_value_get_double (value);
85 cr_item_request_update(CR_ITEM(text));
86 break;
87 case PROP_SCALEABLE:
88 if (g_value_get_boolean(value))
89 text->flags |= CR_TEXT_SCALEABLE;
90 else
91 text->flags &= ~CR_TEXT_SCALEABLE;
92 cr_item_request_update(CR_ITEM(text));
93 break;
94 case PROP_FONT:
95 if (text->font_desc)
96 pango_font_description_free(text->font_desc);
97 text->font_desc = pango_font_description_from_string(
98 g_value_get_string (value));
99 cr_item_request_update(CR_ITEM(text));
100 break;
101 case PROP_ANCHOR:
102 text->anchor = g_value_get_enum (value);
103 cr_item_request_update(CR_ITEM(text));
104 break;
105 case PROP_FILL_COLOR_RGBA:
106 text->fill_color_rgba = g_value_get_uint(value);
107 text->flags |= CR_TEXT_FILL_COLOR_RGBA;
108 cr_item_request_update(CR_ITEM(text));
109 break;
110 case PROP_FILL_COLOR:
111 str = g_value_get_string(value);
112 if (str)
113 gdk_color_parse(str, &color);
114 text->fill_color_rgba = ((color.red & 0xff00) << 16 |
115 (color.green & 0xff00) << 8 |
116 (color.blue & 0xff00) |
117 0xff);
118 text->flags |= CR_TEXT_FILL_COLOR_RGBA;
119 cr_item_request_update(CR_ITEM(text));
120 break;
121 case PROP_TEXT:
122 text->text = g_value_dup_string (value);
123 cr_item_request_update(CR_ITEM(text));
124 break;
125 case PROP_USE_MARKUP:
126 if (g_value_get_boolean(value))
127 text->flags |= CR_TEXT_USE_MARKUP;
128 else
129 text->flags &= ~CR_TEXT_USE_MARKUP;
130 break;
131 default:
132 G_OBJECT_WARN_INVALID_PROPERTY_ID (object,
133 property_id, pspec);
137 static void
138 cr_text_get_property(GObject *object, guint property_id,
139 GValue *value, GParamSpec *pspec)
141 CrText *text = (CrText*) object;
142 char *str;
143 char color_string[8];
145 switch (property_id) {
146 case PROP_X_OFFSET:
147 g_value_set_double (value, text->x_offset);
148 break;
149 case PROP_Y_OFFSET:
150 g_value_set_double (value, text->y_offset);
151 break;
152 case PROP_WIDTH:
153 g_value_set_double (value, text->width);
154 break;
155 case PROP_SCALEABLE:
156 g_value_set_boolean(value,
157 text->flags & CR_TEXT_SCALEABLE);
158 break;
159 case PROP_FONT:
160 if (text->font_desc) {
161 str = pango_font_description_to_string(
162 text->font_desc);
163 g_value_set_string (value, str);
164 g_free(str);
166 else
167 g_value_set_string(value, NULL);
168 break;
169 case PROP_ANCHOR:
170 g_value_set_enum (value, text->anchor);
171 break;
172 case PROP_FILL_COLOR_RGBA:
173 g_value_set_uint(value, text->fill_color_rgba);
174 break;
175 case PROP_FILL_COLOR:
176 sprintf(color_string, "#%.6x",
177 text->fill_color_rgba >> 8);
178 g_value_set_string(value, color_string);
179 break;
180 case PROP_TEXT:
181 g_value_set_string (value, text->text);
182 break;
183 case PROP_USE_MARKUP:
184 g_value_set_boolean(value,
185 text->flags & CR_TEXT_USE_MARKUP);
186 break;
187 default:
188 G_OBJECT_WARN_INVALID_PROPERTY_ID (object,
189 property_id, pspec);
193 static void
194 create_layout(CrText *text, cairo_t *c)
196 if (!text->layout)
197 text->layout = pango_cairo_create_layout(c);
199 if (text->font_desc)
200 pango_layout_set_font_description(text->layout,
201 text->font_desc);
203 if (text->width > 0)
204 pango_layout_set_width (text->layout,
205 text->width * PANGO_SCALE);
206 if (text->flags & CR_TEXT_USE_MARKUP)
207 pango_layout_set_markup(text->layout, text->text, -1);
208 else
209 pango_layout_set_text(text->layout, text->text, -1);
212 static void
213 get_device_extents(CrText *text, cairo_t *c, CrDeviceBounds *device)
215 int width, height;
216 double w, h;
218 cairo_save(c);
219 cairo_identity_matrix(c);
221 pango_cairo_update_layout(c, text->layout);
223 pango_layout_get_size (text->layout, &width, &height);
224 w = (double)width / PANGO_SCALE;
225 h = (double)height / PANGO_SCALE;
227 cairo_restore(c);
229 /* Because this item does not mix both device and item coordinates,
230 * the device anchor can be tied anywhere. */
231 device->anchor = GTK_ANCHOR_NW;
233 switch (text->anchor) {
234 case GTK_ANCHOR_N:
235 case GTK_ANCHOR_CENTER:
236 case GTK_ANCHOR_S:
237 device->x1 = -w/2 + text->x_offset;
238 device->x2 = w/2 + text->x_offset;
239 break;
240 case GTK_ANCHOR_NE:
241 case GTK_ANCHOR_E:
242 case GTK_ANCHOR_SE:
243 device->x1 = -w + text->x_offset;
244 device->x2 = 0 + text->x_offset;
245 break;
246 case GTK_ANCHOR_NW:
247 case GTK_ANCHOR_W:
248 case GTK_ANCHOR_SW:
249 device->x1 = 0 + text->x_offset;
250 device->x2 = w + text->x_offset;
251 break;
253 switch (text->anchor) {
254 case GTK_ANCHOR_W:
255 case GTK_ANCHOR_CENTER:
256 case GTK_ANCHOR_E:
257 device->y1 = -h/2 + text->y_offset;
258 device->y2 = h/2 + text->y_offset;
259 break;
260 case GTK_ANCHOR_SW:
261 case GTK_ANCHOR_S:
262 case GTK_ANCHOR_SE:
263 device->y1 = -h + text->y_offset;
264 device->y2 = 0 + text->y_offset;
265 break;
266 case GTK_ANCHOR_NW:
267 case GTK_ANCHOR_N:
268 case GTK_ANCHOR_NE:
269 device->y1 = 0 + text->y_offset;
270 device->y2 = h + text->y_offset;
271 break;
275 static void
276 get_item_extents(CrText *text, cairo_t *c,
277 double *x, double *y, double *w, double *h)
279 int width, height;
281 *x = *y = 0;
283 pango_cairo_update_layout(c, text->layout);
284 pango_layout_get_size (text->layout, &width, &height);
285 *w = (double)width / PANGO_SCALE;
286 *h = (double)height / PANGO_SCALE;
288 switch (text->anchor) {
289 case GTK_ANCHOR_N:
290 case GTK_ANCHOR_CENTER:
291 case GTK_ANCHOR_S:
292 *x -= *w/ 2.0;
293 break;
295 case GTK_ANCHOR_NE:
296 case GTK_ANCHOR_E:
297 case GTK_ANCHOR_SE:
298 *x -= *w;
299 break;
301 switch (text->anchor) {
302 case GTK_ANCHOR_NW:
303 case GTK_ANCHOR_N:
304 case GTK_ANCHOR_NE:
305 *y += *h;
306 break;
307 case GTK_ANCHOR_W:
308 case GTK_ANCHOR_CENTER:
309 case GTK_ANCHOR_E:
310 *y += *h/ 2.0;
311 break;
315 static void
316 report_new_bounds(CrItem *item, cairo_t *ct, gboolean all)
318 double w, h;
319 CrText *text;
321 text = CR_TEXT(item);
323 /* Pango layout can change item coordinate size depending on the scale
324 * and rotation of the items higher up in the tree. This is because the
325 * fonts do not scale linearly. So each time the bounds get requested,
326 * I re-calculate the bounds just in case the higher up scale has
327 * changed. This is inefficient, but the alternative is setting up
328 * separate recalculate-bounds and update-item cycles.*/
330 if (text->layout && item->bounds && (text->flags & CR_TEXT_SCALEABLE)) {
331 get_item_extents(text, ct,
332 &item->bounds->x1, &item->bounds->y2,
333 &w, &h);
334 item->bounds->x2 = item->bounds->x1 + w;
335 item->bounds->y1 = item->bounds->y2 - h;
337 CR_ITEM_CLASS(parent_class)->report_new_bounds(item, ct, all);
340 static void
341 paint(CrItem *item, cairo_t *c)
343 CrText *text;
344 double r, g, b, a;
345 double x, y;
347 text = CR_TEXT(item);
349 if (!text->text) return;
351 if (text->flags & CR_TEXT_FILL_COLOR_RGBA) {
352 r = ((text->fill_color_rgba & 0xff000000) >> 24) / 255.;
353 g = ((text->fill_color_rgba & 0x00ff0000) >> 16) / 255.;
354 b = ((text->fill_color_rgba & 0x0000ff00) >> 8) / 255.;
355 a = (text->fill_color_rgba & 0xff) / 255.;
356 if (a == 1)
357 cairo_set_source_rgb(c, r, g, b);
358 else
359 cairo_set_source_rgba(c, r, g, b, a);
361 x = item->bounds->x1;
362 y = item->bounds->y1;
364 cairo_move_to(c, 0, 0);
365 cairo_line_to(c, 0, -10);
366 cairo_move_to(c, 0, 0);
367 cairo_line_to(c, 10, 0);
368 cairo_stroke(c);
371 if (!(text->flags & CR_TEXT_SCALEABLE)) {
372 cairo_user_to_device(c, &x, &y);
373 cairo_identity_matrix(c);
374 cairo_move_to(c, x + item->device->x1, y + item->device->y1);
376 else
377 cairo_move_to(c, x, y);
379 pango_cairo_update_layout(c, text->layout);
380 pango_cairo_show_layout(c, text->layout);
383 static gboolean
384 calculate_bounds(CrItem *item, cairo_t *c, CrBounds *bounds,
385 CrDeviceBounds *device)
387 CrText *text;
388 double x, y, w, h;
390 text = CR_TEXT(item);
392 if (text->layout)
393 g_object_unref(text->layout);
394 text->layout = NULL;
396 if (text->text) {
397 create_layout(text, c);
400 if (text->flags & CR_TEXT_SCALEABLE) {
401 get_item_extents(text, c, &x, &y, &w, &h);
402 bounds->x1 = x;
403 bounds->y1 = y - h;
404 bounds->x2 = x + w;
405 bounds->y2 = y;
407 else {
408 get_device_extents(text, c, device);
413 return TRUE;
416 static CrItem *
417 test(CrItem *item, cairo_t *c, double x, double y)
419 /* anything in bounds will match. */
420 return item;
423 static void
424 cr_text_init(CrText *text)
426 text->anchor = GTK_ANCHOR_NW;
427 text->flags |= CR_TEXT_SCALEABLE;
430 static void
431 cr_text_class_init(CrTextClass *klass)
433 GObjectClass *object_class;
434 CrItemClass *item_class;
436 object_class = (GObjectClass *) klass;
437 item_class = (CrItemClass *) klass;
439 parent_class = g_type_class_peek_parent (klass);
440 object_class->get_property = cr_text_get_property;
441 object_class->set_property = cr_text_set_property;
442 object_class->dispose = cr_text_dispose;
443 object_class->finalize = cr_text_finalize;
444 item_class->calculate_bounds = calculate_bounds;
445 item_class->report_new_bounds = report_new_bounds;
446 item_class->test = test;
447 item_class->paint = paint;
449 g_object_class_install_property (object_class,
450 PROP_TEXT,
451 g_param_spec_string ("text", NULL,
452 "The text to be drawn.",
453 NULL, G_PARAM_READWRITE));
454 g_object_class_install_property (object_class,
455 PROP_FONT,
456 g_param_spec_string ("font", "Font",
457 "A pango font description string of the form "
458 "[FAMILY-LIST] [STYLE-OPTIONS] [SIZE]",
459 NULL, G_PARAM_READWRITE));
460 g_object_class_install_property
461 (object_class,
462 PROP_WIDTH,
463 g_param_spec_double ("width", "Width",
464 "Width before line-wrap",
465 -G_MAXDOUBLE, G_MAXDOUBLE, 0,
466 G_PARAM_READWRITE));
467 g_object_class_install_property
468 (object_class,
469 PROP_SCALEABLE,
470 g_param_spec_boolean ("scaleable", NULL, "When True the size "
471 " of the text is in item units. When False, the "
472 "size is in device/pixel units. This effects "
473 "x/y_offset as well. See also @CrInverse for "
474 "another way to achieve the same effect.",
475 TRUE,
476 G_PARAM_READWRITE));
477 g_object_class_install_property
478 (object_class,
479 PROP_X_OFFSET,
480 g_param_spec_double ("x_offset", NULL, "A device offset "
481 "from the item's anchor position. Only used when "
482 "scaleable=FALSE.",
483 -G_MAXDOUBLE, G_MAXDOUBLE, 0.0,
484 G_PARAM_READWRITE));
485 g_object_class_install_property
486 (object_class,
487 PROP_Y_OFFSET,
488 g_param_spec_double ("y_offset", NULL, "A device offset "
489 "from the item's anchor position. Only used when "
490 "scaleable=FALSE.",
491 -G_MAXDOUBLE, G_MAXDOUBLE, 0.0,
492 G_PARAM_READWRITE));
494 g_object_class_install_property
495 (object_class,
496 PROP_ANCHOR,
497 g_param_spec_enum ("anchor", "anchor", "The part of the "
498 "text that is referenced to the item's x, y "
499 "coordinates",
500 GTK_TYPE_ANCHOR_TYPE,
501 GTK_ANCHOR_NW,
502 G_PARAM_READWRITE));
503 g_object_class_install_property
504 (object_class,
505 PROP_FILL_COLOR_RGBA,
506 g_param_spec_uint ("fill_color_rgba", NULL,
507 "Text color, red,grn,blue,alpha",
508 0, G_MAXUINT, 0,
509 G_PARAM_READWRITE));
510 g_object_class_install_property
511 (object_class,
512 PROP_FILL_COLOR,
513 g_param_spec_string ("fill_color", "Fill Color",
514 "A string color such as 'red', or '#123456'",
515 NULL, G_PARAM_READWRITE));
516 g_object_class_install_property
517 (object_class,
518 PROP_USE_MARKUP,
519 g_param_spec_boolean ("use_markup", NULL, "If html style "
520 "markup language should be used.",
521 FALSE,
522 G_PARAM_READWRITE));
525 GType
526 cr_text_get_type(void)
528 static GType type = 0;
529 static const GTypeInfo info = {
530 sizeof(CrTextClass),
531 NULL, /*base_init*/
532 NULL, /*base_finalize*/
533 (GClassInitFunc) cr_text_class_init,
534 (GClassFinalizeFunc) NULL,
535 NULL,
536 sizeof(CrText),
538 (GInstanceInitFunc) cr_text_init,
539 NULL
541 if (!type) {
542 type = g_type_register_static(CR_TYPE_ITEM,
543 "CrText", &info, 0);
545 return type;
549 * cr_text_new:
550 * @parent: The parent canvas item.
551 * @x: X position of the text.
552 * @y: Y position of the text.
553 * @text: The text string.
555 * A convenience constructor for creating an text string and adding it to
556 * an item group in one step.
558 * Returns: A reference to a new CrItem. You must call g_object_ref if you
559 * intend to use this reference outside the local scope.
561 CrItem *
562 cr_text_new(CrItem *parent, double x, double y,
563 const char *text, const gchar *first_arg_name, ...)
565 CrItem *item;
566 va_list args;
568 va_start (args, first_arg_name);
569 item = cr_item_construct(parent, CR_TYPE_TEXT, first_arg_name,
570 args);
571 va_end (args);
573 if (item) {
574 g_object_set(item, "x", x, "y", y, "text", text, NULL);
577 return item;