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_recalc(OBJECT
*o_current
);
90 static void o_text_visit(OBJECT
*o_current
,
91 void (*fn
)(OBJECT
*, void *),
93 static void o_text_destroy(OBJECT
*o_current
);
94 static OBJECT
*o_text_copy(TOPLEVEL
*toplevel
, OBJECT
*o_current
);
96 /*! Default setting for text draw function. */
97 void (*text_draw_func
)() = NULL
;
99 /*! Hashtable storing font_character (string) as a key, and pointer to data
101 * This table stays global, thus all functions can access it.
103 GHashTable
*font_loaded
= NULL
;
105 /*! Hashtable storing mapping between character and font definition file
107 * This table stays global, thus all functions can access it.
109 GHashTable
*font_char_to_file
= NULL
;
111 /*! Size of a tab in characters */
112 int tab_in_chars
= 8;
115 /*! \brief update the visible part of a string
116 * \par Function Description
117 * If a string is an attribute, then it is possible to hide
118 * the name or the value part of the attribute string.
119 * This functions updates the text->disp_string according
120 * to the object->show_name_value settings
122 * \param [in] o The OBJECT to update
124 static void update_disp_string(OBJECT
*o
)
128 TEXT
*text
= o
->text
;
130 g_free (text
->disp_string
);
132 if (o_attrib_get_name_value (text
->string
, &name
, &value
)) {
133 switch (o
->show_name_value
) {
134 case (SHOW_NAME_VALUE
):
135 text
->disp_string
= g_strdup (text
->string
);
139 text
->disp_string
= g_strdup (name
);
143 text
->disp_string
= g_strdup(value
);
146 /* free the strings allocated by o_attrib_get_name_value */
150 text
->disp_string
= g_strdup (text
->string
);
154 /*! \brief calculate and return the boundaries of a text object
155 * \par Function Description
156 * This function calculates the object boudaries of a text \a object.
158 * \param [in] toplevel The TOPLEVEL object.
159 * \param [in] o_current a text object
160 * \param [out] left the left world coord
161 * \param [out] top the top world coord
162 * \param [out] right the right world coord
163 * \param [out] bottom the bottom world coord
165 int world_get_text_bounds(OBJECT
*o_current
, int *left
,
166 int *top
, int *right
, int *bottom
)
168 return world_get_object_list_bounds(o_current
->text
->prim_objs
,
169 left
, top
, right
, bottom
);
172 /*! \brief create a new text head object
173 * \par Function Description
174 * This function creates a <b>text_head</b> OBJECT. This OBJECT
175 * is just a special empty object. This object is never modified.
177 * \return new text head OBJECT
179 OBJECT
*o_text_new_head(TOPLEVEL
*toplevel
)
181 OBJECT
*new_node
=NULL
;
183 new_node
= s_toplevel_new_object(toplevel
, OBJ_HEAD
, "text_head");
185 /* don't need to do this for head nodes */
186 /* ret = s_basic_link_object(new_node, NULL);*/
190 /*! \brief initialize the hash tables for the fonts
191 * \par Function Description
192 * This function initializes the two global hash tables <b>font_loaded</b>
193 * and <b>font_char_to_file</b> that are used to store the fonts characters.
195 void o_text_init(void)
197 if (font_loaded
== NULL
) {
198 font_loaded
= g_hash_table_new_full (g_direct_hash
,
203 fprintf (stderr
, "o_text_init: Tried to initialize an already initialized font_loaded hash table!!\n");
205 if (font_char_to_file
== NULL
) {
206 font_char_to_file
= g_hash_table_new_full (g_direct_hash
,
212 fprintf (stderr
, "o_text_init: Tried to initialize an already initialized font_char_to_file hash table!!\n");
219 /*! \brief print informations about some characters
221 * This is a debugging function. Do not use it in regular code.
223 void o_text_print_set(void)
225 OBJECT
*o_current
, *o_font_set
;
228 for (i
= 'A' ; i
< 'Z'+1; i
++) {
229 o_font_set
= g_hash_table_lookup (font_loaded
,
230 GUINT_TO_POINTER ((gunichar
)i
));
231 if (o_font_set
!= NULL
) {
232 printf("%c: LOADED\n", i
);
233 for (o_current
=return_tail(o_font_set
->font_prim_objs
); o_current
;
234 o_current
=o_current
->prev
)
236 printf(" %s\n", o_current
->name
);
239 printf("%c: unloaded\n", i
);
244 /*! \brief load a font character into an object
245 * \par Function Description
246 * This function loads a character form a font symbol file.
248 * \param [in] toplevel The TOPLEVEL object
249 * \param [in] needed_char unicode character to load
250 * return a character OBJECT
252 OBJECT
*o_text_load_font(TOPLEVEL
*toplevel
, gunichar needed_char
)
254 gchar
*temp_string
= NULL
;
256 int not_found
= FALSE
;
260 /* retrieve the name of the file where the char is defined */
261 aux_str2
= g_hash_table_lookup (font_char_to_file
,
262 GUINT_TO_POINTER (needed_char
));
263 if (aux_str2
== NULL
) {
264 /* this is needed since WinNT file systems are
265 * case insensitive, and cannot tell the difference
266 * between A.sym and a.sym. So we create a_.sym -
267 * z_.sym, this loads up the chars
269 if (needed_char
>= 'a' && needed_char
<= 'z') {
270 temp_string
= g_strdup_printf("%s%c%c_.sym",
271 toplevel
->font_directory
, G_DIR_SEPARATOR
,
274 temp_string
= g_strdup_printf("%s%c%c.sym",
275 toplevel
->font_directory
, G_DIR_SEPARATOR
,
279 temp_string
= g_strdup_printf("%s", aux_str2
);
283 if ( access(temp_string
, R_OK
) != 0 ) {
287 /* convert needed_char to a utf-8 string */
288 l
= g_unichar_to_utf8 (needed_char
, outbuf
);
290 s_log_message(_("Could not find character '%s' definition.\n"), outbuf
);
292 g_free (temp_string
);
293 temp_string
= g_build_filename (toplevel
->font_directory
, "quest.sym", NULL
);
294 if ( access(temp_string
, R_OK
) != 0 ) {
295 fprintf(stderr
, _("Could not load question font char -- check font-directory keyword\n"));
301 /* Make new object for the font set list */
302 o_font_set
= (OBJECT
*)g_new (OBJECT
, 1);
304 o_font_set
->font_prim_objs
= NULL
;
305 o_font_set
->font_text_size
= 100;
307 o_font_set
->name
= g_strdup_printf ("%c", needed_char
);
308 o_font_set
->font_prim_objs
= o_text_new_head(toplevel
);
310 /* Add it to the list and hash table. Some functions will need it */
311 g_hash_table_insert (font_loaded
,
312 GUINT_TO_POINTER (needed_char
), o_font_set
);
314 if (not_found
== TRUE
) {
315 /* set the font text size (width) to the question mark's size */
316 /* yes, the question mark character was loaded instead of the real char */
317 /* 63 == question mark character */
321 aux_obj
= g_hash_table_lookup (font_loaded
,
322 GUINT_TO_POINTER ((gunichar
)'?'));
323 if (aux_obj
== NULL
) {
324 o_text_load_font(toplevel
, (gunichar
) '?');
325 aux_obj
= g_hash_table_lookup (font_loaded
,
326 GUINT_TO_POINTER ((gunichar
)'?'));
329 o_font_set
->font_text_size
= aux_obj
->font_text_size
;
332 o_font_set
->font_prim_objs
= o_read(toplevel
, o_font_set
->font_prim_objs
,
334 if (o_font_set
->font_prim_objs
== NULL
) {
335 g_assert (err
!= NULL
);
336 g_warning ("o_text_basic.c: Failed to read font file: %s\n",
341 o_font_set
->font_prim_objs
= return_head(o_font_set
->font_prim_objs
);
345 return(o_font_set
->font_prim_objs
);
348 /*! \brief count the lines of a text string
349 * \par Function Description
350 * This function just counts the number of lines that are
353 * \param [in] string text string to count the lines
354 * \return the number of lines
356 int o_text_num_lines(const char *string
)
360 gunichar current_char
;
362 if (string
== NULL
) {
366 /* if it's not null, then we have at least one line */
368 /* Count how many \n are in the string */
370 while (aux
&& ((gunichar
) (*aux
) != 0) ) {
371 current_char
= g_utf8_get_char_validated(aux
, -1);
372 if (current_char
== '\n')
374 aux
= g_utf8_find_next_char(aux
, NULL
);
380 /*! \brief calculates the height of a text string
381 * \par Function Description
382 * This function calculates the height of a \a string depending
383 * on it's text \a size. The number of lines and the spacing
384 * between the lines are taken into account.
386 * \param [in] string the text string
387 * \param [in] size the text size of the character
388 * \return the total height of the text string
390 int o_text_height(const char *string
, int size
)
394 if (string
== NULL
|| font_loaded
== NULL
) {
398 /* Get the number of lines in the string */
399 line_count
= o_text_num_lines(string
);
401 /* 26 is the height of a single char (in mils) */
402 /* which represents a character which is 2 pts high */
403 /* So size has to be divided in half */
404 /* and it's added the LINE_SPACING*character_height of each line */
405 return(26*size
/2*(1+LINE_SPACING
*(line_count
-1)));
408 /*! \brief calculate the width of a text
409 * \par Function Description
410 * This function calculates the width of a text \a string
411 * depending on the text \a size and the width of the individual
412 * characters that are in the text string.
414 * \param [in] toplevel The TOPLEVEL object
415 * \param [in] string The text string
416 * \param [in] size The text size
417 * \return the total width of the text.
419 int o_text_width(TOPLEVEL
*toplevel
, char const *string
, int size
)
421 int width
=0, max_width
=0;
422 int size_of_tab_in_coord
;
425 gunichar previous_char
;
427 if (string
== NULL
|| font_loaded
== NULL
) {
431 /* Make sure TAB_CHAR_MODEL is loaded before trying to use its text */
433 o_font_set
= g_hash_table_lookup (
434 font_loaded
, GUINT_TO_POINTER ((gunichar
)TAB_CHAR_MODEL
[0]));
435 if (o_font_set
== NULL
) {
436 o_text_load_font(toplevel
, (gunichar
) TAB_CHAR_MODEL
[0]);
437 o_font_set
= g_hash_table_lookup (
438 font_loaded
, GUINT_TO_POINTER ((gunichar
)TAB_CHAR_MODEL
[0]));
441 /* Get the maximum tab width's in coordinates */
442 size_of_tab_in_coord
= tab_in_chars
* size
* o_font_set
->font_text_size
;
445 ptr
!= NULL
&& *ptr
!= 0;
446 ptr
= g_utf8_find_next_char (ptr
, NULL
))
449 c
= g_utf8_get_char_validated (ptr
, -1);
451 if ( (c
== (gunichar
) '\\') &&
452 (previous_char
!= (gunichar
) '\\') ) {
455 if ( (c
== (gunichar
) '_') &&
456 (previous_char
== (gunichar
) '\\') ) {
460 case ((gunichar
)'\n'):
463 case ((gunichar
)'\t'):
464 width
+= (size_of_tab_in_coord
- (width
% size_of_tab_in_coord
));
467 /* find current_char */
468 o_font_set
= g_hash_table_lookup (font_loaded
,
469 GUINT_TO_POINTER (c
));
470 if (o_font_set
== NULL
) {
471 o_text_load_font (toplevel
, (gunichar
)c
);
472 /* let do a new search for character c */
473 o_font_set
= g_hash_table_lookup (font_loaded
,
474 GUINT_TO_POINTER (c
));
477 if (o_font_set
!= NULL
) {
478 width
= width
+ size
*o_font_set
->font_text_size
;
481 if (width
> max_width
) {
487 /* the size is a fudge factor */
488 /* Changed the return value according to a suggestion of Ales. */
489 /* Yes, the -size*10 fudge factor should be removed. */
490 /* return(max_width - size*10); */
494 /*! \brief create a complex text object from a string
495 * \par Function Description
496 * This function converts the \a string into a list of basic objects.
497 * All basic objects are appendend to the \a object_list.
498 * The basic objects are collected from the basic font definition
499 * of each character of they are created as lines for the overbar feature.
501 * \param [in] toplevel The TOPLEVEL object
502 * \param [in] object_list The list to append the basic objects
503 * \param [in] string The string to create the object list from
504 * \param [in] size The size of the text object
505 * \param [in] color The color of the text object
506 * \param [in] x The x coord of the text object
507 * \param [in] y The y coord of the text object
508 * \param [in] alignment The alignment of the text object
509 * \param [in] angle The angle of the text object (in 90 degree steps)
511 * \return the object list of the primary text objects
513 OBJECT
*o_text_create_string(TOPLEVEL
*toplevel
, OBJECT
*object_list
,
514 char *string
, int size
, int color
, int x
, int y
,
515 int alignment
, int angle
)
517 OBJECT
*temp_tail
=NULL
;
520 OBJECT
*start_of_char
;
526 int line_start_x
, line_start_y
;
528 int overbar_startx
=0, overbar_starty
=0;
529 int overbar_endx
=0, overbar_endy
=0;
530 int overbar_height_offset
= 0;
533 gunichar current_char
;
534 gboolean escape
= FALSE
, overbar_started
= FALSE
;
535 gboolean finish_overbar
, start_overbar
, leave_parser
= FALSE
;
536 gboolean draw_character
, draw_tabulator
, draw_newline
;
538 temp_list
= object_list
;
540 /* error condition hack */
541 if (string
== NULL
|| font_loaded
== NULL
) {
545 /* now read in the chars */
546 temp_tail
= toplevel
->page_current
->object_tail
;
548 text_height
= o_text_height(string
, size
);
549 char_height
= o_text_height("a", size
);
550 text_width
= o_text_width(toplevel
, string
, size
/2);
567 if (angle
== 0 || angle
== 180) {
568 y
= y
- o_text_height("a", size
) + text_height
;
579 y_offset
= y
+ sign
*0.5*text_height
;
584 y_offset
= y
+ sign
*text_height
;
588 x_offset
= x
+ sign
*0.5*text_width
;
593 x_offset
= x
+ sign
*0.5*text_width
;
594 y_offset
= y
+ sign
*0.5*text_height
;
598 x_offset
= x
+ sign
*0.5*text_width
;
599 y_offset
= y
+ sign
*text_height
;
604 x_offset
= x
+ sign
*text_width
;
609 x_offset
= x
+ sign
*text_width
;
610 y_offset
= y
+ sign
*0.5*text_height
;
614 x_offset
= x
+ sign
*text_width
;
615 y_offset
= y
+ sign
*text_height
;
619 fprintf(stderr
, "Got an invalid text alignment [%d]\n",
621 fprintf(stderr
, "Defaulting to Lower Left");
622 alignment
= LOWER_LEFT
;
627 } else { /* angle is 90 or 270 */
628 x
= x
+ sign
*(o_text_height("a", size
) - text_height
);
638 x_offset
= x
+ sign
*0.5*text_height
;
643 x_offset
= x
+ sign
*text_height
;
649 y_offset
= y
- sign
*0.5*text_width
;
653 x_offset
= x
+ sign
*0.5*text_height
;
654 y_offset
= y
- sign
*0.5*text_width
;
658 x_offset
= x
+ sign
*text_height
;
659 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
;
692 x_offset
= x_offset
- text_width
;
693 y_offset
= y_offset
- text_height
;
699 printf("width: %d\n", o_text_width(toplevel
, string
, size
/2));
700 printf("1 %d %d\n", x_offset
, y_offset
);
703 line_start_x
= x_offset
;
704 line_start_y
= y_offset
;
706 /* the overbar is 1/4 above the char height. */
707 overbar_height_offset
= char_height
+ char_height
/4;
710 ptr
!= NULL
&& !leave_parser
;
711 ptr
= g_utf8_find_next_char (ptr
, NULL
)) {
712 current_char
= g_utf8_get_char_validated (ptr
, -1);
714 /* reset all actions */
715 finish_overbar
= FALSE
;
716 start_overbar
= FALSE
;
717 leave_parser
= FALSE
;
718 draw_character
= FALSE
;
719 draw_tabulator
= FALSE
;
720 draw_newline
= FALSE
;
722 /* state machine to interpret the string:
723 * there are two independent state variables overbar_started and escape.
724 * The actions are set according to the current character and
725 * the two state variables.
727 switch (current_char
) {
729 /* end of the string */
731 finish_overbar
= TRUE
;
735 if (escape
== TRUE
) {
736 draw_character
= TRUE
;
743 if (escape
== TRUE
) {
745 if (overbar_started
== TRUE
) {
746 finish_overbar
= TRUE
;
747 overbar_started
= FALSE
;
749 start_overbar
= TRUE
;
750 overbar_started
= TRUE
;
753 draw_character
= TRUE
;
758 if (overbar_started
== TRUE
) {
759 finish_overbar
= TRUE
;
760 start_overbar
= TRUE
;
765 draw_tabulator
= TRUE
;
769 draw_character
= TRUE
;
773 /* execute all actions set by the state machine
774 * Note: It's important that the three actions
775 * finish_overbar, draw_newline, start_overbar are executed
776 * in exacly that order. It's required to continue overbars
779 if (draw_character
) {
780 /* get the character from the hash table */
781 o_font_set
= g_hash_table_lookup (font_loaded
,
782 GUINT_TO_POINTER (current_char
));
783 if (o_font_set
== NULL
) {
784 o_text_load_font(toplevel
, (gunichar
) current_char
);
785 o_font_set
= g_hash_table_lookup (font_loaded
,
786 GUINT_TO_POINTER (current_char
));
789 /* Only add the character if there are primary object.
790 e.g. the space character doesn't have those */
791 if (o_font_set
->font_prim_objs
->next
!= NULL
) {
792 start_of_char
= temp_list
;
793 temp_list
= o_list_copy_all(toplevel
,
794 o_font_set
->font_prim_objs
->next
,
795 temp_list
, NORMAL_FLAG
);
797 if (start_of_char
!= NULL
)
798 start_of_char
= start_of_char
->next
;
800 o_complex_set_color(start_of_char
, color
);
801 o_scale(start_of_char
, size
/2, size
/2);
803 /* Rotate and translate the character to its world position */
804 o_list_rotate_world(0, 0, angle
, start_of_char
);
805 o_list_translate_world(x_offset
, y_offset
, start_of_char
);
808 /* Calculate the position of the next character */
811 x_offset
= (x_offset
) + size
/2*o_font_set
->font_text_size
;
814 y_offset
= (y_offset
) + size
/2*o_font_set
->font_text_size
;
817 x_offset
= (x_offset
) - size
/2*o_font_set
->font_text_size
;
820 y_offset
= (y_offset
) - size
/2*o_font_set
->font_text_size
;
825 if (draw_tabulator
) {
826 gint size_of_tab_in_coord
;
828 /* Get the maximum tab width's in coordinates */
829 size_of_tab_in_coord
= (tab_in_chars
*
830 o_text_width(toplevel
, TAB_CHAR_MODEL
, size
/2));
835 rel_char_coord
= x_offset
- line_start_x
;
836 x_offset
+= (size_of_tab_in_coord
-
837 (rel_char_coord
% size_of_tab_in_coord
));
840 rel_char_coord
= y_offset
- line_start_y
;
841 y_offset
+= (size_of_tab_in_coord
-
842 (rel_char_coord
% size_of_tab_in_coord
));
845 rel_char_coord
= line_start_y
- y_offset
;
846 y_offset
-= (size_of_tab_in_coord
-
847 (rel_char_coord
% size_of_tab_in_coord
));
850 fprintf(stderr
, "o_text_create_string: Angle not supported\n");
855 if (finish_overbar
) {
858 overbar_endx
= x_offset
;
859 overbar_endy
= y_offset
+ overbar_height_offset
;
862 overbar_endx
= x_offset
- overbar_height_offset
;
863 overbar_endy
= y_offset
;
866 overbar_endx
= x_offset
;
867 overbar_endy
= y_offset
- overbar_height_offset
;
870 overbar_endx
= x_offset
+ overbar_height_offset
;
871 overbar_endy
= y_offset
;
874 fprintf(stderr
, "o_text_create_string: Angle not supported\n");
877 /* Now add the overbar (if it is not a zero length overbar) */
878 if ((overbar_startx
!= overbar_endx
)
879 || (overbar_starty
!= overbar_endy
)) {
880 temp_obj
= o_line_new(toplevel
, OBJ_LINE
, color
,
881 overbar_startx
, overbar_starty
,
882 overbar_endx
, overbar_endy
);
883 temp_list
= s_basic_link_object(temp_obj
, temp_list
);
890 x_offset
= line_start_x
;
891 y_offset
= line_start_y
- char_height
* LINE_SPACING
;
894 x_offset
= line_start_x
+ char_height
* LINE_SPACING
;
895 y_offset
= line_start_y
;
898 x_offset
= line_start_x
;
899 y_offset
= line_start_y
+ char_height
* LINE_SPACING
;
902 x_offset
= line_start_x
- char_height
* LINE_SPACING
;
903 y_offset
= line_start_y
;
906 fprintf(stderr
, "o_text_create_string: Angle not supported\n");
909 line_start_x
= x_offset
;
910 line_start_y
= y_offset
;
916 overbar_startx
= x_offset
;
917 overbar_starty
= y_offset
+ overbar_height_offset
;
920 overbar_startx
= x_offset
- overbar_height_offset
;
921 overbar_starty
= y_offset
;
924 overbar_startx
= x_offset
;
925 overbar_starty
= y_offset
- overbar_height_offset
;
928 overbar_startx
= x_offset
+ overbar_height_offset
;
929 overbar_starty
= y_offset
;
932 fprintf(stderr
, "o_text_create_string: Angle not supported\n");
938 toplevel
->page_current
->object_tail
= temp_tail
;
943 static OBJECT
*o_text_new_at_xy(TOPLEVEL
*toplevel
,
944 char type
, int color
, int x
, int y
)
946 OBJECT
*new_node
=NULL
;
949 new_node
= s_toplevel_new_object(toplevel
, type
, "text");
950 new_node
->color
= color
;
952 text
= g_malloc(sizeof (TEXT
));
953 new_node
->text
= text
;
957 text
->toplevel
= g_object_ref(toplevel
);
959 text
->prim_objs
= NULL
;
961 new_node
->copy_func
= &o_text_copy
;
962 new_node
->bounds_recalc_func
= o_text_recalc
;
963 new_node
->draw_func
= text_draw_func
;
964 new_node
->visit_func
= &o_text_visit
;
965 new_node
->destroy_func
= &o_text_destroy
;
970 /*! \brief Creates a text OBJECT and the graphical objects representing it
971 * \par Function Description
972 * Create an OBJECT of type OBJ_TEXT.
973 * Also add the OBJECTs forming the graphical representation of the visible
974 * string, to the text OBJECT's prim_objs list.
976 * \param [in] toplevel The TOPLEVEL object.
977 * \param [in] type OBJ_TEXT (TODO: why bother)
978 * \param [in] color The color of the text.
979 * \param [in] x World x coord of text.
980 * \param [in] y World y coord of text.
981 * \param [in] alignment How text bounding box aligns on (x, y).
982 * \param [in] angle Angle at which text will appear.
983 * \param [in] string The text.
984 * \param [in] size Text size.
985 * \param [in] visibility VISIBLE or INVISIBLE.
986 * \param [in] show_name_value SHOW_NAME_VALUE or friends.
987 * \return Pointer to text OBJECT.
990 * Caller is responsible for string; this function allocates its own copy.
992 OBJECT
*o_text_new(TOPLEVEL
*toplevel
,
993 char type
, int color
, int x
, int y
, int alignment
,
994 int angle
, const char *string
, int size
,
995 int visibility
, int show_name_value
)
997 OBJECT
*new_node
=NULL
;
998 OBJECT
*temp_list
=NULL
;
1003 if (string
== NULL
) {
1007 new_node
= o_text_new_at_xy(toplevel
, type
, color
, x
, y
);
1008 text
= new_node
->text
;
1010 text
->string
= g_strdup (string
);
1011 text
->disp_string
= NULL
; /* We'll fix this up later */
1012 text
->length
= strlen(string
);
1014 text
->alignment
= alignment
;
1015 text
->angle
= angle
;
1017 new_node
->visibility
= visibility
;
1018 new_node
->show_hidden
= toplevel
->show_hidden_default
;
1019 new_node
->show_name_value
= show_name_value
;
1021 update_disp_string (new_node
);
1023 /* now start working on the complex */
1024 temp_list
= o_text_new_head(toplevel
);
1026 if (visibility
== VISIBLE
||
1027 (visibility
== INVISIBLE
&& toplevel
->show_hidden_default
)) {
1028 new_node
->text
->prim_objs
=
1029 o_text_create_string(toplevel
, temp_list
,
1030 text
->disp_string
, size
, color
,
1031 x
, y
, alignment
, angle
);
1032 new_node
->text
->displayed_width
= o_text_width(toplevel
,
1033 text
->disp_string
, size
/2);
1034 new_node
->text
->displayed_height
= o_text_height(text
->disp_string
, size
);
1036 new_node
->text
->prim_objs
= NULL
;
1037 new_node
->text
->displayed_width
= 0;
1038 new_node
->text
->displayed_height
= 0;
1039 s_delete(toplevel
, temp_list
);
1042 /* Update bounding box */
1043 o_text_recalc(new_node
);
1050 /*! \brief update the visual boundaries of the text object
1051 * \par Function Description
1052 * This function updates the boundaries of the object \a o_current.
1054 * \param [in] toplevel The TOPLEVEL object
1055 * \param [in] o_current The OBJECT to update
1057 static void o_text_recalc(OBJECT
*o_current
)
1059 int left
, right
, top
, bottom
;
1061 if (o_current
->visibility
== INVISIBLE
&& !o_current
->show_hidden
) {
1065 if (o_current
->text
->x
== -1 && o_current
->text
->y
== -1) {
1066 /* Off-schematic text has no bounding box. */
1070 if (!world_get_text_bounds(o_current
, &left
, &top
, &right
, &bottom
))
1073 o_current
->w_left
= left
;
1074 o_current
->w_top
= top
;
1075 o_current
->w_right
= right
;
1076 o_current
->w_bottom
= bottom
;
1077 o_current
->w_bounds_valid
= TRUE
;
1080 static void o_text_visit(OBJECT
*o_current
,
1081 void (*fn
)(OBJECT
*, void *),
1086 g_return_if_fail(o_current
->type
== OBJ_TEXT
);
1088 if (!o_current
->complex->prim_objs
) {
1089 /* Text is invisible. */
1093 for (prim_obj
= o_current
->text
->prim_objs
->next
;
1095 prim_obj
= prim_obj
->next
) {
1096 (*fn
)(prim_obj
, context
);
1100 static void o_text_destroy(OBJECT
*o_current
)
1102 g_free(o_current
->text
->string
);
1103 g_free(o_current
->text
->disp_string
);
1105 if (o_current
->text
->prim_objs
) {
1106 s_delete_list_fromstart(o_current
->text
->toplevel
,
1107 o_current
->text
->prim_objs
);
1110 g_object_unref(o_current
->text
->toplevel
);
1112 g_free(o_current
->text
);
1115 /*! \brief read a text object from a char buffer
1116 * \par Function Description
1117 * This function reads a text object from the textbuffer \a tb and
1118 * the text starting with the line \a firstline.
1120 * \param [in] toplevel The TOPLEVEL object
1121 * \param [in] first_line the first line of the text
1122 * \param [in] tb a text buffer (usually a line of a schematic file)
1123 * \param [in] release_ver The release number gEDA
1124 * \param [in] fileformat_ver a integer value of the file format
1125 * \return The object list
1127 OBJECT
*o_text_read(TOPLEVEL
*toplevel
,
1128 const char *first_line
,
1130 unsigned int release_ver
,
1131 unsigned int fileformat_ver
)
1139 int show_name_value
;
1144 char* string
= NULL
;
1147 if (fileformat_ver
>= 1) {
1148 sscanf(first_line
, "%c %d %d %d %d %d %d %d %d %d\n", &type
, &x
, &y
,
1150 &visibility
, &show_name_value
,
1151 &angle
, &alignment
, &num_lines
);
1152 } else if (release_ver
< VERSION_20000220
) {
1153 /* yes, above less than (not less than and equal) is correct. The format */
1154 /* change occurred in 20000220 */
1155 sscanf(first_line
, "%c %d %d %d %d %d %d %d\n", &type
, &x
, &y
,
1157 &visibility
, &show_name_value
,
1159 alignment
= LOWER_LEFT
; /* older versions didn't have this */
1160 num_lines
= 1; /* only support a single line */
1162 sscanf(first_line
, "%c %d %d %d %d %d %d %d %d\n", &type
, &x
, &y
,
1164 &visibility
, &show_name_value
,
1165 &angle
, &alignment
);
1166 num_lines
= 1; /* only support a single line */
1170 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
);
1182 s_log_message(_("Found an unsupported text angle [ %c %d %d %d %d %d %d %d %d ]\n"),
1183 type
, x
, y
, color
, size
, visibility
, show_name_value
, angle
, alignment
);
1184 s_log_message(_("Setting angle to 0\n"));
1195 case(MIDDLE_MIDDLE
):
1204 s_log_message(_("Found an unsupported text alignment [ %c %d %d %d %d %d %d %d %d ]\n"),
1205 type
, x
, y
, color
, size
, visibility
, show_name_value
, angle
, alignment
);
1206 s_log_message(_("Setting alignment to LOWER_LEFT\n"));
1207 alignment
= LOWER_LEFT
;
1211 if (color
< 0 || color
> MAX_COLORS
) {
1212 s_log_message(_("Found an invalid color [ %s ]\n"), first_line
);
1213 s_log_message(_("Setting color to WHITE\n"));
1217 g_assert(num_lines
&& num_lines
> 0);
1219 textstr
= g_string_new ("");
1220 for (i
= 0; i
< num_lines
; i
++) {
1223 line
= s_textbuffer_next_line (tb
);
1227 textstr
= g_string_append (textstr
, line
);
1230 /* retrieve the character string from the GString */
1231 string
= g_string_free (textstr
, FALSE
);
1233 string
= remove_last_nl(string
);
1235 /* convert the character string to UTF-8 if necessary */
1236 if (!g_utf8_validate (string
, -1, NULL
)) {
1237 /* if it is not utf-8, it is ISO_8859-15 */
1238 gchar
*tmp
= g_convert (string
, strlen (string
),
1239 "UTF-8", "ISO_8859-15",
1242 fprintf (stderr
, "Failed to convert text string to UTF-8: %s.\n",
1245 /* successfully converted string, now use tmp as string */
1251 new_obj
= o_text_new(toplevel
, type
, color
, x
, y
,
1252 alignment
, angle
, string
,
1253 size
, visibility
, show_name_value
);
1259 /*! \brief read and set infos of a font object
1260 * \par Function Description
1261 * This function reads the font definition buffer \a buf and sets
1262 * the width of a character. This function also deals with the special,
1263 * invisible character space and newline.
1265 * \param [in] buf the font definition according to the geda file format
1266 * \todo Investigate why the TAB character is not defined here.
1268 void o_text_set_info_font(char buf
[])
1272 gunichar character
=0;
1278 string
= remove_nl (buf
);
1280 /* parse the font info: */
1281 buf_ptr
= (gchar
*)string
;
1284 if (type
!= INFO_FONT
) {
1285 g_critical ("o_text_set_info_font: Bad font type '%c', expected '%c'\n",
1290 while (buf_ptr
!= NULL
&& *buf_ptr
== ' ') buf_ptr
++;
1292 if (buf_ptr
!= NULL
&& *buf_ptr
!= '\0') {
1293 character
= g_utf8_get_char_validated (buf_ptr
, -1);
1294 if (character
== (gunichar
)-1) {
1295 s_log_message (_("Failed to validate utf-8 character in font definition: \"%s\".\n"),
1299 /* move buf_ptr just after character */
1300 buf_ptr
= g_utf8_find_next_char (buf_ptr
, NULL
);
1302 while (buf_ptr
!= NULL
&& *buf_ptr
== ' ') buf_ptr
++;
1303 /* - width and special */
1304 if (buf_ptr
!= NULL
) {
1305 sscanf (buf_ptr
, "%d %d\n", &width
, &special
);
1308 /* deal with special characters */
1310 switch (character
) {
1311 case ((gunichar
)'_'):
1313 character
= (gunichar
)' ';
1315 case ((gunichar
)'n'):
1317 character
= (gunichar
)'\n';
1322 o_font_set
= g_hash_table_lookup (font_loaded
,
1323 GUINT_TO_POINTER ((gunichar
)character
));
1324 if (o_font_set
!= NULL
) {
1325 o_font_set
->font_text_size
= width
;
1328 gint l
= g_unichar_to_utf8 (character
, outbuf
);
1331 "o_text_set_info_font: character %s not found!!!\n", outbuf
);
1336 /*! \brief Create a string representation of the text object
1337 * \par Function Description
1338 * This function takes a text \a object and return a string
1339 * according to the file format definition.
1341 * \param [in] object a text OBJECT
1342 * \return the string representation of the text OBJECT
1344 char *o_text_save(OBJECT
*object
)
1353 x
= object
->text
->x
;
1354 y
= object
->text
->y
;
1356 string
= object
->text
->string
;
1357 size
= object
->text
->size
;
1359 /* Use the right color */
1360 if (object
->saved_color
== -1) {
1361 color
= object
->color
;
1363 color
= object
->saved_color
;
1366 /* string can have multiple lines (separated by \n's) */
1367 num_lines
= o_text_num_lines(string
);
1369 buf
= g_strdup_printf("%c %d %d %d %d %d %d %d %d %d\n%s", object
->type
,
1370 x
, y
, color
, size
, object
->visibility
,
1371 object
->show_name_value
, object
->text
->angle
,
1372 object
->text
->alignment
, num_lines
, string
);
1377 /*! \brief recreate the graphics of a text object
1378 * \par Function Description
1379 * This function updates the underlying primary of the text object
1382 * \param o_current The text object to update
1384 void o_text_recreate(OBJECT
*o_current
)
1388 TEXT
*text
= o_current
->text
;
1390 update_disp_string (o_current
);
1392 o_list_delete_rest(text
->toplevel
, text
->prim_objs
);
1394 if (o_current
->visibility
== VISIBLE
||
1395 (o_current
->visibility
== INVISIBLE
&& o_current
->show_hidden
)) {
1397 /* need to create that head node if complex is null */
1398 if (text
->prim_objs
== NULL
) {
1399 text
->prim_objs
= o_text_new_head(text
->toplevel
);
1402 text
->prim_objs
= o_text_create_string (text
->toplevel
,
1412 o_complex_set_saved_color_only(text
->prim_objs
,
1413 o_current
->saved_color
);
1414 text
->displayed_width
= o_text_width (text
->toplevel
,
1417 text
->displayed_height
= o_text_height (text
->disp_string
,
1420 /* make sure list is truly free */
1421 s_delete_list_fromstart(text
->toplevel
, text
->prim_objs
);
1422 text
->prim_objs
= NULL
;
1423 text
->displayed_width
= 0;
1424 text
->displayed_height
= 0;
1427 o_text_recalc(o_current
);
1433 /*! \brief move a text object
1434 * \par Function Description
1435 * This function changes the position of a text object \a o_current.
1437 * \param [in] dx The x-distance to move the object
1438 * \param [in] dy The y-distance to move the object
1439 * \param [in] o_current The text OBJECT to be moved
1441 void o_text_translate_world(int dx
, int dy
, OBJECT
*o_current
)
1443 o_current
->text
->x
= o_current
->text
->x
+ dx
;
1444 o_current
->text
->y
= o_current
->text
->y
+ dy
;
1446 o_list_translate_world(dx
, dy
, o_current
->text
->prim_objs
);
1448 /* Update bounding box */
1449 o_text_recalc(o_current
);
1452 /*! \brief create a copy of a text object
1453 * \par Function Description
1454 * This function creates a copy of the text object \a o_current.
1456 * \param [in] toplevel The TOPLEVEL object
1457 * \param [in] o_current The object that is copied
1458 * \return a new text object
1460 static OBJECT
*o_text_copy(TOPLEVEL
*toplevel
, OBJECT
*o_current
)
1465 if (o_current
->saved_color
== -1) {
1466 color
= o_current
->color
;
1468 color
= o_current
->saved_color
;
1471 new_obj
= o_text_new (toplevel
, OBJ_TEXT
, color
,
1472 o_current
->text
->x
, o_current
->text
->y
,
1473 o_current
->text
->alignment
,
1474 o_current
->text
->angle
,
1475 o_current
->text
->string
,
1476 o_current
->text
->size
,
1477 o_current
->visibility
,
1478 o_current
->show_name_value
);
1483 /*! \brief delete a font set
1484 * \par Function Description
1485 * This is a GHRFunc function that deletes a single font set.
1487 * \param [in] key The hash key (the font character)
1488 * \param [in] value The value of the hash table (the font object)
1489 * \param [in] user_data Data supplied by the user (the TOPLEVEL object)
1491 static gboolean
delete_font_set (gpointer key G_GNUC_UNUSED
, gpointer value
,
1494 OBJECT
*tmp
= (OBJECT
*)value
;
1495 TOPLEVEL
*toplevel
= (TOPLEVEL
*)user_data
;
1498 if (tmp
->font_prim_objs
!= NULL
) {
1499 s_delete_list_fromstart (toplevel
, tmp
->font_prim_objs
);
1500 tmp
->font_prim_objs
= NULL
;
1502 /* do not use s_delete() as tmp is not fully initialized */
1505 /* Do not free tmp here since it will be freed with the function */
1506 /* that was specified when the hash table was created. */
1512 /*! \brief free the font hash tables
1513 * \par Function Description
1514 * This function destroys the two global font hash tables
1515 * <b>font_loaded</b> and <b>font_char_to_file</b>
1517 void o_text_freeallfonts(TOPLEVEL
*toplevel
)
1519 /* destroy the char-to-objects hastable */
1520 g_hash_table_foreach_remove (font_loaded
,
1523 g_hash_table_destroy (font_loaded
);
1526 /* destroy the font-to-filename hashtable */
1527 g_hash_table_destroy (font_char_to_file
);
1528 font_char_to_file
= NULL
;
1532 /*! \brief write a text string to a postscript file
1533 * \par Function Description
1534 * This function writes the single \a string into the postscript file \a fp.
1536 * \param [in] fp pointer to a FILE structure
1537 * \param [in] string The string to print
1538 * \param [in] unicode_count Number of items in the unicode table
1539 * \param [in] unicode_table Table of unicode items
1541 * \todo investigate whether the TAB character is handled correctly
1543 void o_text_print_text_string(FILE *fp
, char *string
, int unicode_count
,
1544 gunichar
*unicode_table
)
1548 gunichar current_char
, c
;
1559 while (aux
&& ((gunichar
) (*aux
) != 0)) {
1560 current_char
= g_utf8_get_char_validated(aux
, -1);
1561 if (current_char
== '(' || current_char
== ')' || current_char
== '\\') {
1568 if (unicode_count
) {
1569 for (j
= 0; j
< unicode_count
; j
++)
1570 if (c
== unicode_table
[j
]) {
1571 current_char
= j
+ 128;
1578 if (current_char
== '\t') {
1579 /* Output eight spaces instead of the tab character */
1582 fprintf(fp
, "%c", current_char
);
1585 aux
= g_utf8_find_next_char(aux
, NULL
);
1591 /*! \brief print a text object into a postscript file
1592 * \par Function Description
1593 * This function writes the postscript representation of the text object
1594 * \a o_current into the the file \a fp.
1595 * \param [in] toplevel The TOPLEVEL object
1596 * \param [in] fp pointer to a FILE structure
1597 * \param [in] o_current The OBJECT to print
1598 * \param [in] origin_x x-coord of the postscript origin
1599 * \param [in] origin_y y-coord of the postscript origin
1600 * \param [in] unicode_count Number of items in the unicode table
1601 * \param [in] unicode_table Table of unicode items
1603 void o_text_print(TOPLEVEL
*toplevel
, FILE *fp
, OBJECT
*o_current
,
1604 int origin_x
, int origin_y
,
1605 int unicode_count
, gunichar
*unicode_table
)
1608 char *centering_control
= NULL
;
1610 char *output_string
= NULL
;
1613 int x
, y
, angle
, len
, char_height
;
1617 if (!o_current
->text
->string
) {
1621 if (toplevel
->print_color
) {
1622 f_print_set_color(fp
, o_current
->color
);
1626 if (o_attrib_get_name_value(o_current
->text
->string
, &name
, &value
)) {
1627 switch(o_current
->show_name_value
) {
1628 case(SHOW_NAME_VALUE
):
1629 output_string
= g_strdup(o_current
->text
->string
);
1633 output_string
= g_strdup(name
);
1637 output_string
= g_strdup(value
);
1641 output_string
= g_strdup(o_current
->text
->string
);
1644 /* Apply alignment map to apply when text is 180 degrees rotated.
1645 * We want the text on the printer to appear upside right, even
1646 * though mathematically it aught to be upside down. To make this
1647 * work, we will reset the angle to 0, when it's equal to 180
1648 * degrees, then apply a transformation to the origin location as if
1649 * the text was rotated about that point. E.g. if the text origin
1650 * was at the lower left, and the text was rotated by 180 degrees,
1651 * it would be as if the origin was at the upper right. The same
1652 * reasoning has been applied to all 8 other text origins.
1653 * MIDDLE_MIDDLE maps to itself.
1655 alignment
= o_current
->text
->alignment
;
1656 angle
= o_current
->text
->angle
;
1658 angle
= 0; /* reset angle to 0 to make text upright */
1660 case(LOWER_LEFT
): alignment
= UPPER_RIGHT
;
1662 case(MIDDLE_LEFT
): alignment
= MIDDLE_RIGHT
;
1664 case(UPPER_LEFT
): alignment
= LOWER_RIGHT
;
1666 case(LOWER_MIDDLE
): alignment
= UPPER_MIDDLE
;
1668 case(MIDDLE_MIDDLE
): alignment
= MIDDLE_MIDDLE
;
1670 case(UPPER_MIDDLE
): alignment
= LOWER_MIDDLE
;
1672 case(LOWER_RIGHT
): alignment
= UPPER_LEFT
;
1674 case(MIDDLE_RIGHT
): alignment
= MIDDLE_LEFT
;
1676 case(UPPER_RIGHT
): alignment
= LOWER_LEFT
;
1681 /* Create an appropriate control string for the centering. */
1683 /* hcenter rjustify vcenter vjustify */
1684 case(LOWER_LEFT
): centering_control
= "false false false false";
1686 case(MIDDLE_LEFT
): centering_control
= "false false true false";
1688 case(UPPER_LEFT
): centering_control
= "false false false true";
1690 case(LOWER_MIDDLE
): centering_control
= "true false false false";
1692 case(MIDDLE_MIDDLE
): centering_control
= "true false true false";
1694 case(UPPER_MIDDLE
): centering_control
= "true false false true";
1696 case(LOWER_RIGHT
): centering_control
= "false true false false";
1698 case(MIDDLE_RIGHT
): centering_control
= "false true true false";
1700 case(UPPER_RIGHT
): centering_control
= "false true false true";
1704 char_height
= o_text_height("a", o_current
->text
->size
);
1705 fprintf(fp
,"%s %f [",centering_control
,(float)(char_height
*LINE_SPACING
));
1707 /* split the line at each newline and print them */
1708 p
= output_string
; /* Current point */
1709 s
= output_string
; /* Start of the current string */
1710 len
= strlen(output_string
)+1;
1712 /* Have we reached the end of a line? */
1713 if((*p
== '\n') || (*p
== '\0')) {
1714 /* Yes, replace the newline with a NULL and output the string */
1716 o_text_print_text_string(fp
,s
,unicode_count
,unicode_table
);
1717 /* Update output string start for next string */
1718 s
= p
+1; /* One past the current character. */
1720 p
++; /* Advance to next character */
1721 len
--; /* Keep track of how many characters left to process */
1724 /* Finish up with the rest of the text print command */
1725 /* Collect pertinent info about the text location */
1726 x
= o_current
->text
->x
;
1727 y
= o_current
->text
->y
;
1728 font_size
= (((float)(o_current
->text
->size
))
1729 * toplevel
->postscript_font_scale
/ 72.0 * 1000.0);
1730 fprintf(fp
,"] %d %d %d %f text\n",angle
,x
,y
,font_size
);
1733 g_free(output_string
);
1739 /*! \brief rotate a text object around a centerpoint
1740 * \par Function Description
1741 * This function rotates a text \a object around the point
1742 * (\a world_centerx, \a world_centery).
1744 * \param [in] world_centerx x-coord of the rotation center
1745 * \param [in] world_centery y-coord of the rotation center
1746 * \param [in] angle The angle to rotate the text object
1747 * \param [in] object The text object
1748 * \note only steps of 90 degrees are allowed for the \a angle
1750 void o_text_rotate_world(int world_centerx
, int world_centery
,
1751 int angle
, OBJECT
*object
)
1756 g_return_if_fail(object
!= NULL
);
1757 g_return_if_fail(object
->type
== OBJ_TEXT
);
1759 object
->text
->angle
= ( object
->text
->angle
+ angle
) % 360;
1761 x
= object
->text
->x
+ (-world_centerx
);
1762 y
= object
->text
->y
+ (-world_centery
);
1764 rotate_point_90(x
, y
, angle
, &newx
, &newy
);
1766 x
= newx
+ (world_centerx
);
1767 y
= newy
+ (world_centery
);
1769 o_text_translate_world(x
-object
->text
->x
, y
-object
->text
->y
, object
);
1771 o_text_recreate(object
);
1775 /*! \brief mirror a text object horizontaly at a centerpoint
1776 * \par Function Description
1777 * This function mirrors a text \a object horizontaly at the point
1778 * (\a world_centerx, \a world_centery).
1780 * \param [in] world_centerx x-coord of the mirror position
1781 * \param [in] world_centery y-coord of the mirror position
1782 * \param [in] object The text object
1784 void o_text_mirror_world(int world_centerx
, int world_centery
,
1790 origx
= object
->text
->x
;
1791 origy
= object
->text
->y
;
1793 x
= origx
+ (-world_centerx
);
1794 y
= origy
+ (-world_centery
);
1796 if ((object
->text
->angle
%180)==0) {
1797 switch(object
->text
->alignment
) {
1799 object
->text
->alignment
=LOWER_RIGHT
;
1803 object
->text
->alignment
=MIDDLE_RIGHT
;
1807 object
->text
->alignment
=UPPER_RIGHT
;
1811 object
->text
->alignment
=LOWER_LEFT
;
1815 object
->text
->alignment
=MIDDLE_LEFT
;
1819 object
->text
->alignment
=UPPER_LEFT
;
1826 switch(object
->text
->alignment
) {
1828 object
->text
->alignment
=UPPER_LEFT
;
1832 object
->text
->alignment
=LOWER_LEFT
;
1836 object
->text
->alignment
=UPPER_RIGHT
;
1840 object
->text
->alignment
=LOWER_RIGHT
;
1844 object
->text
->alignment
=UPPER_MIDDLE
;
1848 object
->text
->alignment
=LOWER_MIDDLE
;
1856 object
->text
->x
= -x
+ (world_centerx
);
1857 object
->text
->y
= y
+ (world_centery
);
1859 o_text_recreate(object
);
1862 /*! \brief Calculates the distance between the given point and the closest
1863 * point on the text.
1865 * This function will calculate the distance to the text regardless
1866 * if the text is visible or not.
1868 * \param [in] text the text of the OBJECT
1869 * \param [in] x The x coordinate of the given point.
1870 * \param [in] y The y coordinate of the given point.
1871 * \return The shortest distance from the object to the point. If the
1872 * distance cannot be calculated, this function returns a really large
1873 * number (G_MAXDOUBLE). With an invalid parameter, this function
1874 * returns G_MAXDOUBLE.
1876 gdouble
o_text_shortest_distance(TEXT
const *text
, gint x
, gint y
)
1879 gdouble shortest_distance
= G_MAXDOUBLE
;
1883 g_critical("o_text_shortest_distance(): text == NULL\n");
1887 temp
= text
->prim_objs
;
1893 while (temp
!= NULL
) {
1894 distance
= o_shortest_distance(temp
, x
, y
);
1896 shortest_distance
= min(shortest_distance
, distance
);
1901 return shortest_distance
;
1904 /*! \brief Set the string displayed by a text object.
1905 * \par Function Description
1906 * Updates the text object with a new text string.
1908 * \param [in] obj The text object.
1909 * \param [in] new_string The new value.
1911 void o_text_set_string(OBJECT
*obj
, const gchar
*new_string
)
1913 g_return_if_fail(new_string
!= NULL
);
1914 o_text_take_string(obj
, g_strdup(new_string
));
1917 void o_text_take_string(OBJECT
*obj
, gchar
*new_string
)
1919 g_return_if_fail (obj
!= NULL
);
1920 g_return_if_fail (obj
->type
== OBJ_TEXT
);
1921 g_return_if_fail (obj
->text
!= NULL
);
1922 g_return_if_fail (new_string
!= NULL
);
1924 g_free (obj
->text
->string
);
1925 obj
->text
->string
= new_string
;
1927 g_signal_emit_by_name(obj
, "changed");
1928 if (obj
->attached_to
) {
1929 g_signal_emit_by_name(obj
->attached_to
, "attribs-changed");
1933 /*! \brief Get the string displayed by a text object.
1934 * \par Function Description
1935 * Retrieve the text string from a text object. The returned string
1936 * should be treated as constant.
1938 * \param [in] obj The text object.
1939 * \return The text object's string, or NULL on failure.
1941 const gchar
*o_text_get_string(OBJECT
const *obj
)
1943 g_return_val_if_fail (obj
!= NULL
, NULL
);
1944 g_return_val_if_fail (obj
->type
== OBJ_TEXT
, NULL
);
1945 g_return_val_if_fail (obj
->text
!= NULL
, NULL
);
1947 return obj
->text
->string
;
1950 /*! \todo Finish function documentation!!!
1952 * \par Function Description
1955 * The object passed in should be the REAL object, NOT any copy in any
1958 void o_text_change(OBJECT
*object
, char const *string
, int visibility
, int show
)
1960 if (object
== NULL
) {
1964 if (object
->type
!= OBJ_TEXT
) {
1968 /* FIXME: Block changed:: until all changes are done. */
1970 /* second change the real object */
1971 o_text_set_string(object
, string
);
1973 object
->visibility
= visibility
;
1974 object
->show_name_value
= show
;
1975 g_signal_emit_by_name(object
, "changed");