1 /* Dia -- an diagram creation/manipulation program
2 * Copyright (C) 1998 Alexander Larsson
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
9 * This program 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
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
26 #include <gdk/gdkkeysyms.h>
28 #include "propinternals.h"
31 #include "diarenderer.h"
32 #include "diagramdata.h"
33 #include "objchange.h"
36 static void text_line_dirty_cache(TextLine
*text_line
);
37 static void text_line_cache_values(TextLine
*text_line
);
39 /** Sets this object to display a particular string.
40 * @param text_line The object to change.
41 * @param string The string to display. This string will be copied.
44 text_line_set_string(TextLine
*text_line
, const gchar
*string
)
46 if (text_line
->chars
== NULL
||
47 strcmp(text_line
->chars
, string
)) {
48 if (text_line
->chars
!= NULL
) {
49 g_free(text_line
->chars
);
52 text_line
->chars
= g_strdup(string
);
54 text_line_dirty_cache(text_line
);
58 /** Sets the font used by this object.
59 * @param text_line The object to change.
60 * @param font The font to use for displaying this object.
63 text_line_set_font(TextLine
*text_line
, DiaFont
*font
)
65 if (text_line
->font
!= font
) {
66 DiaFont
*old_font
= text_line
->font
;
68 text_line
->font
= font
;
69 if (old_font
!= NULL
) {
70 dia_font_unref(old_font
);
72 text_line_dirty_cache(text_line
);
76 /** Sets the font height used by this object.
77 * @param text_line The object to change.
78 * @param height The font height to use for displaying this object
79 * (in cm, from baseline to baseline)
82 text_line_set_height(TextLine
*text_line
, real height
)
84 if (fabs(text_line
->height
- height
) > 0.00001) {
85 text_line
->height
= height
;
86 text_line_dirty_cache(text_line
);
90 /** Creates a new TextLine object from its components.
91 * @param string the string to display
92 * @param font the font to display the string with.
93 * @param height the height of the font, in cm from baseline to baseline.
96 text_line_new(const gchar
*string
, DiaFont
*font
, real height
)
98 TextLine
*text_line
= g_new0(TextLine
, 1);
100 text_line_set_string(text_line
, string
);
101 text_line_set_font(text_line
, font
);
102 text_line_set_height(text_line
, height
);
108 text_line_copy(const TextLine
*text_line
)
110 return text_line_new(text_line
->chars
, text_line
->font
, text_line
->height
);
113 /** Destroy a text_line object, deallocating all memory used and unreffing
115 * @param text_line the object to kill.
118 text_line_destroy(TextLine
*text_line
)
120 if (text_line
->chars
!= NULL
) {
121 g_free(text_line
->chars
);
123 if (text_line
->font
!= NULL
) {
124 dia_font_unref(text_line
->font
);
126 /* TODO: Handle renderer's cached content. */
130 /** Calculate the bounding box size of this object. Since a text object has no
131 * position or alignment, this collapses to just a size.
133 * @param size A place to store the width and height of the text.
136 text_line_calc_boundingbox_size(TextLine
*text_line
, Point
*size
)
138 text_line_cache_values(text_line
);
140 size
->x
= text_line
->width
;
141 size
->y
= text_line
->ascent
+ text_line
->descent
;
144 /** Draw a line of text at a given position and in a given color.
151 text_line_draw(DiaRenderer
*renderer
, TextLine
*text_line
,
152 Point
*pos
, Color
*color
)
154 DIA_RENDERER_GET_CLASS(renderer
)->draw_text_line(renderer
, text_line
,
159 text_line_get_string(const TextLine
*text_line
)
161 return text_line
->chars
;
165 text_line_get_font(const TextLine
*text_line
)
167 return text_line
->font
;
171 text_line_get_height(const TextLine
*text_line
)
173 return text_line
->height
;
177 text_line_get_width(TextLine
*text_line
)
179 text_line_cache_values(text_line
);
180 return text_line
->width
;
184 text_line_get_ascent(TextLine
*text_line
)
186 text_line_cache_values(text_line
);
187 return text_line
->ascent
;
191 text_line_get_descent(TextLine
*text_line
)
193 text_line_cache_values(text_line
);
194 return text_line
->descent
;
197 /** Set some cache data for the renderer object. This data will get
198 * freed if this textline is ever changed or viewed at a different size.
199 * Any cache already set, regardless of identity, will be freed.
202 * @param free_func A function for freeing the data.
203 * @param scale The zooming scale factor (as defined by the renderer) used
204 * to make these data. If scale independent, just use 0.0.
208 text_line_set_renderer_cache(TextLine
*text_line
, DiaRenderer
*renderer
,
209 RendererCacheFreeFunc free_func
, real scale
,
211 RendererCache
*cache
;
212 if (text_line
->renderer_cache
!= NULL
) {
213 (*text_line
->renderer_cache
->free_func
)(text_line
->renderer_cache
);
214 text_line
->renderer_cache
= NULL
;
216 cache
= g_new(RendererCache
, 1);
217 cache
->renderer
= renderer
;
218 cache
->free_func
= free_func
;
219 cache
->scale
= scale
;
223 /** Get any renderer cache data that might be around.
226 * @param scale The scale we want text rendered at, or 0.0 if this renderer
227 * is scale independent.
228 * @returns Previously cached data (which shouldn't be freed) for the
229 * same text rendering.
232 text_line_get_renderer_cache(TextLine
*text_line
, DiaRenderer
*renderer
,
234 if (text_line
->clean
&& text_line
->renderer_cache
!= NULL
&&
235 text_line
->renderer_cache
->renderer
== renderer
&&
236 fabs(text_line
->renderer_cache
->scale
- scale
) < 0.0000001) {
237 return text_line
->renderer_cache
->data
;
243 /** Return the amount this text line would need to be shifted in order to
244 * implement the given alignment.
245 * @param text_line a line of text
246 * @param alignment how to align it.
247 * @returns The amount (in diagram lengths) to shift the x positiion of
248 * rendering this such that it looks aligned when printed with x at the left.
249 * Always a positive number.
252 text_line_get_alignment_adjustment(TextLine
*text_line
, Alignment alignment
)
254 text_line_cache_values(text_line
);
257 return text_line
->width
/ 2;
259 return text_line
->width
;
265 /* **** Private functions **** */
266 /** Mark this object as needing update before usage.
267 * @param text_line the object that has changed.
270 text_line_dirty_cache(TextLine
*text_line
)
272 text_line
->clean
= FALSE
;
276 text_line_cache_values(TextLine
*text_line
)
278 if (!text_line
->clean
||
279 text_line
->chars
!= text_line
->chars_cache
||
280 text_line
->font
!= text_line
->font_cache
||
281 text_line
->height
!= text_line
->height_cache
) {
284 if (text_line
->offsets
!= NULL
) {
285 g_free(text_line
->offsets
);
286 text_line
->offsets
= NULL
;
288 if (text_line
->renderer_cache
!= NULL
) {
289 (*text_line
->renderer_cache
->free_func
)(text_line
->renderer_cache
);
290 text_line
->renderer_cache
= NULL
;
292 if (text_line
->layout_offsets
!= NULL
) {
293 GSList
*runs
= text_line
->layout_offsets
->runs
;
295 for (; runs
!= NULL
; runs
= g_slist_next(runs
)) {
296 PangoGlyphItem
*run
= (PangoGlyphItem
*) runs
->data
;
299 g_free(run
->glyphs
->glyphs
);
303 g_free(text_line
->layout_offsets
);
304 text_line
->layout_offsets
= NULL
;
307 if (text_line
->chars
== NULL
||
308 text_line
->chars
[0] == '\0') {
309 text_line
->offsets
= g_new(real
, 0);
310 text_line
->layout_offsets
= NULL
;
311 text_line
->ascent
= text_line
->height
* .5;
312 text_line
->descent
= text_line
->height
* .5;
313 text_line
->width
= 0;
316 dia_font_get_sizes(text_line
->chars
, text_line
->font
, text_line
->height
,
317 &text_line
->width
, &text_line
->ascent
,
318 &text_line
->descent
, &n_offsets
,
319 &text_line
->layout_offsets
);
321 text_line
->clean
= TRUE
;
322 text_line
->chars_cache
= text_line
->chars
;
323 text_line
->font_cache
= text_line
->font
;
324 text_line
->height_cache
= text_line
->height
;
328 /** Adjust a line of glyphs to match the sizes stored in the TextLine
329 * @param line The TextLine object that corresponds to the glyphs.
330 * @param glyphs The one set of glyphs contained in layout created for
331 * this TextLine during rendering. The values in here will be changed.
332 * @param scale The relative height of the font in glyphs.
335 text_line_adjust_glyphs(TextLine
*line
, PangoGlyphString
*glyphs
, real scale
)
339 for (i
= 0; i
< glyphs
->num_glyphs
; i
++) {
341 printf("Glyph %d: width %d, offset %f, textwidth %f\n",
342 i, new_glyphs->glyphs[i].geometry.width, line->offsets[i],
343 line->offsets[i] * scale * 20.0 * PANGO_SCALE);
345 glyphs
->glyphs
[i
].geometry
.width
=
346 (int)(line
->offsets
[i
] * scale
* 20.0 * PANGO_SCALE
);
350 /** Adjust a layout line to match the more fine-grained values stored in the
351 * textline. This circumvents the rounding errors in Pango and ensures a
352 * linear scaling for zooming and export filters.
353 * @param line The TextLine object that corresponds to the glyphs.
354 * @param layoutline The one set of glyphs contained in the TextLine's layout.
355 * @param scale The relative height of the font in glyphs.
356 * @return An adjusted glyphstring, which should be freed by the caller.
359 text_line_adjust_layout_line(TextLine
*line
, PangoLayoutLine
*layoutline
,
362 GSList
*layoutruns
= layoutline
->runs
;
365 if (line
->layout_offsets
== NULL
) {
369 runs
= line
->layout_offsets
->runs
;
371 if (g_slist_length(runs
) != g_slist_length(layoutruns
)) {
372 printf("Runs length error: %d != %d\n",
373 g_slist_length(line
->layout_offsets
->runs
),
374 g_slist_length(layoutline
->runs
));
376 for (; runs
!= NULL
&& layoutruns
!= NULL
; runs
= g_slist_next(runs
),
377 layoutruns
= g_slist_next(layoutruns
)) {
378 PangoGlyphString
*glyphs
= ((PangoLayoutRun
*) runs
->data
)->glyphs
;
379 PangoGlyphString
*layoutglyphs
=
380 ((PangoLayoutRun
*) layoutruns
->data
)->glyphs
;
383 for (i
= 0; i
< glyphs
->num_glyphs
&& i
< layoutglyphs
->num_glyphs
; i
++) {
384 layoutglyphs
->glyphs
[i
].geometry
.width
=
385 (int)(glyphs
->glyphs
[i
].geometry
.width
* scale
/ 20.0);
386 layoutglyphs
->glyphs
[i
].geometry
.x_offset
=
387 (int)(glyphs
->glyphs
[i
].geometry
.x_offset
* scale
/ 20.0);
388 layoutglyphs
->glyphs
[i
].geometry
.y_offset
=
389 (int)(glyphs
->glyphs
[i
].geometry
.y_offset
* scale
/ 20.0);
391 if (glyphs
->num_glyphs
!= layoutglyphs
->num_glyphs
) {
392 printf("Glyph length error: %d != %d\n",
393 glyphs
->num_glyphs
, layoutglyphs
->num_glyphs
);