作図ソフト dia の改良版
修订版 | ca1bbf458aec08657c8e687db0c5b33ea8a26343 (tree) |
---|---|
时间 | 2014-10-10 03:01:12 |
作者 | Hans Breuer <hans@breu...> |
Commiter | Hans Breuer |
[transform] text rotation for "Standard - Text"
Rotations is done around the text handle position. This position is
only known to the object implementation. There is new render API
draw_rotated_text() taking the rotation center an angle, which is
much less invasive than making the underlying Text object aware of
angle and vertical alignment (and than not using it almost anywhere).
It is assumed that free text rotation will only be directly supported
by very few objects, so the burden should not be propagated to all
objects with text.
The initial version is should be working for almost all renderers
due to the default implementation of DiaRenderer::draw_rotated_text()
converting the text to path and rotating that.
@@ -37,7 +37,7 @@ object_add_updates(DiaObject *obj, Diagram *dia) | ||
37 | 37 | |
38 | 38 | /* Bounding box */ |
39 | 39 | if (data_object_get_highlight(dia->data,obj) != DIA_HIGHLIGHT_NONE) { |
40 | - diagram_add_update_with_border(dia, &obj->bounding_box, 5); | |
40 | + diagram_add_update_with_border(dia, dia_object_get_enclosing_box (obj), 5); | |
41 | 41 | } else { |
42 | 42 | diagram_add_update(dia, dia_object_get_enclosing_box (obj)); |
43 | 43 | } |
@@ -19,14 +19,15 @@ | ||
19 | 19 | * along with this program; if not, write to the Free Software |
20 | 20 | * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. |
21 | 21 | */ |
22 | - | |
22 | +#include <config.h> | |
23 | 23 | |
24 | 24 | #include "diarenderer.h" |
25 | 25 | #include "object.h" |
26 | 26 | #include "text.h" |
27 | 27 | #include "textline.h" |
28 | 28 | #include "diatransformrenderer.h" |
29 | - | |
29 | +#include "standard-path.h" /* text_to_path */ | |
30 | +#include "boundingbox.h" /* PolyBBextra */ | |
30 | 31 | /* |
31 | 32 | * redefinition of isnan, for portability, as explained in : |
32 | 33 | * http://www.gnu.org/software/autoconf/manual/html_node/Function-Portability.html |
@@ -105,6 +106,8 @@ static void draw_text (DiaRenderer *renderer, | ||
105 | 106 | Text *text); |
106 | 107 | static void draw_text_line (DiaRenderer *renderer, |
107 | 108 | TextLine *text_line, Point *pos, Alignment alignment, Color *color); |
109 | +static void draw_rotated_text (DiaRenderer *renderer, Text *text, | |
110 | + Point *center, real angle); | |
108 | 111 | |
109 | 112 | static void draw_polyline (DiaRenderer *renderer, |
110 | 113 | Point *points, int num_points, |
@@ -335,6 +338,7 @@ dia_renderer_class_init (DiaRendererClass *klass) | ||
335 | 338 | renderer_class->draw_polyline = draw_polyline; |
336 | 339 | renderer_class->draw_text = draw_text; |
337 | 340 | renderer_class->draw_text_line = draw_text_line; |
341 | + renderer_class->draw_rotated_text = draw_rotated_text; | |
338 | 342 | |
339 | 343 | /* highest level functions */ |
340 | 344 | renderer_class->draw_rounded_rect = draw_rounded_rect; |
@@ -602,6 +606,108 @@ draw_text (DiaRenderer *renderer, | ||
602 | 606 | } |
603 | 607 | |
604 | 608 | /*! |
609 | + * \brief Default implementation to draw rotated text | |
610 | + * | |
611 | + * The default implementation converts the given _Text object to a | |
612 | + * path and passes it to draw_beziergon() if the renderer support | |
613 | + * rendering wih holes. If not a fallback implementation is used. | |
614 | + * | |
615 | + * A Renderer with a good own concept of rotated text should | |
616 | + * overwrite it. | |
617 | + * | |
618 | + * \memberof _DiaRenderer | |
619 | + */ | |
620 | +static void | |
621 | +draw_rotated_text (DiaRenderer *renderer, Text *text, | |
622 | + Point *center, real angle) | |
623 | +{ | |
624 | + if (angle == 0.0) { | |
625 | + /* maybe the fallback should also consider center? */ | |
626 | + draw_text (renderer, text); | |
627 | + } else { | |
628 | + GArray *path = g_array_new (FALSE, FALSE, sizeof(BezPoint)); | |
629 | + if (!text_is_empty (text) && text_to_path (text, path)) { | |
630 | + /* Scaling and transformation here */ | |
631 | + Rectangle bz_bb, tx_bb; | |
632 | + PolyBBExtras extra = { 0, }; | |
633 | + real sx, sy; | |
634 | + guint i; | |
635 | + real dx = center ? (text->position.x - center->x) : 0; | |
636 | + real dy = center ? (text->position.y - center->y) : 0; | |
637 | + DiaMatrix m = { 1, 0, 0, 1, 0, 0 }; | |
638 | + DiaMatrix t = { 1, 0, 0, 1, 0, 0 }; | |
639 | + | |
640 | + polybezier_bbox (&g_array_index (path, BezPoint, 0), path->len, &extra, TRUE, &bz_bb); | |
641 | + text_calc_boundingbox (text, &tx_bb); | |
642 | + sx = (tx_bb.right - tx_bb.left) / (bz_bb.right - bz_bb.left); | |
643 | + sy = (tx_bb.bottom - tx_bb.top) / (bz_bb.bottom - bz_bb.top); | |
644 | + | |
645 | + /* move center to origin */ | |
646 | + if (ALIGN_LEFT == text->alignment) | |
647 | + t.x0 = -bz_bb.left; | |
648 | + else if (ALIGN_RIGHT == text->alignment) | |
649 | + t.x0 = - bz_bb.right; | |
650 | + else | |
651 | + t.x0 = -(bz_bb.left + bz_bb.right) / 2.0; | |
652 | + t.x0 -= dx / sx; | |
653 | + t.y0 = - bz_bb.top - (text_get_ascent (text) - dy) / sy; | |
654 | + dia_matrix_set_angle_and_scales (&m, G_PI * angle / 180.0, sx, sx); | |
655 | + dia_matrix_multiply (&m, &t, &m); | |
656 | + /* move back center from origin */ | |
657 | + if (ALIGN_LEFT == text->alignment) | |
658 | + t.x0 = tx_bb.left; | |
659 | + else if (ALIGN_RIGHT == text->alignment) | |
660 | + t.x0 = tx_bb.right; | |
661 | + else | |
662 | + t.x0 = (tx_bb.left + tx_bb.right) / 2.0; | |
663 | + t.x0 += dx; | |
664 | + t.y0 = tx_bb.top + (text_get_ascent (text) - dy); | |
665 | + dia_matrix_multiply (&m, &m, &t); | |
666 | + | |
667 | + for (i = 0; i < path->len; ++i) { | |
668 | + BezPoint *bp = &g_array_index (path, BezPoint, i); | |
669 | + transform_bezpoint (bp, &m); | |
670 | + } | |
671 | + | |
672 | + if (DIA_RENDERER_GET_CLASS (renderer)->is_capable_to(renderer, RENDER_HOLES)) | |
673 | + DIA_RENDERER_GET_CLASS (renderer)->draw_beziergon(renderer, | |
674 | + &g_array_index (path, BezPoint, 0), | |
675 | + path->len, | |
676 | + &text->color, NULL); | |
677 | + else | |
678 | + bezier_render_fill (renderer, | |
679 | + &g_array_index (path, BezPoint, 0), path->len, | |
680 | + &text->color); | |
681 | + } else { | |
682 | + Color magenta = { 1.0, 0.0, 1.0, 1.0 }; | |
683 | + Point pt = center ? *center : text->position; | |
684 | + DiaMatrix m = { 1, 0, 0, 1, pt.x, pt.y }; | |
685 | + DiaMatrix t = { 1, 0, 0, 1, -pt.x, -pt.y }; | |
686 | + Rectangle tb; | |
687 | + Point poly[4]; | |
688 | + int i; | |
689 | + | |
690 | + text_calc_boundingbox (text, &tb); | |
691 | + poly[0].x = tb.left; poly[0].y = tb.top; | |
692 | + poly[1].x = tb.right; poly[1].y = tb.top; | |
693 | + poly[2].x = tb.right; poly[2].y = tb.bottom; | |
694 | + poly[3].x = tb.left; poly[3].y = tb.bottom; | |
695 | + | |
696 | + dia_matrix_set_angle_and_scales (&m, G_PI * angle / 180.0, 1.0, 1.0); | |
697 | + dia_matrix_multiply (&m, &t, &m); | |
698 | + | |
699 | + for (i = 0; i < 4; ++i) | |
700 | + transform_point (&poly[i], &m); | |
701 | + | |
702 | + DIA_RENDERER_GET_CLASS (renderer)->set_linewidth (renderer, 0.0); | |
703 | + DIA_RENDERER_GET_CLASS (renderer)->draw_polygon (renderer, poly, 4, | |
704 | + NULL, &magenta); | |
705 | + } | |
706 | + g_array_free (path, TRUE); | |
707 | + } | |
708 | +} | |
709 | + | |
710 | +/*! | |
605 | 711 | * \brief Default implementation of draw_text_line |
606 | 712 | * |
607 | 713 | * The default implementation of draw_text_line() just calls set_font() and |
@@ -252,6 +252,9 @@ struct _DiaRendererClass | ||
252 | 252 | RenderCapability cap); |
253 | 253 | /*! fill with a pattern, currently only gradient */ |
254 | 254 | void (*set_pattern) (DiaRenderer *renderer, DiaPattern *pat); |
255 | + /*! draw text rotated around center with given angle in degrees */ | |
256 | + void (*draw_rotated_text) (DiaRenderer *renderer, Text *text, | |
257 | + Point *center, real angle); | |
255 | 258 | }; |
256 | 259 | |
257 | 260 | /* |
@@ -744,6 +744,7 @@ EXPORTS | ||
744 | 744 | text_get_ascent |
745 | 745 | text_get_attributes |
746 | 746 | text_get_descent |
747 | + text_get_height | |
747 | 748 | text_get_line |
748 | 749 | text_get_line_strlen |
749 | 750 | text_get_line_width |
@@ -392,6 +392,12 @@ text_set_height(Text *text, real height) | ||
392 | 392 | calc_ascent_descent(text); |
393 | 393 | } |
394 | 394 | |
395 | +real | |
396 | +text_get_height(const Text *text) | |
397 | +{ | |
398 | + return text->height; | |
399 | +} | |
400 | + | |
395 | 401 | void |
396 | 402 | text_set_font(Text *text, DiaFont *font) |
397 | 403 | { |
@@ -77,6 +77,7 @@ gchar *text_get_line(const Text *text, int line); | ||
77 | 77 | char *text_get_string_copy(const Text *text); |
78 | 78 | void text_set_string(Text *text, const char *string); |
79 | 79 | void text_set_height(Text *text, real height); |
80 | +real text_get_height(const Text *text); | |
80 | 81 | void text_set_font(Text *text, DiaFont *font); |
81 | 82 | void text_set_position(Text *text, Point *pos); |
82 | 83 | void text_set_color(Text *text, Color *col); |
@@ -33,6 +33,7 @@ | ||
33 | 33 | #include "properties.h" |
34 | 34 | #include "diamenu.h" |
35 | 35 | #include "create.h" |
36 | +#include "message.h" /* just dia_log_message */ | |
36 | 37 | |
37 | 38 | #include "tool-icons.h" |
38 | 39 |
@@ -66,6 +67,8 @@ struct _Textobj { | ||
66 | 67 | gboolean show_background; |
67 | 68 | /*! margin used for background drawing and connection point placement */ |
68 | 69 | real margin; |
70 | + /*! rotation around text handle position */ | |
71 | + real text_angle; | |
69 | 72 | }; |
70 | 73 | |
71 | 74 | static struct _TextobjProperties { |
@@ -86,6 +89,7 @@ static DiaObject *textobj_create(Point *startpoint, | ||
86 | 89 | void *user_data, |
87 | 90 | Handle **handle1, |
88 | 91 | Handle **handle2); |
92 | +static DiaObject *textobj_copy(Textobj *textobj); | |
89 | 93 | static void textobj_destroy(Textobj *textobj); |
90 | 94 | |
91 | 95 | static void textobj_get_props(Textobj *textobj, GPtrArray *props); |
@@ -95,6 +99,7 @@ static void textobj_save(Textobj *textobj, ObjectNode obj_node, | ||
95 | 99 | DiaContext *ctx); |
96 | 100 | static DiaObject *textobj_load(ObjectNode obj_node, int version, DiaContext *ctx); |
97 | 101 | static DiaMenu *textobj_get_object_menu(Textobj *textobj, Point *clickedpoint); |
102 | +static gboolean textobj_transform(Textobj *textobj, const DiaMatrix *m); | |
98 | 103 | |
99 | 104 | static void textobj_valign_point(Textobj *textobj, Point* p); |
100 | 105 |
@@ -115,6 +120,7 @@ PropEnumData prop_text_vert_align_data[] = { | ||
115 | 120 | { NULL, 0 } |
116 | 121 | }; |
117 | 122 | static PropNumData text_margin_range = { 0.0, 10.0, 0.1 }; |
123 | +static PropNumData text_angle_range = { -180.0, 180.0, 5.0 }; | |
118 | 124 | static PropDescription textobj_props[] = { |
119 | 125 | OBJECT_COMMON_PROPERTIES, |
120 | 126 | PROP_STD_TEXT_ALIGNMENT, |
@@ -123,6 +129,8 @@ static PropDescription textobj_props[] = { | ||
123 | 129 | PROP_STD_TEXT_FONT, |
124 | 130 | PROP_STD_TEXT_HEIGHT, |
125 | 131 | PROP_STD_TEXT_COLOUR, |
132 | + { "text_angle", PROP_TYPE_REAL, PROP_FLAG_VISIBLE | PROP_FLAG_OPTIONAL, | |
133 | + N_("Text angle"), NULL, &text_angle_range }, | |
126 | 134 | PROP_STD_SAVED_TEXT, |
127 | 135 | PROP_STD_FILL_COLOUR_OPTIONAL, |
128 | 136 | PROP_STD_SHOW_BACKGROUND_OPTIONAL, |
@@ -138,6 +146,7 @@ static PropOffset textobj_offsets[] = { | ||
138 | 146 | {PROP_STDNAME_TEXT_HEIGHT,PROP_STDTYPE_TEXT_HEIGHT,offsetof(Textobj,text),offsetof(Text,height)}, |
139 | 147 | {"text_colour",PROP_TYPE_COLOUR,offsetof(Textobj,text),offsetof(Text,color)}, |
140 | 148 | {"text_alignment",PROP_TYPE_ENUM,offsetof(Textobj,text),offsetof(Text,alignment)}, |
149 | + {"text_angle",PROP_TYPE_REAL,offsetof(Textobj,text_angle)}, | |
141 | 150 | {"text_vert_alignment",PROP_TYPE_ENUM,offsetof(Textobj,vert_align)}, |
142 | 151 | { "fill_colour", PROP_TYPE_COLOUR, offsetof(Textobj, fill_color) }, |
143 | 152 | { "show_background", PROP_TYPE_BOOL, offsetof(Textobj, show_background) }, |
@@ -168,7 +177,7 @@ static ObjectOps textobj_ops = { | ||
168 | 177 | (DrawFunc) textobj_draw, |
169 | 178 | (DistanceFunc) textobj_distance_from, |
170 | 179 | (SelectFunc) textobj_select, |
171 | - (CopyFunc) object_copy_using_properties, | |
180 | + (CopyFunc) textobj_copy, | |
172 | 181 | (MoveFunc) textobj_move, |
173 | 182 | (MoveHandleFunc) textobj_move_handle, |
174 | 183 | (GetPropertiesFunc) object_create_props_dialog, |
@@ -179,6 +188,7 @@ static ObjectOps textobj_ops = { | ||
179 | 188 | (SetPropsFunc) textobj_set_props, |
180 | 189 | (TextEditFunc) 0, |
181 | 190 | (ApplyPropertiesListFunc) object_apply_props, |
191 | + (TransformFunc) textobj_transform, | |
182 | 192 | }; |
183 | 193 | |
184 | 194 | static void |
@@ -194,9 +204,47 @@ textobj_set_props(Textobj *textobj, GPtrArray *props) | ||
194 | 204 | textobj_update_data(textobj); |
195 | 205 | } |
196 | 206 | |
207 | +static void | |
208 | +_textobj_get_poly (const Textobj *textobj, Point poly[4]) | |
209 | +{ | |
210 | + Point ul, lr; | |
211 | + Point pt = textobj->text_handle.pos; | |
212 | + Rectangle box; | |
213 | + DiaMatrix m = { 1, 0, 0, 1, pt.x, pt.y }; | |
214 | + DiaMatrix t = { 1, 0, 0, 1, -pt.x, -pt.y }; | |
215 | + int i; | |
216 | + | |
217 | + dia_matrix_set_angle_and_scales (&m, G_PI * textobj->text_angle / 180.0, 1.0, 1.0); | |
218 | + dia_matrix_multiply (&m, &t, &m); | |
219 | + | |
220 | + text_calc_boundingbox (textobj->text, &box); | |
221 | + ul.x = box.left - textobj->margin; | |
222 | + ul.y = box.top - textobj->margin; | |
223 | + lr.x = box.right + textobj->margin; | |
224 | + lr.y = box.bottom + textobj->margin; | |
225 | + | |
226 | + poly[0].x = ul.x; | |
227 | + poly[0].y = ul.y; | |
228 | + poly[1].x = lr.x; | |
229 | + poly[1].y = ul.y; | |
230 | + poly[2].x = lr.x; | |
231 | + poly[2].y = lr.y; | |
232 | + poly[3].x = ul.x; | |
233 | + poly[3].y = lr.y; | |
234 | + | |
235 | + for (i = 0; i < 4; ++i) | |
236 | + transform_point (&poly[i], &m); | |
237 | +} | |
238 | + | |
197 | 239 | static real |
198 | 240 | textobj_distance_from(Textobj *textobj, Point *point) |
199 | 241 | { |
242 | + if (textobj->text_angle != 0) { | |
243 | + Point poly[4]; | |
244 | + | |
245 | + _textobj_get_poly (textobj, poly); | |
246 | + return distance_polygon_point(poly, 4, 0.0, point); | |
247 | + } | |
200 | 248 | if (textobj->show_background) |
201 | 249 | return distance_rectangle_point(&textobj->object.bounding_box, point); |
202 | 250 | return text_distance_from(textobj->text, point); |
@@ -250,9 +298,29 @@ textobj_draw(Textobj *textobj, DiaRenderer *renderer) | ||
250 | 298 | ul.y = box.top - textobj->margin; |
251 | 299 | lr.x = box.right + textobj->margin; |
252 | 300 | lr.y = box.bottom + textobj->margin; |
253 | - DIA_RENDERER_GET_CLASS (renderer)->draw_rect (renderer, &ul, &lr, &textobj->fill_color, NULL); | |
301 | + if (textobj->text_angle == 0) { | |
302 | + DIA_RENDERER_GET_CLASS (renderer)->draw_rect (renderer, &ul, &lr, &textobj->fill_color, NULL); | |
303 | + } else { | |
304 | + Point poly[4]; | |
305 | + | |
306 | + _textobj_get_poly (textobj, poly); | |
307 | + DIA_RENDERER_GET_CLASS (renderer)->draw_polygon (renderer, poly, 4, &textobj->fill_color, NULL); | |
308 | + } | |
309 | + } | |
310 | + if (textobj->text_angle == 0) { | |
311 | + text_draw(textobj->text, renderer); | |
312 | + } else { | |
313 | + DIA_RENDERER_GET_CLASS (renderer)->draw_rotated_text (renderer, textobj->text, | |
314 | + &textobj->text_handle.pos, textobj->text_angle); | |
315 | + /* XXX: interactive case not working correctly */ | |
316 | + if (renderer->is_interactive && | |
317 | + dia_object_is_selected(&textobj->object) && | |
318 | + textobj->text->focus.has_focus) { | |
319 | + /* editing is not rotated */ | |
320 | + text_draw(textobj->text, renderer); | |
321 | + } | |
322 | + | |
254 | 323 | } |
255 | - text_draw(textobj->text, renderer); | |
256 | 324 | } |
257 | 325 | |
258 | 326 | static void |
@@ -282,6 +350,7 @@ textobj_update_data(Textobj *textobj) | ||
282 | 350 | { |
283 | 351 | Point to2; |
284 | 352 | DiaObject *obj = &textobj->object; |
353 | + Rectangle tx_bb; | |
285 | 354 | |
286 | 355 | text_set_position(textobj->text, &obj->position); |
287 | 356 | text_calc_boundingbox(textobj->text, &obj->bounding_box); |
@@ -298,7 +367,9 @@ textobj_update_data(Textobj *textobj) | ||
298 | 367 | else if (ALIGN_RIGHT == textobj->text->alignment) |
299 | 368 | to2.x -= textobj->margin; /* left */ |
300 | 369 | text_set_position(textobj->text, &to2); |
301 | - text_calc_boundingbox(textobj->text, &obj->bounding_box); | |
370 | + | |
371 | + /* always use the unrotated box ... */ | |
372 | + text_calc_boundingbox(textobj->text, &tx_bb); | |
302 | 373 | /* grow the bounding box by 2x margin */ |
303 | 374 | obj->bounding_box.top -= textobj->margin; |
304 | 375 | obj->bounding_box.left -= textobj->margin; |
@@ -306,6 +377,28 @@ textobj_update_data(Textobj *textobj) | ||
306 | 377 | obj->bounding_box.right += textobj->margin; |
307 | 378 | |
308 | 379 | textobj->text_handle.pos = obj->position; |
380 | + if (textobj->text_angle == 0) { | |
381 | + obj->bounding_box = tx_bb; | |
382 | + g_return_if_fail (obj->enclosing_box != NULL); | |
383 | + *obj->enclosing_box = tx_bb; | |
384 | + } else { | |
385 | + /* ... and grow it when necessary */ | |
386 | + Point poly[4]; | |
387 | + int i; | |
388 | + | |
389 | + _textobj_get_poly (textobj, poly); | |
390 | + /* we don't want the joined box for boundingbox because | |
391 | + * selection would become too greedy than. | |
392 | + */ | |
393 | + obj->bounding_box.left = obj->bounding_box.right = poly[0].x; | |
394 | + obj->bounding_box.top = obj->bounding_box.bottom = poly[0].y; | |
395 | + for (i = 1; i < 4; ++i) | |
396 | + rectangle_add_point (&obj->bounding_box, &poly[i]); | |
397 | + g_return_if_fail (obj->enclosing_box != NULL); | |
398 | + *obj->enclosing_box = obj->bounding_box; | |
399 | + /* join for editing/selection bbox */ | |
400 | + rectangle_union (obj->enclosing_box, &tx_bb); | |
401 | + } | |
309 | 402 | } |
310 | 403 | |
311 | 404 | static DiaObject * |
@@ -322,7 +415,8 @@ textobj_create(Point *startpoint, | ||
322 | 415 | |
323 | 416 | textobj = g_malloc0(sizeof(Textobj)); |
324 | 417 | obj = &textobj->object; |
325 | - | |
418 | + obj->enclosing_box = g_new0 (Rectangle, 1); | |
419 | + | |
326 | 420 | obj->type = &textobj_type; |
327 | 421 | |
328 | 422 | obj->ops = &textobj_ops; |
@@ -358,10 +452,21 @@ textobj_create(Point *startpoint, | ||
358 | 452 | return &textobj->object; |
359 | 453 | } |
360 | 454 | |
455 | +static DiaObject * | |
456 | +textobj_copy(Textobj *textobj) | |
457 | +{ | |
458 | + Textobj *copied = (Textobj *)object_copy_using_properties(&textobj->object); | |
459 | + copied->object.enclosing_box = g_new (Rectangle, 1); | |
460 | + *copied->object.enclosing_box = *textobj->object.enclosing_box; | |
461 | + return &copied->object; | |
462 | +} | |
463 | + | |
361 | 464 | static void |
362 | 465 | textobj_destroy(Textobj *textobj) |
363 | 466 | { |
364 | 467 | text_destroy(textobj->text); |
468 | + g_free (textobj->object.enclosing_box); | |
469 | + textobj->object.enclosing_box = NULL; | |
365 | 470 | object_destroy(&textobj->object); |
366 | 471 | } |
367 | 472 |
@@ -381,6 +486,8 @@ textobj_save(Textobj *textobj, ObjectNode obj_node, DiaContext *ctx) | ||
381 | 486 | } |
382 | 487 | if (textobj->margin > 0.0) |
383 | 488 | data_add_real(new_attribute(obj_node, "margin"), textobj->margin, ctx); |
489 | + if (textobj->text_angle != 0.0) | |
490 | + data_add_real(new_attribute(obj_node, "text_angle"), textobj->text_angle, ctx); | |
384 | 491 | } |
385 | 492 | |
386 | 493 | static DiaObject * |
@@ -393,6 +500,7 @@ textobj_load(ObjectNode obj_node, int version, DiaContext *ctx) | ||
393 | 500 | |
394 | 501 | textobj = g_malloc0(sizeof(Textobj)); |
395 | 502 | obj = &textobj->object; |
503 | + obj->enclosing_box = g_new0(Rectangle,1); | |
396 | 504 | |
397 | 505 | obj->type = &textobj_type; |
398 | 506 | obj->ops = &textobj_ops; |
@@ -415,6 +523,9 @@ textobj_load(ObjectNode obj_node, int version, DiaContext *ctx) | ||
415 | 523 | else if (version == 0) { |
416 | 524 | textobj->vert_align = VALIGN_FIRST_LINE; |
417 | 525 | } |
526 | + attr = object_find_attribute(obj_node, "text_angle"); | |
527 | + if (attr != NULL) | |
528 | + textobj->text_angle = data_real(attribute_first_data(attr), ctx); | |
418 | 529 | |
419 | 530 | /* default visibility must be off to keep compatibility */ |
420 | 531 | textobj->fill_color = attributes_get_background(); |
@@ -489,3 +600,29 @@ textobj_get_object_menu(Textobj *textobj, Point *clickedpoint) | ||
489 | 600 | |
490 | 601 | return &textobj_menu; |
491 | 602 | } |
603 | +static gboolean | |
604 | +textobj_transform(Textobj *textobj, const DiaMatrix *m) | |
605 | +{ | |
606 | + real a, sx, sy; | |
607 | + | |
608 | + g_return_val_if_fail(m != NULL, FALSE); | |
609 | + | |
610 | + if (!dia_matrix_get_angle_and_scales (m, &a, &sx, &sy)) { | |
611 | + dia_log_message ("textobj_transform() can't convert given matrix"); | |
612 | + return FALSE; | |
613 | + } else { | |
614 | + /* XXX: what to do if width!=height */ | |
615 | + real height = text_get_height (textobj->text) * MIN(sx,sy); | |
616 | + real angle = a*180/G_PI; | |
617 | + Point p = textobj->object.position; | |
618 | + | |
619 | + /* rotation is invariant to the handle position */ | |
620 | + transform_point (&p, m); | |
621 | + text_set_height (textobj->text, height); | |
622 | + textobj->text_angle = angle; | |
623 | + textobj->object.position = p; | |
624 | + } | |
625 | + | |
626 | + textobj_update_data(textobj); | |
627 | + return TRUE; | |
628 | +} |