1 /* gEDA - GPL Electronic Design Automation
2 * libgeda - gEDA's library
3 * Copyright (C) 1998-2007 Ales Hvezda
4 * Copyright (C) 1998-2007 gEDA Contributors (see ChangeLog for details)
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111 USA
21 /*! \file o_text_basic.c
22 * \brief functions for the text and fonts
24 * \par The font definitions
26 * Each letter of the font is defined in a single font symbol file. In
27 * the font symbol file, the character width is defined in the second
28 * line. The first line contains the file format version.
30 * All remaining lines are basic graphical lines. They build the
31 * appearance of the character.
33 * \image html o_text_font_overview.png
34 * \image latex o_text_font_overview.pdf "font overview" width=14cm
36 * The height of capital characters in the font files is 26. The size
37 * of small letters is 16. The space below the zero line is used by
38 * characters like <b>g</b>, <b>p</b> or <b>q</b>. The space above 26
39 * is used by diacritic marks like accents, breve, circumflex mostly in
40 * european characters.
42 * When loading a font definition the basic line objects are stored in
43 * <b>OBJECT->font_prim_objs</b> as a list of OBJECTs.
45 * All font objects are stored in the hash table #font_loaded when they
48 * \par The text definitions
50 * The text is stored and printed in several different representations.
52 * In the gEDA files the text is just a string. It is stored unmodified
53 * in <b>OBJECT->text->string</b>.
55 * If the string is an attribute with an equal sign as delimiter between
56 * an attribute name and an attribute value, then it is possible to
57 * hide some parts of the text. The still visible part of an attribute
58 * is stored in <b>OBJECT->text->disp_string</b>.
60 * \image html o_text_text_overview.png
61 * \image latex o_text_text_overview.pdf "text overview" width=14cm
63 * To draw the text in gschem, the string is interpreted and converted
64 * to a list of basic graphical objects. The basic line objects are
65 * collected from the font character objects.
66 * All basic graphical objects are stored in
67 * <b>OBJECT->text->prim_objs</b>.
83 #include "libgeda_priv.h"
85 #ifdef HAVE_LIBDMALLOC
89 static void o_text_create_string(TOPLEVEL
*toplevel
, OBJECT
*object
, char *string
, int size
, int color
, int x
, int y
, int alignment
, int angle
);
90 static void o_text_recalc(OBJECT
*o_current
);
91 static enum visit_result
92 o_text_visit(OBJECT
*o_current
,
93 enum visit_result (*fn
)(OBJECT
*, void *),
95 enum visit_order order
, int depth
);
96 static void o_text_destroy(OBJECT
*o_current
);
97 static OBJECT
*o_text_copy(TOPLEVEL
*toplevel
, OBJECT
*o_current
);
99 /*! Default setting for text draw function. */
100 void (*text_draw_func
)() = NULL
;
102 /*! Hashtable storing font_character (string) as a key, and pointer to data
104 * This table stays global, thus all functions can access it.
106 GHashTable
*font_loaded
= NULL
;
108 /*! Hashtable storing mapping between character and font definition file
110 * This table stays global, thus all functions can access it.
112 GHashTable
*font_char_to_file
= NULL
;
114 /*! Size of a tab in characters */
115 int tab_in_chars
= 8;
118 /*! \brief update the visible part of a string
119 * \par Function Description
120 * If a string is an attribute, then it is possible to hide
121 * the name or the value part of the attribute string.
122 * This functions updates the text->disp_string according
123 * to the object->show_name_value settings
125 * \param [in] o The OBJECT to update
127 static void update_disp_string(OBJECT
*o
)
131 TEXT
*text
= o
->text
;
133 g_free (text
->disp_string
);
135 if (o_attrib_get_name_value (text
->string
, &name
, &value
)) {
136 switch (o
->show_name_value
) {
137 case (SHOW_NAME_VALUE
):
138 text
->disp_string
= g_strdup (text
->string
);
142 text
->disp_string
= g_strdup (name
);
146 text
->disp_string
= g_strdup(value
);
149 /* free the strings allocated by o_attrib_get_name_value */
153 text
->disp_string
= g_strdup (text
->string
);
157 /*! \brief calculate and return the boundaries of a text object
158 * \par Function Description
159 * This function calculates the object boudaries of a text \a object.
161 * \param [in] toplevel The TOPLEVEL object.
162 * \param [in] o_current a text object
163 * \param [out] left the left world coord
164 * \param [out] top the top world coord
165 * \param [out] right the right world coord
166 * \param [out] bottom the bottom world coord
168 int world_get_text_bounds(OBJECT
*o_current
, int *left
,
169 int *top
, int *right
, int *bottom
)
171 return world_get_object_list_bounds(o_current
->text
->prim_objs
, LIST_KIND_HEAD
,
172 left
, top
, right
, bottom
);
175 /*! \brief create a new text head object
176 * \par Function Description
177 * This function creates a <b>text_head</b> OBJECT. This OBJECT
178 * is just a special empty object. This object is never modified.
180 * \return new text head OBJECT
182 OBJECT
*o_text_new_head(TOPLEVEL
*toplevel
)
184 OBJECT
*new_node
=NULL
;
186 new_node
= s_toplevel_new_object(toplevel
, OBJ_HEAD
, "text_head");
188 /* don't need to do this for head nodes */
189 /* ret = s_basic_link_object(new_node, NULL);*/
193 /*! \brief initialize the hash tables for the fonts
194 * \par Function Description
195 * This function initializes the two global hash tables <b>font_loaded</b>
196 * and <b>font_char_to_file</b> that are used to store the fonts characters.
198 void o_text_init(void)
200 if (font_loaded
== NULL
) {
201 font_loaded
= g_hash_table_new_full (g_direct_hash
,
206 fprintf (stderr
, "o_text_init: Tried to initialize an already initialized font_loaded hash table!!\n");
208 if (font_char_to_file
== NULL
) {
209 font_char_to_file
= g_hash_table_new_full (g_direct_hash
,
215 fprintf (stderr
, "o_text_init: Tried to initialize an already initialized font_char_to_file hash table!!\n");
222 /*! \brief print informations about some characters
224 * This is a debugging function. Do not use it in regular code.
226 void o_text_print_set(void)
228 OBJECT
*o_current
, *o_font_set
;
231 for (i
= 'A' ; i
< 'Z'+1; i
++) {
232 o_font_set
= g_hash_table_lookup (font_loaded
,
233 GUINT_TO_POINTER ((gunichar
)i
));
234 if (o_font_set
!= NULL
) {
235 printf("%c: LOADED\n", i
);
236 for (o_current
=return_tail(o_font_set
->font_prim_objs
); o_current
;
237 o_current
=o_current
->prev
)
239 printf(" %s\n", o_current
->name
);
242 printf("%c: unloaded\n", i
);
247 /*! \brief load a font character into an object
248 * \par Function Description
249 * This function loads a character form a font symbol file.
251 * \param [in] toplevel The TOPLEVEL object
252 * \param [in] needed_char unicode character to load
253 * return a character OBJECT
255 OBJECT
*o_text_load_font(TOPLEVEL
*toplevel
, gunichar needed_char
)
257 gchar
*temp_string
= NULL
;
259 int not_found
= FALSE
;
263 /* retrieve the name of the file where the char is defined */
264 aux_str2
= g_hash_table_lookup (font_char_to_file
,
265 GUINT_TO_POINTER (needed_char
));
266 if (aux_str2
== NULL
) {
267 /* this is needed since WinNT file systems are
268 * case insensitive, and cannot tell the difference
269 * between A.sym and a.sym. So we create a_.sym -
270 * z_.sym, this loads up the chars
272 if (needed_char
>= 'a' && needed_char
<= 'z') {
273 temp_string
= g_strdup_printf("%s%c%c_.sym",
274 toplevel
->font_directory
, G_DIR_SEPARATOR
,
277 temp_string
= g_strdup_printf("%s%c%c.sym",
278 toplevel
->font_directory
, G_DIR_SEPARATOR
,
282 temp_string
= g_strdup_printf("%s", aux_str2
);
286 if ( access(temp_string
, R_OK
) != 0 ) {
290 /* convert needed_char to a utf-8 string */
291 l
= g_unichar_to_utf8 (needed_char
, outbuf
);
293 s_log_message(_("Could not find character '%s' definition.\n"), outbuf
);
295 g_free (temp_string
);
296 temp_string
= g_build_filename (toplevel
->font_directory
, "quest.sym", NULL
);
297 if ( access(temp_string
, R_OK
) != 0 ) {
298 fprintf(stderr
, _("Could not load question font char -- check font-directory keyword\n"));
304 /* Make new object for the font set list */
305 o_font_set
= (OBJECT
*)g_new (OBJECT
, 1);
307 o_font_set
->font_prim_objs
= NULL
;
308 o_font_set
->font_text_size
= 100;
310 o_font_set
->name
= g_strdup_printf ("%c", needed_char
);
311 o_font_set
->font_prim_objs
= o_text_new_head(toplevel
);
313 /* Add it to the list and hash table. Some functions will need it */
314 g_hash_table_insert (font_loaded
,
315 GUINT_TO_POINTER (needed_char
), o_font_set
);
317 if (not_found
== TRUE
) {
318 /* set the font text size (width) to the question mark's size */
319 /* yes, the question mark character was loaded instead of the real char */
320 /* 63 == question mark character */
324 aux_obj
= g_hash_table_lookup (font_loaded
,
325 GUINT_TO_POINTER ((gunichar
)'?'));
326 if (aux_obj
== NULL
) {
327 o_text_load_font(toplevel
, (gunichar
) '?');
328 aux_obj
= g_hash_table_lookup (font_loaded
,
329 GUINT_TO_POINTER ((gunichar
)'?'));
332 o_font_set
->font_text_size
= aux_obj
->font_text_size
;
335 o_font_set
->font_prim_objs
= o_read(toplevel
, o_font_set
->font_prim_objs
,
337 if (o_font_set
->font_prim_objs
== NULL
) {
338 g_assert (err
!= NULL
);
339 g_warning ("o_text_basic.c: Failed to read font file: %s\n",
344 o_font_set
->font_prim_objs
= return_head(o_font_set
->font_prim_objs
);
348 return(o_font_set
->font_prim_objs
);
351 /*! \brief count the lines of a text string
352 * \par Function Description
353 * This function just counts the number of lines that are
356 * \param [in] string text string to count the lines
357 * \return the number of lines
359 int o_text_num_lines(const char *string
)
363 gunichar current_char
;
365 if (string
== NULL
) {
369 /* if it's not null, then we have at least one line */
371 /* Count how many \n are in the string */
373 while (aux
&& ((gunichar
) (*aux
) != 0) ) {
374 current_char
= g_utf8_get_char_validated(aux
, -1);
375 if (current_char
== '\n')
377 aux
= g_utf8_find_next_char(aux
, NULL
);
383 /*! \brief calculates the height of a text string
384 * \par Function Description
385 * This function calculates the height of a \a string depending
386 * on it's text \a size. The number of lines and the spacing
387 * between the lines are taken into account.
389 * \param [in] obj The text object that will contain the string.
390 * \param [in] string the text string
391 * \return the total height of the text string
393 int o_text_height(OBJECT
*obj
, const char *string
)
397 if (string
== NULL
|| font_loaded
== NULL
) {
401 /* Get the number of lines in the string */
402 line_count
= o_text_num_lines(string
);
404 /* 26 is the height of a single char (in mils) */
405 /* which represents a character which is 2 pts high */
406 /* So size has to be divided in half */
407 /* and it's added the LINE_SPACING*character_height of each line */
408 return(26*obj
->text
->size
/2*(1+LINE_SPACING
*(line_count
-1)));
411 /*! \brief calculate the width of a text
412 * \par Function Description
413 * This function calculates the width of a text \a string
414 * depending on the text size and the width of the individual
415 * characters that are in the text string.
417 * \param [in] toplevel The TOPLEVEL object
418 * \param [in] obj The text object that will contain the string.
419 * \param [in] string The text string
420 * \return the total width of the text.
422 int o_text_width(TOPLEVEL
*toplevel
, OBJECT
*obj
, char const *string
)
424 int size
= obj
->text
->size
/2;
425 int width
=0, max_width
=0;
426 int size_of_tab_in_coord
;
429 gunichar previous_char
;
431 if (string
== NULL
|| font_loaded
== NULL
) {
435 /* Make sure TAB_CHAR_MODEL is loaded before trying to use its text */
437 o_font_set
= g_hash_table_lookup (
438 font_loaded
, GUINT_TO_POINTER ((gunichar
)TAB_CHAR_MODEL
[0]));
439 if (o_font_set
== NULL
) {
440 o_text_load_font(toplevel
, (gunichar
) TAB_CHAR_MODEL
[0]);
441 o_font_set
= g_hash_table_lookup (
442 font_loaded
, GUINT_TO_POINTER ((gunichar
)TAB_CHAR_MODEL
[0]));
445 /* Get the maximum tab width's in coordinates */
446 size_of_tab_in_coord
= tab_in_chars
* size
* o_font_set
->font_text_size
;
449 ptr
!= NULL
&& *ptr
!= 0;
450 ptr
= g_utf8_find_next_char (ptr
, NULL
))
453 c
= g_utf8_get_char_validated (ptr
, -1);
455 if ( (c
== (gunichar
) '\\') &&
456 (previous_char
!= (gunichar
) '\\') ) {
459 if ( (c
== (gunichar
) '_') &&
460 (previous_char
== (gunichar
) '\\') ) {
464 case ((gunichar
)'\n'):
467 case ((gunichar
)'\t'):
468 width
+= (size_of_tab_in_coord
- (width
% size_of_tab_in_coord
));
471 /* find current_char */
472 o_font_set
= g_hash_table_lookup (font_loaded
,
473 GUINT_TO_POINTER (c
));
474 if (o_font_set
== NULL
) {
475 o_text_load_font (toplevel
, (gunichar
)c
);
476 /* let do a new search for character c */
477 o_font_set
= g_hash_table_lookup (font_loaded
,
478 GUINT_TO_POINTER (c
));
481 if (o_font_set
!= NULL
) {
482 width
= width
+ size
*o_font_set
->font_text_size
;
485 if (width
> max_width
) {
491 /* the size is a fudge factor */
492 /* Changed the return value according to a suggestion of Ales. */
493 /* Yes, the -size*10 fudge factor should be removed. */
494 /* return(max_width - size*10); */
498 /*! \brief create a complex text object from a string
499 * \par Function Description
500 * This function converts the \a string into a list of basic objects.
501 * All basic objects are appendend to the \a object.
502 * The basic objects are collected from the basic font definition
503 * of each character of they are created as lines for the overbar feature.
505 * \param [in] toplevel The TOPLEVEL object
506 * \param [in] object The symbol for which to create the basic objects
507 * \param [in] string The string to create the object list from
508 * \param [in] size The size of the text object
509 * \param [in] color The color of the text object
510 * \param [in] x The x coord of the text object
511 * \param [in] y The y coord of the text object
512 * \param [in] alignment The alignment of the text object
513 * \param [in] angle The angle of the text object (in 90 degree steps)
515 static void o_text_create_string(TOPLEVEL
*toplevel
, OBJECT
*object
,
516 char *string
, int size
, int color
,
517 int x
, int y
, int alignment
, int angle
)
519 OBJECT
*temp_tail
=NULL
;
522 OBJECT
*start_of_char
;
528 int line_start_x
, line_start_y
;
530 int overbar_startx
=0, overbar_starty
=0;
531 int overbar_endx
=0, overbar_endy
=0;
532 int overbar_height_offset
= 0;
535 gunichar current_char
;
536 gboolean escape
= FALSE
, overbar_started
= FALSE
;
537 gboolean finish_overbar
, start_overbar
, leave_parser
= FALSE
;
538 gboolean draw_character
, draw_tabulator
, draw_newline
;
540 g_return_if_fail(object
);
541 g_return_if_fail(string
);
542 g_return_if_fail(font_loaded
);
544 temp_list
= object
->text
->prim_objs
;
546 /* now read in the chars */
547 temp_tail
= toplevel
->page_current
->object_tail
;
549 text_height
= o_text_height(object
, string
);
550 char_height
= o_text_height(object
, "a");
551 text_width
= o_text_width(toplevel
, object
, string
);
568 if (angle
== 0 || angle
== 180) {
569 y
= y
- o_text_height(object
, "a") + text_height
;
580 y_offset
= y
+ sign
*0.5*text_height
;
585 y_offset
= y
+ sign
*text_height
;
589 x_offset
= x
+ sign
*0.5*text_width
;
594 x_offset
= x
+ sign
*0.5*text_width
;
595 y_offset
= y
+ sign
*0.5*text_height
;
599 x_offset
= x
+ sign
*0.5*text_width
;
600 y_offset
= y
+ sign
*text_height
;
605 x_offset
= x
+ sign
*text_width
;
610 x_offset
= x
+ sign
*text_width
;
611 y_offset
= y
+ sign
*0.5*text_height
;
615 x_offset
= x
+ sign
*text_width
;
616 y_offset
= y
+ sign
*text_height
;
620 fprintf(stderr
, "Got an invalid text alignment [%d]\n",
622 fprintf(stderr
, "Defaulting to Lower Left");
623 alignment
= LOWER_LEFT
;
628 } else { /* angle is 90 or 270 */
629 x
= x
+ sign
*(o_text_height(object
, "a") - text_height
);
639 x_offset
= x
+ sign
*0.5*text_height
;
644 x_offset
= x
+ sign
*text_height
;
650 y_offset
= y
- sign
*0.5*text_width
;
654 x_offset
= x
+ sign
*0.5*text_height
;
655 y_offset
= y
- sign
*0.5*text_width
;
659 x_offset
= x
+ sign
*text_height
;
660 y_offset
= y
- sign
*0.5*text_width
;
665 y_offset
= y
- sign
*text_width
;
669 x_offset
= x
+ sign
*0.5*text_height
;
670 y_offset
= y
- sign
*text_width
;
674 x_offset
= x
+ sign
*text_height
;
675 y_offset
= y
- sign
*text_width
;
679 fprintf(stderr
, "Got an invalid text alignment [%d]\n",
681 fprintf(stderr
, "Defaulting to Lower Left");
682 alignment
= LOWER_LEFT
;
691 x_offset
= x_offset
- text_width
;
692 y_offset
= y_offset
- text_height
;
698 printf("width: %d\n", o_text_width(toplevel
, object
, string
));
699 printf("1 %d %d\n", x_offset
, y_offset
);
702 line_start_x
= x_offset
;
703 line_start_y
= y_offset
;
705 /* the overbar is 1/4 above the char height. */
706 overbar_height_offset
= char_height
+ char_height
/4;
709 ptr
!= NULL
&& !leave_parser
;
710 ptr
= g_utf8_find_next_char (ptr
, NULL
)) {
711 current_char
= g_utf8_get_char_validated (ptr
, -1);
713 /* reset all actions */
714 finish_overbar
= FALSE
;
715 start_overbar
= FALSE
;
716 leave_parser
= FALSE
;
717 draw_character
= FALSE
;
718 draw_tabulator
= FALSE
;
719 draw_newline
= FALSE
;
721 /* state machine to interpret the string:
722 * there are two independent state variables overbar_started and escape.
723 * The actions are set according to the current character and
724 * the two state variables.
726 switch (current_char
) {
728 /* end of the string */
730 finish_overbar
= TRUE
;
734 if (escape
== TRUE
) {
735 draw_character
= TRUE
;
742 if (escape
== TRUE
) {
744 if (overbar_started
== TRUE
) {
745 finish_overbar
= TRUE
;
746 overbar_started
= FALSE
;
748 start_overbar
= TRUE
;
749 overbar_started
= TRUE
;
752 draw_character
= TRUE
;
757 if (overbar_started
== TRUE
) {
758 finish_overbar
= TRUE
;
759 start_overbar
= TRUE
;
764 draw_tabulator
= TRUE
;
768 draw_character
= TRUE
;
772 /* execute all actions set by the state machine
773 * Note: It's important that the three actions
774 * finish_overbar, draw_newline, start_overbar are executed
775 * in exacly that order. It's required to continue overbars
778 if (draw_character
) {
779 /* get the character from the hash table */
780 o_font_set
= g_hash_table_lookup (font_loaded
,
781 GUINT_TO_POINTER (current_char
));
782 if (o_font_set
== NULL
) {
783 o_text_load_font(toplevel
, (gunichar
) current_char
);
784 o_font_set
= g_hash_table_lookup (font_loaded
,
785 GUINT_TO_POINTER (current_char
));
788 /* Only add the character if there are primary object.
789 e.g. the space character doesn't have those */
790 if (o_font_set
->font_prim_objs
->next
!= NULL
) {
791 start_of_char
= temp_list
;
792 temp_list
= o_list_copy_all(toplevel
,
793 o_font_set
->font_prim_objs
,
794 temp_list
, NORMAL_FLAG
);
796 if (start_of_char
!= NULL
)
797 start_of_char
= start_of_char
->next
;
799 o_scale(start_of_char
, size
/2, size
/2);
801 /* Rotate and translate the character to its world position */
802 o_list_rotate_world(0, 0, angle
, start_of_char
);
803 o_list_translate_world(x_offset
, y_offset
, start_of_char
);
806 /* Calculate the position of the next character */
809 x_offset
= (x_offset
) + size
/2*o_font_set
->font_text_size
;
812 y_offset
= (y_offset
) + size
/2*o_font_set
->font_text_size
;
815 x_offset
= (x_offset
) - size
/2*o_font_set
->font_text_size
;
818 y_offset
= (y_offset
) - size
/2*o_font_set
->font_text_size
;
823 if (draw_tabulator
) {
824 gint size_of_tab_in_coord
;
826 /* Get the maximum tab width's in coordinates */
827 size_of_tab_in_coord
= (tab_in_chars
*
828 o_text_width(toplevel
, object
, TAB_CHAR_MODEL
));
833 rel_char_coord
= x_offset
- line_start_x
;
834 x_offset
+= (size_of_tab_in_coord
-
835 (rel_char_coord
% size_of_tab_in_coord
));
838 rel_char_coord
= y_offset
- line_start_y
;
839 y_offset
+= (size_of_tab_in_coord
-
840 (rel_char_coord
% size_of_tab_in_coord
));
843 rel_char_coord
= line_start_y
- y_offset
;
844 y_offset
-= (size_of_tab_in_coord
-
845 (rel_char_coord
% size_of_tab_in_coord
));
848 fprintf(stderr
, "o_text_create_string: Angle not supported\n");
853 if (finish_overbar
) {
856 overbar_endx
= x_offset
;
857 overbar_endy
= y_offset
+ overbar_height_offset
;
860 overbar_endx
= x_offset
- overbar_height_offset
;
861 overbar_endy
= y_offset
;
864 overbar_endx
= x_offset
;
865 overbar_endy
= y_offset
- overbar_height_offset
;
868 overbar_endx
= x_offset
+ overbar_height_offset
;
869 overbar_endy
= y_offset
;
872 fprintf(stderr
, "o_text_create_string: Angle not supported\n");
875 /* Now add the overbar (if it is not a zero length overbar) */
876 if ((overbar_startx
!= overbar_endx
)
877 || (overbar_starty
!= overbar_endy
)) {
878 temp_obj
= o_line_new(toplevel
, OBJ_LINE
, color
,
879 overbar_startx
, overbar_starty
,
880 overbar_endx
, overbar_endy
);
881 temp_list
= s_basic_link_object(temp_obj
, temp_list
);
888 x_offset
= line_start_x
;
889 y_offset
= line_start_y
- char_height
* LINE_SPACING
;
892 x_offset
= line_start_x
+ char_height
* LINE_SPACING
;
893 y_offset
= line_start_y
;
896 x_offset
= line_start_x
;
897 y_offset
= line_start_y
+ char_height
* LINE_SPACING
;
900 x_offset
= line_start_x
- char_height
* LINE_SPACING
;
901 y_offset
= line_start_y
;
904 fprintf(stderr
, "o_text_create_string: Angle not supported\n");
907 line_start_x
= x_offset
;
908 line_start_y
= y_offset
;
914 overbar_startx
= x_offset
;
915 overbar_starty
= y_offset
+ overbar_height_offset
;
918 overbar_startx
= x_offset
- overbar_height_offset
;
919 overbar_starty
= y_offset
;
922 overbar_startx
= x_offset
;
923 overbar_starty
= y_offset
- overbar_height_offset
;
926 overbar_startx
= x_offset
+ overbar_height_offset
;
927 overbar_starty
= y_offset
;
930 fprintf(stderr
, "o_text_create_string: Angle not supported\n");
936 o_complex_set_color(object
, color
);
938 toplevel
->page_current
->object_tail
= temp_tail
;
941 static OBJECT
*o_text_new_at_xy(TOPLEVEL
*toplevel
,
942 char type
, int color
, int x
, int y
)
944 OBJECT
*new_node
=NULL
;
947 new_node
= s_toplevel_new_object(toplevel
, type
, "text");
948 new_node
->color
= color
;
950 text
= g_malloc(sizeof (TEXT
));
951 new_node
->text
= text
;
955 text
->toplevel
= g_object_ref(toplevel
);
957 text
->prim_objs
= o_text_new_head(toplevel
);
959 new_node
->copy_func
= &o_text_copy
;
960 new_node
->bounds_recalc_func
= o_text_recalc
;
961 new_node
->draw_func
= text_draw_func
;
962 new_node
->psprint_func
= &o_text_print
;
963 new_node
->visit_func
= &o_text_visit
;
964 new_node
->destroy_func
= &o_text_destroy
;
969 /*! \brief Creates a text OBJECT and the graphical objects representing it
970 * \par Function Description
971 * Create an OBJECT of type OBJ_TEXT.
972 * Also add the OBJECTs forming the graphical representation of the visible
973 * string, to the text OBJECT's prim_objs list.
975 * \param [in] toplevel The TOPLEVEL object.
976 * \param [in] type OBJ_TEXT (TODO: why bother)
977 * \param [in] color The color of the text.
978 * \param [in] x World x coord of text.
979 * \param [in] y World y coord of text.
980 * \param [in] alignment How text bounding box aligns on (x, y).
981 * \param [in] angle Angle at which text will appear.
982 * \param [in] string The text.
983 * \param [in] size Text size.
984 * \param [in] visibility VISIBLE or INVISIBLE.
985 * \param [in] show_name_value SHOW_NAME_VALUE or friends.
986 * \return Pointer to text OBJECT.
989 * Caller is responsible for string; this function allocates its own copy.
991 OBJECT
*o_text_new(TOPLEVEL
*toplevel
,
992 char type
, int color
, int x
, int y
, int alignment
,
993 int angle
, const char *string
, int size
,
994 int visibility
, int show_name_value
)
996 OBJECT
*new_node
=NULL
;
997 OBJECT
*temp_list
=NULL
;
1002 if (string
== NULL
) {
1006 new_node
= o_text_new_at_xy(toplevel
, type
, color
, x
, y
);
1007 text
= new_node
->text
;
1009 text
->string
= g_strdup (string
);
1010 text
->disp_string
= NULL
; /* We'll fix this up later */
1011 text
->length
= strlen(string
);
1013 text
->alignment
= alignment
;
1014 text
->angle
= angle
;
1016 new_node
->visibility
= visibility
;
1017 new_node
->show_hidden
= toplevel
->show_hidden_default
;
1018 new_node
->show_name_value
= show_name_value
;
1020 update_disp_string (new_node
);
1022 if (visibility
== VISIBLE
||
1023 (visibility
== INVISIBLE
&& toplevel
->show_hidden_default
)) {
1024 o_text_create_string(toplevel
, new_node
,
1025 text
->disp_string
, size
, color
,
1026 x
, y
, alignment
, angle
);
1027 new_node
->text
->displayed_width
= o_text_width(toplevel
, new_node
,
1029 new_node
->text
->displayed_height
= o_text_height(new_node
, text
->disp_string
);
1031 new_node
->text
->displayed_width
= 0;
1032 new_node
->text
->displayed_height
= 0;
1035 /* Update bounding box */
1036 o_text_recalc(new_node
);
1043 /*! \brief update the visual boundaries of the text object
1044 * \par Function Description
1045 * This function updates the boundaries of the object \a o_current.
1047 * \param [in] toplevel The TOPLEVEL object
1048 * \param [in] o_current The OBJECT to update
1050 static void o_text_recalc(OBJECT
*o_current
)
1052 int left
, right
, top
, bottom
;
1054 if (o_current
->visibility
== INVISIBLE
&& !o_current
->show_hidden
) {
1058 if (o_current
->text
->x
== -1 && o_current
->text
->y
== -1) {
1059 /* Off-schematic text has no bounding box. */
1063 if (!world_get_text_bounds(o_current
, &left
, &top
, &right
, &bottom
))
1066 o_current
->w_left
= left
;
1067 o_current
->w_top
= top
;
1068 o_current
->w_right
= right
;
1069 o_current
->w_bottom
= bottom
;
1070 o_current
->w_bounds_valid
= TRUE
;
1073 static enum visit_result
1074 o_text_visit(OBJECT
*o_current
,
1075 enum visit_result (*fn
)(OBJECT
*, void *), void *context
,
1076 enum visit_order order
, int depth
)
1078 g_return_if_fail(o_current
->type
== OBJ_TEXT
);
1079 g_return_if_fail(o_current
->text
->prim_objs
!= NULL
);
1081 return (s_visit_list(o_current
->text
->prim_objs
, LIST_KIND_HEAD
, fn
, context
,
1085 static void o_text_destroy(OBJECT
*o_current
)
1087 g_free(o_current
->text
->string
);
1088 g_free(o_current
->text
->disp_string
);
1090 if (o_current
->text
->prim_objs
) {
1091 s_delete_list_fromstart(o_current
->text
->toplevel
,
1092 o_current
->text
->prim_objs
);
1095 g_object_unref(o_current
->text
->toplevel
);
1097 g_free(o_current
->text
);
1100 /*! \brief read a text object from a char buffer
1101 * \par Function Description
1102 * This function reads a text object from the textbuffer \a tb and
1103 * the text starting with the line \a firstline.
1105 * \param [in] toplevel The TOPLEVEL object
1106 * \param [in] first_line the first line of the text
1107 * \param [in] tb a text buffer (usually a line of a schematic file)
1108 * \param [in] release_ver The release number gEDA
1109 * \param [in] fileformat_ver a integer value of the file format
1110 * \return The object list
1112 OBJECT
*o_text_read(TOPLEVEL
*toplevel
,
1113 const char *first_line
,
1115 unsigned int release_ver
,
1116 unsigned int fileformat_ver
)
1124 int show_name_value
;
1129 char* string
= NULL
;
1132 if (fileformat_ver
>= 1) {
1133 sscanf(first_line
, "%c %d %d %d %d %d %d %d %d %d\n", &type
, &x
, &y
,
1135 &visibility
, &show_name_value
,
1136 &angle
, &alignment
, &num_lines
);
1137 } else if (release_ver
< VERSION_20000220
) {
1138 /* yes, above less than (not less than and equal) is correct. The format */
1139 /* change occurred in 20000220 */
1140 sscanf(first_line
, "%c %d %d %d %d %d %d %d\n", &type
, &x
, &y
,
1142 &visibility
, &show_name_value
,
1144 alignment
= LOWER_LEFT
; /* older versions didn't have this */
1145 num_lines
= 1; /* only support a single line */
1147 sscanf(first_line
, "%c %d %d %d %d %d %d %d %d\n", &type
, &x
, &y
,
1149 &visibility
, &show_name_value
,
1150 &angle
, &alignment
);
1151 num_lines
= 1; /* only support a single line */
1155 s_log_message(_("Found a zero size text string [ %c %d %d %d %d %d %d %d %d ]\n"), type
, x
, y
, color
, size
, visibility
, show_name_value
, angle
, alignment
);
1166 s_log_message(_("Found an unsupported text angle [ %c %d %d %d %d %d %d %d %d ]\n"),
1167 type
, x
, y
, color
, size
, visibility
, show_name_value
, angle
, alignment
);
1168 s_log_message(_("Setting angle to 0\n"));
1179 case(MIDDLE_MIDDLE
):
1188 s_log_message(_("Found an unsupported text alignment [ %c %d %d %d %d %d %d %d %d ]\n"),
1189 type
, x
, y
, color
, size
, visibility
, show_name_value
, angle
, alignment
);
1190 s_log_message(_("Setting alignment to LOWER_LEFT\n"));
1191 alignment
= LOWER_LEFT
;
1195 if (color
< 0 || color
> MAX_COLORS
) {
1196 s_log_message(_("Found an invalid color [ %s ]\n"), first_line
);
1197 s_log_message(_("Setting color to WHITE\n"));
1201 g_assert(num_lines
&& num_lines
> 0);
1203 textstr
= g_string_new ("");
1204 for (i
= 0; i
< num_lines
; i
++) {
1207 line
= s_textbuffer_next_line (tb
);
1211 textstr
= g_string_append (textstr
, line
);
1214 /* retrieve the character string from the GString */
1215 string
= g_string_free (textstr
, FALSE
);
1217 string
= remove_last_nl(string
);
1219 /* convert the character string to UTF-8 if necessary */
1220 if (!g_utf8_validate (string
, -1, NULL
)) {
1221 /* if it is not utf-8, it is ISO_8859-15 */
1222 gchar
*tmp
= g_convert (string
, strlen (string
),
1223 "UTF-8", "ISO_8859-15",
1226 fprintf (stderr
, "Failed to convert text string to UTF-8: %s.\n",
1229 /* successfully converted string, now use tmp as string */
1235 new_obj
= o_text_new(toplevel
, type
, color
, x
, y
,
1236 alignment
, angle
, string
,
1237 size
, visibility
, show_name_value
);
1243 /*! \brief read and set infos of a font object
1244 * \par Function Description
1245 * This function reads the font definition buffer \a buf and sets
1246 * the width of a character. This function also deals with the special,
1247 * invisible character space and newline.
1249 * \param [in] buf the font definition according to the geda file format
1250 * \todo Investigate why the TAB character is not defined here.
1252 void o_text_set_info_font(char buf
[])
1256 gunichar character
=0;
1262 string
= remove_nl (buf
);
1264 /* parse the font info: */
1265 buf_ptr
= (gchar
*)string
;
1268 if (type
!= INFO_FONT
) {
1269 g_critical ("o_text_set_info_font: Bad font type '%c', expected '%c'\n",
1274 while (buf_ptr
!= NULL
&& *buf_ptr
== ' ') buf_ptr
++;
1276 if (buf_ptr
!= NULL
&& *buf_ptr
!= '\0') {
1277 character
= g_utf8_get_char_validated (buf_ptr
, -1);
1278 if (character
== (gunichar
)-1) {
1279 s_log_message (_("Failed to validate utf-8 character in font definition: \"%s\".\n"),
1283 /* move buf_ptr just after character */
1284 buf_ptr
= g_utf8_find_next_char (buf_ptr
, NULL
);
1286 while (buf_ptr
!= NULL
&& *buf_ptr
== ' ') buf_ptr
++;
1287 /* - width and special */
1288 if (buf_ptr
!= NULL
) {
1289 sscanf (buf_ptr
, "%d %d\n", &width
, &special
);
1292 /* deal with special characters */
1294 switch (character
) {
1295 case ((gunichar
)'_'):
1297 character
= (gunichar
)' ';
1299 case ((gunichar
)'n'):
1301 character
= (gunichar
)'\n';
1306 o_font_set
= g_hash_table_lookup (font_loaded
,
1307 GUINT_TO_POINTER ((gunichar
)character
));
1308 if (o_font_set
!= NULL
) {
1309 o_font_set
->font_text_size
= width
;
1312 gint l
= g_unichar_to_utf8 (character
, outbuf
);
1315 "o_text_set_info_font: character %s not found!!!\n", outbuf
);
1320 /*! \brief Create a string representation of the text object
1321 * \par Function Description
1322 * This function takes a text \a object and return a string
1323 * according to the file format definition.
1325 * \param [in] object a text OBJECT
1326 * \return the string representation of the text OBJECT
1328 char *o_text_save(OBJECT
*object
)
1337 x
= object
->text
->x
;
1338 y
= object
->text
->y
;
1340 string
= object
->text
->string
;
1341 size
= object
->text
->size
;
1343 /* Use the right color */
1344 if (object
->saved_color
== -1) {
1345 color
= object
->color
;
1347 color
= object
->saved_color
;
1350 /* string can have multiple lines (separated by \n's) */
1351 num_lines
= o_text_num_lines(string
);
1353 buf
= g_strdup_printf("%c %d %d %d %d %d %d %d %d %d\n%s", object
->type
,
1354 x
, y
, color
, size
, object
->visibility
,
1355 object
->show_name_value
, object
->text
->angle
,
1356 object
->text
->alignment
, num_lines
, string
);
1361 /*! \brief recreate the graphics of a text object
1362 * \par Function Description
1363 * This function updates the underlying primary of the text object
1366 * \param o_current The text object to update
1368 void o_text_recreate(OBJECT
*o_current
)
1372 TEXT
*text
= o_current
->text
;
1374 update_disp_string (o_current
);
1376 o_list_delete_rest(text
->toplevel
, text
->prim_objs
);
1378 if (o_current
->visibility
== VISIBLE
||
1379 (o_current
->visibility
== INVISIBLE
&& o_current
->show_hidden
)) {
1380 o_text_create_string(text
->toplevel
, o_current
,
1388 o_complex_set_saved_color_only(o_current
, o_current
->saved_color
);
1389 text
->displayed_width
= o_text_width(text
->toplevel
, o_current
,
1391 text
->displayed_height
= o_text_height(o_current
, text
->disp_string
);
1393 /* make sure list is truly free */
1394 text
->displayed_width
= 0;
1395 text
->displayed_height
= 0;
1398 o_text_recalc(o_current
);
1404 /*! \brief move a text object
1405 * \par Function Description
1406 * This function changes the position of a text object \a o_current.
1408 * \param [in] dx The x-distance to move the object
1409 * \param [in] dy The y-distance to move the object
1410 * \param [in] o_current The text OBJECT to be moved
1412 void o_text_translate_world(int dx
, int dy
, OBJECT
*o_current
)
1414 o_current
->text
->x
= o_current
->text
->x
+ dx
;
1415 o_current
->text
->y
= o_current
->text
->y
+ dy
;
1417 o_list_translate_world(dx
, dy
, o_current
->text
->prim_objs
);
1419 /* Update bounding box */
1420 o_text_recalc(o_current
);
1423 /*! \brief create a copy of a text object
1424 * \par Function Description
1425 * This function creates a copy of the text object \a o_current.
1427 * \param [in] toplevel The TOPLEVEL object
1428 * \param [in] o_current The object that is copied
1429 * \return a new text object
1431 static OBJECT
*o_text_copy(TOPLEVEL
*toplevel
, OBJECT
*o_current
)
1436 if (o_current
->saved_color
== -1) {
1437 color
= o_current
->color
;
1439 color
= o_current
->saved_color
;
1442 new_obj
= o_text_new (toplevel
, OBJ_TEXT
, color
,
1443 o_current
->text
->x
, o_current
->text
->y
,
1444 o_current
->text
->alignment
,
1445 o_current
->text
->angle
,
1446 o_current
->text
->string
,
1447 o_current
->text
->size
,
1448 o_current
->visibility
,
1449 o_current
->show_name_value
);
1454 /*! \brief delete a font set
1455 * \par Function Description
1456 * This is a GHRFunc function that deletes a single font set.
1458 * \param [in] key The hash key (the font character)
1459 * \param [in] value The value of the hash table (the font object)
1460 * \param [in] user_data Data supplied by the user (the TOPLEVEL object)
1462 static gboolean
delete_font_set (gpointer key G_GNUC_UNUSED
, gpointer value
,
1465 OBJECT
*tmp
= (OBJECT
*)value
;
1466 TOPLEVEL
*toplevel
= (TOPLEVEL
*)user_data
;
1469 if (tmp
->font_prim_objs
!= NULL
) {
1470 s_delete_list_fromstart (toplevel
, tmp
->font_prim_objs
);
1471 tmp
->font_prim_objs
= NULL
;
1473 /* do not use s_delete() as tmp is not fully initialized */
1476 /* Do not free tmp here since it will be freed with the function */
1477 /* that was specified when the hash table was created. */
1483 /*! \brief free the font hash tables
1484 * \par Function Description
1485 * This function destroys the two global font hash tables
1486 * <b>font_loaded</b> and <b>font_char_to_file</b>
1488 void o_text_freeallfonts(TOPLEVEL
*toplevel
)
1490 /* destroy the char-to-objects hastable */
1491 g_hash_table_foreach_remove (font_loaded
,
1494 g_hash_table_destroy (font_loaded
);
1497 /* destroy the font-to-filename hashtable */
1498 g_hash_table_destroy (font_char_to_file
);
1499 font_char_to_file
= NULL
;
1503 /*! \brief write a text string to a postscript file
1504 * \par Function Description
1505 * This function writes the single \a string into the postscript file \a fp.
1507 * \param [in] fp pointer to a FILE structure
1508 * \param [in] string The string to print
1509 * \param [in] unicode_table Table of unicode items
1511 * \todo investigate whether the TAB character is handled correctly
1513 void o_text_print_text_string(FILE *fp
, char *string
, GArray
*unicode_table
)
1517 gunichar current_char
, c
;
1528 while (aux
&& ((gunichar
) (*aux
) != 0)) {
1529 current_char
= g_utf8_get_char_validated(aux
, -1);
1530 if (current_char
== '(' || current_char
== ')' || current_char
== '\\') {
1537 for (j
= 0; j
< unicode_table
->len
; j
++) {
1538 if (c
== g_array_index(unicode_table
, gunichar
, j
)) {
1539 current_char
= j
+ 128;
1546 if (current_char
== '\t') {
1547 /* Output eight spaces instead of the tab character */
1550 fprintf(fp
, "%c", current_char
);
1553 aux
= g_utf8_find_next_char(aux
, NULL
);
1559 /*! \brief print a text object into a postscript file
1560 * \par Function Description
1561 * This function writes the postscript representation of the text object
1562 * \a o_current into the the file \a fp.
1563 * \param [in] toplevel The TOPLEVEL object
1564 * \param [in] fp pointer to a FILE structure
1565 * \param [in] o_current The OBJECT to print
1566 * \param [in] unicode_table Table of unicode items
1568 void o_text_print(TOPLEVEL
*toplevel
, FILE *fp
, OBJECT
*o_current
,
1569 double scale
, GArray
*unicode_table
)
1572 char *centering_control
= NULL
;
1574 char *output_string
= NULL
;
1577 int x
, y
, angle
, len
, char_height
;
1580 if (o_current
->visibility
!= VISIBLE
) {
1584 if (!o_current
->text
->string
) {
1588 fprintf(fp
, "gsave\n");
1590 if (toplevel
->text_output
== VECTOR_FONTS
1591 || (toplevel
->print_vector_threshold
> 0
1592 && (o_text_num_lines(o_current
->text
->string
)
1593 > toplevel
->print_vector_threshold
))) {
1594 f_print_objects(toplevel
,
1596 o_current
->text
->prim_objs
,
1598 scale
, unicode_table
);
1599 fprintf(fp
, "grestore\n");
1603 if (toplevel
->print_color
) {
1604 f_print_set_color(fp
, o_current
->color
);
1608 if (o_attrib_get_name_value(o_current
->text
->string
, &name
, &value
)) {
1609 switch(o_current
->show_name_value
) {
1610 case(SHOW_NAME_VALUE
):
1611 output_string
= g_strdup(o_current
->text
->string
);
1615 output_string
= g_strdup(name
);
1619 output_string
= g_strdup(value
);
1623 output_string
= g_strdup(o_current
->text
->string
);
1626 /* Apply alignment map to apply when text is 180 degrees rotated.
1627 * We want the text on the printer to appear upside right, even
1628 * though mathematically it aught to be upside down. To make this
1629 * work, we will reset the angle to 0, when it's equal to 180
1630 * degrees, then apply a transformation to the origin location as if
1631 * the text was rotated about that point. E.g. if the text origin
1632 * was at the lower left, and the text was rotated by 180 degrees,
1633 * it would be as if the origin was at the upper right. The same
1634 * reasoning has been applied to all 8 other text origins.
1635 * MIDDLE_MIDDLE maps to itself.
1637 alignment
= o_current
->text
->alignment
;
1638 angle
= o_current
->text
->angle
;
1640 angle
= 0; /* reset angle to 0 to make text upright */
1642 case(LOWER_LEFT
): alignment
= UPPER_RIGHT
;
1644 case(MIDDLE_LEFT
): alignment
= MIDDLE_RIGHT
;
1646 case(UPPER_LEFT
): alignment
= LOWER_RIGHT
;
1648 case(LOWER_MIDDLE
): alignment
= UPPER_MIDDLE
;
1650 case(MIDDLE_MIDDLE
): alignment
= MIDDLE_MIDDLE
;
1652 case(UPPER_MIDDLE
): alignment
= LOWER_MIDDLE
;
1654 case(LOWER_RIGHT
): alignment
= UPPER_LEFT
;
1656 case(MIDDLE_RIGHT
): alignment
= MIDDLE_LEFT
;
1658 case(UPPER_RIGHT
): alignment
= LOWER_LEFT
;
1663 /* Create an appropriate control string for the centering. */
1665 /* hcenter rjustify vcenter vjustify */
1666 case(LOWER_LEFT
): centering_control
= "false false false false";
1668 case(MIDDLE_LEFT
): centering_control
= "false false true false";
1670 case(UPPER_LEFT
): centering_control
= "false false false true";
1672 case(LOWER_MIDDLE
): centering_control
= "true false false false";
1674 case(MIDDLE_MIDDLE
): centering_control
= "true false true false";
1676 case(UPPER_MIDDLE
): centering_control
= "true false false true";
1678 case(LOWER_RIGHT
): centering_control
= "false true false false";
1680 case(MIDDLE_RIGHT
): centering_control
= "false true true false";
1682 case(UPPER_RIGHT
): centering_control
= "false true false true";
1686 char_height
= o_text_height(o_current
, "a");
1687 fprintf(fp
,"%s %f [",centering_control
,(float)(char_height
*LINE_SPACING
));
1689 /* split the line at each newline and print them */
1690 p
= output_string
; /* Current point */
1691 s
= output_string
; /* Start of the current string */
1692 len
= strlen(output_string
)+1;
1694 /* Have we reached the end of a line? */
1695 if((*p
== '\n') || (*p
== '\0')) {
1696 /* Yes, replace the newline with a NULL and output the string */
1698 o_text_print_text_string(fp
, s
, unicode_table
);
1699 /* Update output string start for next string */
1700 s
= p
+1; /* One past the current character. */
1702 p
++; /* Advance to next character */
1703 len
--; /* Keep track of how many characters left to process */
1706 /* Finish up with the rest of the text print command */
1707 /* Collect pertinent info about the text location */
1708 x
= o_current
->text
->x
;
1709 y
= o_current
->text
->y
;
1710 font_size
= (((float)(o_current
->text
->size
))
1711 * toplevel
->postscript_font_scale
/ 72.0 * 1000.0);
1712 fprintf(fp
,"] %d %d %d %f text\n",angle
,x
,y
,font_size
);
1715 g_free(output_string
);
1719 fprintf(fp
, "grestore\n");
1723 /*! \brief rotate a text object around a centerpoint
1724 * \par Function Description
1725 * This function rotates a text \a object around the point
1726 * (\a world_centerx, \a world_centery).
1728 * \param [in] world_centerx x-coord of the rotation center
1729 * \param [in] world_centery y-coord of the rotation center
1730 * \param [in] angle The angle to rotate the text object
1731 * \param [in] object The text object
1732 * \note only steps of 90 degrees are allowed for the \a angle
1734 void o_text_rotate_world(int world_centerx
, int world_centery
,
1735 int angle
, OBJECT
*object
)
1740 g_return_if_fail(object
!= NULL
);
1741 g_return_if_fail(object
->type
== OBJ_TEXT
);
1743 object
->text
->angle
= ( object
->text
->angle
+ angle
) % 360;
1745 x
= object
->text
->x
+ (-world_centerx
);
1746 y
= object
->text
->y
+ (-world_centery
);
1748 rotate_point_90(x
, y
, angle
, &newx
, &newy
);
1750 x
= newx
+ (world_centerx
);
1751 y
= newy
+ (world_centery
);
1753 o_text_translate_world(x
-object
->text
->x
, y
-object
->text
->y
, object
);
1755 o_text_recreate(object
);
1759 /*! \brief mirror a text object horizontaly at a centerpoint
1760 * \par Function Description
1761 * This function mirrors a text \a object horizontaly at the point
1762 * (\a world_centerx, \a world_centery).
1764 * \param [in] world_centerx x-coord of the mirror position
1765 * \param [in] world_centery y-coord of the mirror position
1766 * \param [in] object The text object
1768 void o_text_mirror_world(int world_centerx
, int world_centery
,
1774 origx
= object
->text
->x
;
1775 origy
= object
->text
->y
;
1777 x
= origx
+ (-world_centerx
);
1778 y
= origy
+ (-world_centery
);
1780 if ((object
->text
->angle
%180)==0) {
1781 switch(object
->text
->alignment
) {
1783 object
->text
->alignment
=LOWER_RIGHT
;
1787 object
->text
->alignment
=MIDDLE_RIGHT
;
1791 object
->text
->alignment
=UPPER_RIGHT
;
1795 object
->text
->alignment
=LOWER_LEFT
;
1799 object
->text
->alignment
=MIDDLE_LEFT
;
1803 object
->text
->alignment
=UPPER_LEFT
;
1810 switch(object
->text
->alignment
) {
1812 object
->text
->alignment
=UPPER_LEFT
;
1816 object
->text
->alignment
=LOWER_LEFT
;
1820 object
->text
->alignment
=UPPER_RIGHT
;
1824 object
->text
->alignment
=LOWER_RIGHT
;
1828 object
->text
->alignment
=UPPER_MIDDLE
;
1832 object
->text
->alignment
=LOWER_MIDDLE
;
1840 object
->text
->x
= -x
+ (world_centerx
);
1841 object
->text
->y
= y
+ (world_centery
);
1843 o_text_recreate(object
);
1846 /*! \brief Calculates the distance between the given point and the closest
1847 * point on the text.
1849 * This function will calculate the distance to the text regardless
1850 * if the text is visible or not.
1852 * \param [in] text the text of the OBJECT
1853 * \param [in] x The x coordinate of the given point.
1854 * \param [in] y The y coordinate of the given point.
1855 * \return The shortest distance from the object to the point. If the
1856 * distance cannot be calculated, this function returns a really large
1857 * number (G_MAXDOUBLE). With an invalid parameter, this function
1858 * returns G_MAXDOUBLE.
1860 gdouble
o_text_shortest_distance(TEXT
const *text
, gint x
, gint y
)
1863 gdouble shortest_distance
= G_MAXDOUBLE
;
1867 g_critical("o_text_shortest_distance(): text == NULL\n");
1871 temp
= text
->prim_objs
;
1877 while (temp
!= NULL
) {
1878 distance
= o_shortest_distance(temp
, x
, y
);
1880 shortest_distance
= min(shortest_distance
, distance
);
1885 return shortest_distance
;
1888 /*! \brief Set the string displayed by a text object.
1889 * \par Function Description
1890 * Updates the text object with a new text string.
1892 * \param [in] obj The text object.
1893 * \param [in] new_string The new value.
1895 void o_text_set_string(OBJECT
*obj
, const gchar
*new_string
)
1897 g_return_if_fail(new_string
!= NULL
);
1898 o_text_take_string(obj
, g_strdup(new_string
));
1901 void o_text_take_string(OBJECT
*obj
, gchar
*new_string
)
1903 g_return_if_fail (obj
!= NULL
);
1904 g_return_if_fail (obj
->type
== OBJ_TEXT
);
1905 g_return_if_fail (obj
->text
!= NULL
);
1906 g_return_if_fail (new_string
!= NULL
);
1908 g_free (obj
->text
->string
);
1909 obj
->text
->string
= new_string
;
1911 g_signal_emit_by_name(obj
, "changed");
1912 if (obj
->attached_to
) {
1913 g_signal_emit_by_name(obj
->attached_to
, "attribs-changed");
1917 /*! \brief Get the string displayed by a text object.
1918 * \par Function Description
1919 * Retrieve the text string from a text object. The returned string
1920 * should be treated as constant.
1922 * \param [in] obj The text object.
1923 * \return The text object's string, or NULL on failure.
1925 const gchar
*o_text_get_string(OBJECT
const *obj
)
1927 g_return_val_if_fail (obj
!= NULL
, NULL
);
1928 g_return_val_if_fail (obj
->type
== OBJ_TEXT
, NULL
);
1929 g_return_val_if_fail (obj
->text
!= NULL
, NULL
);
1931 return obj
->text
->string
;
1934 /*! \todo Finish function documentation!!!
1936 * \par Function Description
1939 * The object passed in should be the REAL object, NOT any copy in any
1942 void o_text_change(OBJECT
*object
, char const *string
, int visibility
, int show
)
1944 if (object
== NULL
) {
1948 if (object
->type
!= OBJ_TEXT
) {
1952 /* FIXME: Block changed:: until all changes are done. */
1954 /* second change the real object */
1955 o_text_set_string(object
, string
);
1957 object
->visibility
= visibility
;
1958 object
->show_name_value
= show
;
1959 g_signal_emit_by_name(object
, "changed");