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.
26 * @short_description: A text canvas item that uses #PangoLayout.
28 * This canvas item can render #PangoLayout.
31 static GObjectClass
*parent_class
= NULL
;
48 cr_text_dispose(GObject
*object
)
50 parent_class
->dispose(object
);
54 cr_text_finalize(GObject
*object
)
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
);
67 cr_text_set_property(GObject
*object
, guint property_id
,
68 const GValue
*value
, GParamSpec
*pspec
)
71 GdkColor color
= { 0, 0, 0, 0, };
72 CrText
*text
= (CrText
*) object
;
74 switch (property_id
) {
76 text
->x_offset
= g_value_get_double (value
);
77 cr_item_request_update(CR_ITEM(text
));
80 text
->y_offset
= g_value_get_double (value
);
81 cr_item_request_update(CR_ITEM(text
));
84 text
->width
= g_value_get_double (value
);
85 cr_item_request_update(CR_ITEM(text
));
88 if (g_value_get_boolean(value
))
89 text
->flags
|= CR_TEXT_SCALEABLE
;
91 text
->flags
&= ~CR_TEXT_SCALEABLE
;
92 cr_item_request_update(CR_ITEM(text
));
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
));
102 text
->anchor
= g_value_get_enum (value
);
103 cr_item_request_update(CR_ITEM(text
));
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
));
110 case PROP_FILL_COLOR
:
111 str
= g_value_get_string(value
);
113 gdk_color_parse(str
, &color
);
114 text
->fill_color_rgba
= ((color
.red
& 0xff00) << 16 |
115 (color
.green
& 0xff00) << 8 |
116 (color
.blue
& 0xff00) |
118 text
->flags
|= CR_TEXT_FILL_COLOR_RGBA
;
119 cr_item_request_update(CR_ITEM(text
));
122 text
->text
= g_value_dup_string (value
);
123 cr_item_request_update(CR_ITEM(text
));
125 case PROP_USE_MARKUP
:
126 if (g_value_get_boolean(value
))
127 text
->flags
|= CR_TEXT_USE_MARKUP
;
129 text
->flags
&= ~CR_TEXT_USE_MARKUP
;
132 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
,
138 cr_text_get_property(GObject
*object
, guint property_id
,
139 GValue
*value
, GParamSpec
*pspec
)
141 CrText
*text
= (CrText
*) object
;
143 char color_string
[8];
145 switch (property_id
) {
147 g_value_set_double (value
, text
->x_offset
);
150 g_value_set_double (value
, text
->y_offset
);
153 g_value_set_double (value
, text
->width
);
156 g_value_set_boolean(value
,
157 text
->flags
& CR_TEXT_SCALEABLE
);
160 if (text
->font_desc
) {
161 str
= pango_font_description_to_string(
163 g_value_set_string (value
, str
);
167 g_value_set_string(value
, NULL
);
170 g_value_set_enum (value
, text
->anchor
);
172 case PROP_FILL_COLOR_RGBA
:
173 g_value_set_uint(value
, text
->fill_color_rgba
);
175 case PROP_FILL_COLOR
:
176 sprintf(color_string
, "#%.6x",
177 text
->fill_color_rgba
>> 8);
178 g_value_set_string(value
, color_string
);
181 g_value_set_string (value
, text
->text
);
183 case PROP_USE_MARKUP
:
184 g_value_set_boolean(value
,
185 text
->flags
& CR_TEXT_USE_MARKUP
);
188 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
,
194 create_layout(CrText
*text
, cairo_t
*c
)
197 text
->layout
= pango_cairo_create_layout(c
);
200 pango_layout_set_font_description(text
->layout
,
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);
209 pango_layout_set_text(text
->layout
, text
->text
, -1);
213 get_device_extents(CrText
*text
, cairo_t
*c
, CrDeviceBounds
*device
)
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
;
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
) {
235 case GTK_ANCHOR_CENTER
:
237 device
->x1
= -w
/2 + text
->x_offset
;
238 device
->x2
= w
/2 + text
->x_offset
;
243 device
->x1
= -w
+ text
->x_offset
;
244 device
->x2
= 0 + text
->x_offset
;
249 device
->x1
= 0 + text
->x_offset
;
250 device
->x2
= w
+ text
->x_offset
;
253 switch (text
->anchor
) {
255 case GTK_ANCHOR_CENTER
:
257 device
->y1
= -h
/2 + text
->y_offset
;
258 device
->y2
= h
/2 + text
->y_offset
;
263 device
->y1
= -h
+ text
->y_offset
;
264 device
->y2
= 0 + text
->y_offset
;
269 device
->y1
= 0 + text
->y_offset
;
270 device
->y2
= h
+ text
->y_offset
;
276 get_item_extents(CrText
*text
, cairo_t
*c
,
277 double *x
, double *y
, double *w
, double *h
)
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
) {
290 case GTK_ANCHOR_CENTER
:
301 switch (text
->anchor
) {
308 case GTK_ANCHOR_CENTER
:
316 report_new_bounds(CrItem
*item
, cairo_t
*ct
, gboolean all
)
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
,
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
);
341 paint(CrItem
*item
, cairo_t
*c
)
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.;
357 cairo_set_source_rgb(c
, r
, g
, b
);
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);
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
);
377 cairo_move_to(c
, x
, y
);
379 pango_cairo_update_layout(c
, text
->layout
);
380 pango_cairo_show_layout(c
, text
->layout
);
384 calculate_bounds(CrItem
*item
, cairo_t
*c
, CrBounds
*bounds
,
385 CrDeviceBounds
*device
)
390 text
= CR_TEXT(item
);
393 g_object_unref(text
->layout
);
397 create_layout(text
, c
);
400 if (text
->flags
& CR_TEXT_SCALEABLE
) {
401 get_item_extents(text
, c
, &x
, &y
, &w
, &h
);
408 get_device_extents(text
, c
, device
);
417 test(CrItem
*item
, cairo_t
*c
, double x
, double y
)
419 /* anything in bounds will match. */
424 cr_text_init(CrText
*text
)
426 text
->anchor
= GTK_ANCHOR_NW
;
427 text
->flags
|= CR_TEXT_SCALEABLE
;
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
,
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
,
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
463 g_param_spec_double ("width", "Width",
464 "Width before line-wrap",
465 -G_MAXDOUBLE
, G_MAXDOUBLE
, 0,
467 g_object_class_install_property
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.",
477 g_object_class_install_property
480 g_param_spec_double ("x_offset", NULL
, "A device offset "
481 "from the item's anchor position. Only used when "
483 -G_MAXDOUBLE
, G_MAXDOUBLE
, 0.0,
485 g_object_class_install_property
488 g_param_spec_double ("y_offset", NULL
, "A device offset "
489 "from the item's anchor position. Only used when "
491 -G_MAXDOUBLE
, G_MAXDOUBLE
, 0.0,
494 g_object_class_install_property
497 g_param_spec_enum ("anchor", "anchor", "The part of the "
498 "text that is referenced to the item's x, y "
500 GTK_TYPE_ANCHOR_TYPE
,
503 g_object_class_install_property
505 PROP_FILL_COLOR_RGBA
,
506 g_param_spec_uint ("fill_color_rgba", NULL
,
507 "Text color, red,grn,blue,alpha",
510 g_object_class_install_property
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
519 g_param_spec_boolean ("use_markup", NULL
, "If html style "
520 "markup language should be used.",
526 cr_text_get_type(void)
528 static GType type
= 0;
529 static const GTypeInfo info
= {
532 NULL
, /*base_finalize*/
533 (GClassInitFunc
) cr_text_class_init
,
534 (GClassFinalizeFunc
) NULL
,
538 (GInstanceInitFunc
) cr_text_init
,
542 type
= g_type_register_static(CR_TYPE_ITEM
,
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.
562 cr_text_new(CrItem
*parent
, double x
, double y
,
563 const char *text
, const gchar
*first_arg_name
, ...)
568 va_start (args
, first_arg_name
);
569 item
= cr_item_construct(parent
, CR_TYPE_TEXT
, first_arg_name
,
574 g_object_set(item
, "x", x
, "y", y
, "text", text
, NULL
);