1 /* Dia -- an diagram creation/manipulation program
2 * Copyright (C) 1998 Alexander Larsson
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
26 #include <gdk/gdkkeysyms.h>
28 #include "propinternals.h"
31 #include "diarenderer.h"
32 #include "diagramdata.h"
33 #include "objchange.h"
35 #ifdef USE_TEXTLINE_FOR_LINES
39 static int text_key_event(Focus
*focus
, guint keysym
,
40 const gchar
*str
, int strlen
,
41 ObjectChange
**change
);
52 struct TextObjectChange
{
53 ObjectChange obj_change
;
56 enum change_type type
;
63 #define CURSOR_HEIGHT_RATIO 20
65 /* *** Encapsulation functions for transferring to text_line *** */
67 text_get_line(Text
*text
, int line
)
69 #ifdef USE_TEXTLINE_FOR_LINES
70 return text_line_get_string(text
->lines
[line
]);
72 return text
->line
[line
];
76 /** Raw sets one line to a given text, not copying, not freeing.
79 text_set_line(Text
*text
, int line_no
, gchar
*line
)
81 #ifdef USE_TEXTLINE_FOR_LINES
82 text_line_set_string(text
->lines
[line_no
], line
);
84 text
->line
[line_no
] = line
;
88 /** Set the text of a line, freeing, copying and mallocing as required.
89 * Updates strlen and row_width entries, but not max_width.
92 text_set_line_text(Text
*text
, int line_no
, gchar
*line
)
94 #ifdef USE_TEXTLINE_FOR_LINES
95 text_set_line(text
, line_no
, g_strdup(line
));
96 text
->strlen
[line_no
] = strlen(line
);
98 if (text
->line
[line_no
] != NULL
) {
99 g_free(text
->line
[line_no
]);
101 text_set_line(text
, line_no
, g_strdup(line
));
102 text
->row_width
[line_no
] =
103 dia_font_string_width(line
, text
->font
, text
->height
);
104 text
->strlen
[line_no
] = strlen(line
);
108 /** Delete the line, freeing appropriately and moving stuff up.
109 * This function circumvents the normal free/alloc cycle of
110 * text_set_line_text. */
112 text_delete_line(Text
*text
, int line_no
)
116 #ifdef USE_TEXTLINE_FOR_LINES
117 g_free(text
->lines
[line_no
]);
118 for (i
= line_no
; i
< text
->numlines
- 1; i
++) {
119 text
->lines
[i
] = text
->lines
[i
+1];
120 text
->strlen
[i
] = text
->strlen
[i
+1];
123 text
->lines
= g_realloc(text
->lines
, sizeof(TextLine
*)*text
->numlines
);
124 text
->strlen
= g_realloc(text
->strlen
, sizeof(int)*text
->numlines
);
126 if (text
->line
[line_no
] != NULL
) {
127 free(text
->line
[line_no
]);
130 for (i
= line_no
; i
< text
->numlines
- 1; i
++) {
131 text_set_line(text
, i
, text_get_line(text
, i
+1));
132 text
->strlen
[i
] = text
->strlen
[i
+1];
133 text
->row_width
[i
] = text
->row_width
[i
+1];
137 text
->line
= g_realloc(text
->line
, sizeof(char *)*text
->numlines
);
138 text
->strlen
= g_realloc(text
->strlen
, sizeof(int)*text
->numlines
);
139 text
->row_width
= g_realloc(text
->row_width
, sizeof(real
)*text
->numlines
);
143 /** Insert a new (empty) line at line_no.
144 * This function circumvents the normal free/alloc cycle of
145 * text_set_line_text. */
147 text_insert_line(Text
*text
, int line_no
)
150 #ifdef USE_TEXTLINE_FOR_LINES
152 text
->lines
= g_realloc(text
->lines
, sizeof(char *)*text
->numlines
);
153 text
->strlen
= g_realloc(text
->strlen
, sizeof(int)*text
->numlines
);
155 for (i
= text
->numlines
- 1; i
> line_no
; i
--) {
156 text
->lines
[i
] = text
->lines
[i
- 1];
157 text
->strlen
[i
] = text
->strlen
[i
- 1];
159 text
->lines
[line_no
] = text_line_new("", text
->font
, text
->height
);;
162 text
->line
= g_realloc(text
->line
, sizeof(char *)*text
->numlines
);
163 text
->strlen
= g_realloc(text
->strlen
, sizeof(int)*text
->numlines
);
164 text
->row_width
= g_realloc(text
->row_width
, sizeof(real
)*text
->numlines
);
166 for (i
= text
->numlines
- 1; i
> line_no
; i
--) {
167 text_set_line(text
, i
, text_get_line(text
, i
- 1));
168 text
->strlen
[i
] = text
->strlen
[i
- 1];
169 text
->row_width
[i
] = text
->row_width
[i
- 1];
171 text
->line
[line_no
] = NULL
;
175 /** Get the in-diagram width of the given line.
176 * @param text The text object;
177 * @param line_no The index of the line in the text object, starting at 0.
178 * @returns The width in cm of the indicated line.
181 text_get_line_width(Text
*text
, int line_no
)
183 #ifdef USE_TEXTLINE_FOR_LINES
184 return text_line_get_width(text
->lines
[line_no
]);
186 return text
->row_width
[line_no
];
190 /** Get the number of characters of the given line.
191 * @param text The text object;
192 * @param line_no The index of the line in the text object, starting at 0.
193 * @returns The number of UTF-8 characters of the indicated line.
196 text_get_line_strlen(Text
*text
, int line_no
)
198 return text
->strlen
[line_no
];
202 text_get_max_width(Text
*text
)
204 return text
->max_width
;
207 /** Get the *average* ascent of this Text object.
208 * @param a Text object
209 * @returns the average of the ascents of each line (height above baseline)
212 text_get_ascent(Text
*text
)
217 /** Get the *average* descent of this Text object.
218 * @param a Text object
219 * @returns the average of the descents of each line (height below baseline)
222 text_get_descent(Text
*text
)
224 return text
->descent
;
227 static ObjectChange
*text_create_change(Text
*text
, enum change_type type
,
228 gunichar ch
, int pos
, int row
);
231 calc_width(Text
*text
)
237 for (i
= 0; i
< text
->numlines
; i
++) {
238 #ifndef USE_TEXTLINE_FOR_LINES
240 dia_font_string_width(text_get_line(text
, i
), text
->font
, text
->height
);
242 width
= MAX(width
, text_get_line_width(text
, i
));
245 text
->max_width
= width
;
249 calc_ascent_descent(Text
*text
)
251 real sig_a
= 0.0,sig_d
= 0.0;
254 for ( i
= 0; i
< text
->numlines
; i
++) {
255 #ifdef USE_TEXTLINE_FOR_LINES
256 sig_a
+= text_line_get_ascent(text
->lines
[i
]);
257 sig_d
+= text_line_get_descent(text
->lines
[i
]);
259 sig_a
+= dia_font_ascent(text_get_line(text
, i
), text
->font
, text
->height
);
260 sig_d
+= dia_font_descent(text_get_line(text
, i
), text
->font
, text
->height
);
264 text
->ascent
= sig_a
/ (real
)text
->numlines
;
265 text
->descent
= sig_d
/ (real
)text
->numlines
;
269 free_string(Text
*text
)
273 #ifdef USE_TEXTLINE_FOR_LINES
274 for (i
=0;i
<text
->numlines
;i
++) {
275 text_line_destroy(text
->lines
[i
]);
281 g_free(text
->strlen
);
284 for (i
=0;i
<text
->numlines
;i
++) {
285 g_free(text_get_line(text
, i
));
291 g_free(text
->strlen
);
294 g_free(text
->row_width
);
295 text
->row_width
= NULL
;
300 set_string(Text
*text
, const char *string
)
309 while ( (s
= g_utf8_strchr(s
, -1, '\n')) != NULL
) {
312 s
= g_utf8_next_char(s
);
315 text
->numlines
= numlines
;
316 #ifdef USE_TEXTLINE_FOR_LINES
317 text
->lines
= g_new0(TextLine
*, numlines
);
318 for (i
= 0; i
< numlines
; i
++) {
319 text
->lines
[i
] = text_line_new("", text
->font
, text
->height
);
321 text
->strlen
= g_new(int, numlines
);
323 text
->line
= g_new0(char *, numlines
);
324 text
->strlen
= g_new(int, numlines
);
325 text
->row_width
= g_new(real
, numlines
);
330 if (string
== NULL
) {
331 text_set_line_text(text
, 0, "");
335 for (i
= 0; i
< numlines
; i
++) {
337 s2
= g_utf8_strchr(s
, -1, '\n');
338 if (s2
== NULL
) { /* No newline */
341 string_line
= g_strndup(s
, s2
- s
);
342 text_set_line_text(text
, i
, string_line
);
346 s
= g_utf8_next_char(s
);
350 if (text
->cursor_row
>= text
->numlines
) {
351 text
->cursor_row
= text
->numlines
- 1;
354 if (text
->cursor_pos
> text_get_line_strlen(text
, text
->cursor_row
)) {
355 text
->cursor_pos
= text_get_line_strlen(text
, text
->cursor_row
);
360 text_set_string(Text
*text
, const char *string
)
362 #ifdef USE_TEXTLINE_FOR_LINES
363 if (text
->lines
!= NULL
)
366 if (text
->line
!= NULL
)
370 set_string(text
, string
);
374 new_text(const char *string
, DiaFont
*font
, real height
,
375 Point
*pos
, Color
*color
, Alignment align
)
379 text
= g_new(Text
, 1);
381 text
->font
= dia_font_ref(font
);
382 text
->height
= height
;
384 text
->position
= *pos
;
385 text
->color
= *color
;
386 text
->alignment
= align
;
388 text
->cursor_pos
= 0;
389 text
->cursor_row
= 0;
391 text
->focus
.obj
= NULL
;
392 text
->focus
.has_focus
= FALSE
;
393 text
->focus
.user_data
= (void *)text
;
394 text
->focus
.key_event
= text_key_event
;
396 set_string(text
, string
);
398 calc_ascent_descent(text
);
404 text_copy(Text
*text
)
409 copy
= g_new(Text
, 1);
410 copy
->numlines
= text
->numlines
;
411 #ifdef USE_TEXTLINE_FOR_LINES
412 copy
->lines
= g_new(TextLine
*, text
->numlines
);
413 copy
->strlen
= g_new(int, text
->numlines
);
415 copy
->line
= g_malloc(sizeof(char *)*text
->numlines
);
416 copy
->strlen
= g_malloc(sizeof(int)*text
->numlines
);
417 copy
->row_width
= g_malloc(sizeof(real
)*text
->numlines
);
420 copy
->font
= dia_font_ref(text
->font
);
421 copy
->height
= text
->height
;
422 copy
->position
= text
->position
;
423 copy
->color
= text
->color
;
424 copy
->alignment
= text
->alignment
;
426 for (i
=0;i
<text
->numlines
;i
++) {
427 text_set_line_text(copy
, i
, text_get_line(text
, i
));
430 copy
->cursor_pos
= 0;
431 copy
->cursor_row
= 0;
432 copy
->focus
.obj
= NULL
;
433 copy
->focus
.has_focus
= FALSE
;
434 copy
->focus
.user_data
= (void *)copy
;
435 copy
->focus
.key_event
= text_key_event
;
437 copy
->ascent
= text
->ascent
;
438 copy
->descent
= text
->descent
;
439 copy
->max_width
= text
->max_width
;
445 text_destroy(Text
*text
)
448 dia_font_unref(text
->font
);
453 text_set_height(Text
*text
, real height
)
456 text
->height
= height
;
457 #ifdef USE_TEXTLINE_FOR_LINES
458 for (i
= 0; i
< text
->numlines
; i
++) {
459 text_line_set_height(text
->lines
[i
], height
);
463 calc_ascent_descent(text
);
467 text_set_font(Text
*text
, DiaFont
*font
)
469 DiaFont
*old_font
= text
->font
;
472 text
->font
= dia_font_ref(font
);
473 dia_font_unref(old_font
);
475 #ifdef USE_TEXTLINE_FOR_LINES
476 for (i
= 0; i
< text
->numlines
; i
++) {
477 text_line_set_font(text
->lines
[i
], font
);
482 calc_ascent_descent(text
);
486 text_set_position(Text
*text
, Point
*pos
)
488 text
->position
= *pos
;
492 text_set_color(Text
*text
, Color
*col
)
498 text_set_alignment(Text
*text
, Alignment align
)
500 text
->alignment
= align
;
504 text_calc_boundingbox(Text
*text
, Rectangle
*box
)
507 calc_ascent_descent(text
);
509 if (box
== NULL
) return; /* For those who just want the text info
511 box
->left
= text
->position
.x
;
512 switch (text
->alignment
) {
516 box
->left
-= text
->max_width
/ 2.0;
519 box
->left
-= text
->max_width
;
523 box
->right
= box
->left
+ text
->max_width
;
525 box
->top
= text
->position
.y
- text
->ascent
;
527 box
->bottom
= box
->top
+ text
->height
*text
->numlines
+ text
->descent
;
529 if (text
->focus
.has_focus
) {
530 real height
= text
->ascent
+ text
->descent
;
531 if (text
->cursor_pos
== 0) {
532 /* Half the cursor width */
533 box
->left
-= height
/(CURSOR_HEIGHT_RATIO
*2);
535 /* Half the cursor width. Assume that
536 if it isn't at position zero, it might be
537 at the last position possible. */
538 box
->right
+= height
/(CURSOR_HEIGHT_RATIO
*2);
541 /* Account for the size of the cursor top and bottom */
542 box
->top
-= height
/(CURSOR_HEIGHT_RATIO
*2);
543 box
->bottom
+= height
/CURSOR_HEIGHT_RATIO
;
550 text_get_string_copy(Text
*text
)
556 for (i
=0;i
<text
->numlines
;i
++) {
557 num
+= strlen(text_get_line(text
, i
))+1;
564 for (i
=0;i
<text
->numlines
;i
++) {
565 strcat(str
, text_get_line(text
, i
));
566 if (i
!= (text
->numlines
-1)) {
575 text_distance_from(Text
*text
, Point
*point
)
582 topy
= text
->position
.y
- text
->ascent
;
583 bottomy
= topy
+ text
->height
*text
->numlines
;
584 if (point
->y
<= topy
) {
585 dy
= topy
- point
->y
;
587 } else if (point
->y
>= bottomy
) {
588 dy
= point
->y
- bottomy
;
589 line
= text
->numlines
- 1;
592 line
= (int) floor( (point
->y
- topy
) / text
->height
);
595 left
= text
->position
.x
;
596 switch (text
->alignment
) {
600 left
-= text_get_line_width(text
, line
) / 2.0;
603 left
-= text_get_line_width(text
, line
);
606 right
= left
+ text_get_line_width(text
, line
);
608 if (point
->x
<= left
) {
609 dx
= left
- point
->x
;
610 } else if (point
->x
>= right
) {
611 dx
= point
->x
- right
;
620 text_draw(Text
*text
, DiaRenderer
*renderer
)
622 DIA_RENDERER_GET_CLASS(renderer
)->draw_text(renderer
, text
);
624 if ((renderer
->is_interactive
) && (text
->focus
.has_focus
)) {
626 real str_width_first
;
627 real str_width_whole
;
629 real height
= text
->ascent
+text
->descent
;
630 curs_y
= text
->position
.y
- text
->ascent
+ text
->cursor_row
*text
->height
;
632 DIA_RENDERER_GET_CLASS(renderer
)->set_font(renderer
, text
->font
, text
->height
);
635 DIA_RENDERER_GET_CLASS(renderer
)->get_text_width(renderer
,
636 text_get_line(text
, text
->cursor_row
),
639 DIA_RENDERER_GET_CLASS(renderer
)->get_text_width(renderer
,
640 text_get_line(text
, text
->cursor_row
),
641 text_get_line_strlen(text
, text
->cursor_row
));
642 curs_x
= text
->position
.x
+ str_width_first
;
644 switch (text
->alignment
) {
648 curs_x
-= str_width_whole
/ 2.0;
651 curs_x
-= str_width_whole
;
658 p2
.y
= curs_y
+ height
;
660 DIA_RENDERER_GET_CLASS(renderer
)->set_linestyle(renderer
, LINESTYLE_SOLID
);
661 DIA_RENDERER_GET_CLASS(renderer
)->set_linewidth(renderer
, height
/CURSOR_HEIGHT_RATIO
);
662 DIA_RENDERER_GET_CLASS(renderer
)->draw_line(renderer
, &p1
, &p2
, &color_black
);
667 text_grab_focus(Text
*text
, DiaObject
*object
)
669 text
->focus
.obj
= object
;
670 request_focus(&text
->focus
);
674 text_set_cursor_at_end( Text
* text
)
676 text
->cursor_row
= text
->numlines
- 1 ;
677 text
->cursor_pos
= text_get_line_strlen(text
, text
->cursor_row
) ;
680 /* The renderer is only used to determine where the click is, so is not
681 * required when no point is given. */
683 text_set_cursor(Text
*text
, Point
*clicked_point
,
684 DiaRenderer
*renderer
)
686 real str_width_whole
;
687 real str_width_first
;
693 if (clicked_point
!= NULL
) {
694 top
= text
->position
.y
- text
->ascent
;
696 row
= (int)floor((clicked_point
->y
- top
) / text
->height
);
701 if (row
>= text
->numlines
)
702 row
= text
->numlines
- 1;
704 text
->cursor_row
= row
;
705 text
->cursor_pos
= 0;
707 if (!renderer
->is_interactive
) {
708 message_error("Internal error: Select gives non interactive renderer!\n"
709 "val: %d\n", renderer
->is_interactive
);
714 DIA_RENDERER_GET_CLASS(renderer
)->set_font(renderer
, text
->font
, text
->height
);
716 DIA_RENDERER_GET_CLASS(renderer
)->get_text_width(renderer
,
717 text_get_line(text
, row
),
718 text_get_line_strlen(text
, row
));
719 start_x
= text
->position
.x
;
720 switch (text
->alignment
) {
724 start_x
-= str_width_whole
/ 2.0;
727 start_x
-= str_width_whole
;
731 /* Do an ugly linear search for the cursor index:
732 TODO: Change to binary search */
734 for (i
=0;i
<=text_get_line_strlen(text
, row
);i
++) {
736 DIA_RENDERER_GET_CLASS(renderer
)->get_text_width(renderer
, text_get_line(text
, row
), i
);
737 if (clicked_point
->x
- start_x
>= str_width_first
) {
738 text
->cursor_pos
= i
;
743 text
->cursor_pos
= text_get_line_strlen(text
, row
);
745 /* No clicked point, leave cursor where it is */
750 text_join_lines(Text
*text
, int first_line
)
752 gchar
*combined_line
;
755 len1
= text_get_line_strlen(text
, first_line
);
757 combined_line
= g_strconcat(text_get_line(text
, first_line
),
758 text_get_line(text
, first_line
+ 1), NULL
);
759 text_delete_line(text
, first_line
);
760 text_set_line_text(text
, first_line
, combined_line
);
761 g_free(combined_line
);
763 text
->max_width
= MAX(text
->max_width
, text_get_line_width(text
, first_line
));
765 text
->cursor_row
= first_line
;
766 text
->cursor_pos
= len1
;
770 text_delete_forward(Text
*text
)
776 gchar
*utf8_before
, *utf8_after
;
779 row
= text
->cursor_row
;
781 if (text
->cursor_pos
>= text_get_line_strlen(text
, row
)) {
782 if (row
+ 1 < text
->numlines
)
783 text_join_lines(text
, row
);
787 line
= text_get_line(text
, row
);
788 utf8_before
= g_utf8_offset_to_pointer(line
, (glong
)(text
->cursor_pos
));
789 utf8_after
= g_utf8_offset_to_pointer(utf8_before
, 1);
790 str1
= g_strndup(line
, utf8_before
- line
);
791 str
= g_strconcat(str1
, utf8_after
, NULL
);
792 text_set_line_text(text
, row
, str
);
796 if (text
->cursor_pos
> text_get_line_strlen(text
, text
->cursor_row
))
797 text
->cursor_pos
= text_get_line_strlen(text
, text
->cursor_row
);
800 for (i
= 0; i
< text
->numlines
; i
++) {
801 width
= MAX(width
, text_get_line_width(text
, i
));
803 text
->max_width
= width
;
807 text_delete_backward(Text
*text
)
813 gchar
*utf8_before
, *utf8_after
;
816 row
= text
->cursor_row
;
818 if (text
->cursor_pos
<= 0) {
820 text_join_lines(text
, row
-1);
824 line
= text_get_line(text
, row
);
825 utf8_before
= g_utf8_offset_to_pointer(line
, (glong
)(text
->cursor_pos
- 1));
826 utf8_after
= g_utf8_offset_to_pointer(utf8_before
, 1);
827 str1
= g_strndup(line
, utf8_before
- line
);
828 str
= g_strconcat(str1
, utf8_after
, NULL
);
829 text_set_line_text(text
, row
, str
);
834 if (text
->cursor_pos
> text_get_line_strlen(text
, text
->cursor_row
))
835 text
->cursor_pos
= text_get_line_strlen(text
, text
->cursor_row
);
838 for (i
= 0; i
< text
->numlines
; i
++) {
839 width
= MAX(width
, text_get_line_width(text
, i
));
841 text
->max_width
= width
;
845 text_split_line(Text
*text
)
853 /* Split the lines at cursor_pos */
854 line
= text_get_line(text
, text
->cursor_row
);
855 text_insert_line(text
, text
->cursor_row
);
856 utf8_before
= g_utf8_offset_to_pointer(line
, (glong
)(text
->cursor_pos
));
857 str1
= g_strndup(line
, utf8_before
- line
);
858 str2
= g_strdup(utf8_before
); /* Must copy before dealloc */
859 text_set_line_text(text
, text
->cursor_row
, str1
);
860 text_set_line_text(text
, text
->cursor_row
+ 1, str2
);
865 text
->cursor_pos
= 0;
868 for (i
=0;i
<text
->numlines
;i
++) {
869 width
= MAX(width
, text_get_line_width(text
, i
));
871 text
->max_width
= width
;
875 text_insert_char(Text
*text
, gunichar c
)
884 /* Make a string of the the char */
885 unilen
= g_unichar_to_utf8 (c
, ch
);
888 row
= text
->cursor_row
;
890 /* Copy the before and after parts with the new char in between */
891 line
= text_get_line(text
, row
);
892 utf8_before
= g_utf8_offset_to_pointer(line
, (glong
)(text
->cursor_pos
));
893 str1
= g_strndup(line
, utf8_before
- line
);
894 str
= g_strconcat(str1
, ch
, utf8_before
, NULL
);
895 text_set_line_text(text
, row
, str
);
900 text
->max_width
= MAX(text
->max_width
, text_get_line_width(text
, row
));
904 text_key_event(Focus
*focus
, guint keyval
, const gchar
*str
, int strlen
,
905 ObjectChange
**change
)
908 int return_val
= FALSE
;
915 text
= (Text
*)focus
->user_data
;
920 if (text
->cursor_row
<0)
921 text
->cursor_row
= 0;
923 if (text
->cursor_pos
> text_get_line_strlen(text
, text
->cursor_row
))
924 text
->cursor_pos
= text_get_line_strlen(text
, text
->cursor_row
);
929 if (text
->cursor_row
>= text
->numlines
)
930 text
->cursor_row
= text
->numlines
- 1;
932 if (text
->cursor_pos
> text_get_line_strlen(text
, text
->cursor_row
))
933 text
->cursor_pos
= text_get_line_strlen(text
, text
->cursor_row
);
938 if (text
->cursor_pos
<0)
939 text
->cursor_pos
= 0;
943 if (text
->cursor_pos
> text_get_line_strlen(text
, text
->cursor_row
))
944 text
->cursor_pos
= text_get_line_strlen(text
, text
->cursor_row
);
947 text
->cursor_pos
= 0;
950 text
->cursor_pos
= text_get_line_strlen(text
, text
->cursor_row
);
954 row
= text
->cursor_row
;
955 if (text
->cursor_pos
>= text_get_line_strlen(text
, row
)) {
956 if (row
+1 < text
->numlines
) {
957 *change
= text_create_change(text
, TYPE_JOIN_ROW
, 'Q',
958 text
->cursor_pos
, row
);
964 utf
= text_get_line(text
, row
);
965 for (i
= 0; i
< text
->cursor_pos
; i
++)
966 utf
= g_utf8_next_char (utf
);
967 c
= g_utf8_get_char (utf
);
968 *change
= text_create_change (text
, TYPE_DELETE_FORWARD
, c
,
969 text
->cursor_pos
, text
->cursor_row
);
971 text_delete_forward(text
);
975 row
= text
->cursor_row
;
976 if (text
->cursor_pos
<= 0) {
978 *change
= text_create_change(text
, TYPE_JOIN_ROW
, 'Q',
979 text_get_line_strlen(text
, row
-1), row
-1);
985 utf
= text_get_line(text
, row
);
986 for (i
= 0; i
< (text
->cursor_pos
- 1); i
++)
987 utf
= g_utf8_next_char (utf
);
988 c
= g_utf8_get_char (utf
);
989 *change
= text_create_change (text
, TYPE_DELETE_BACKWARD
, c
,
990 text
->cursor_pos
- 1,
993 text_delete_backward(text
);
997 *change
= text_create_change(text
, TYPE_SPLIT_ROW
, 'Q',
998 text
->cursor_pos
, text
->cursor_row
);
999 text_split_line(text
);
1002 if (str
|| (strlen
>0)) {
1005 for (utf
= str
; utf
&& *utf
&& strlen
> 0 ;
1006 utf
= g_utf8_next_char (utf
), strlen
--) {
1007 c
= g_utf8_get_char (utf
);
1009 *change
= text_create_change (text
, TYPE_INSERT_CHAR
, c
,
1010 text
->cursor_pos
, text
->cursor_row
);
1011 text_insert_char (text
, c
);
1020 int text_is_empty(Text
*text
)
1023 for (i
= 0; i
< text
->numlines
; i
++) {
1024 if (text_get_line_strlen(text
, i
) != 0) {
1032 text_delete_all(Text
*text
, ObjectChange
**change
)
1034 if (!text_is_empty(text
)) {
1035 *change
= text_create_change(text
, TYPE_DELETE_ALL
,
1036 0, text
->cursor_pos
, text
->cursor_row
);
1038 text_set_string(text
, "");
1039 calc_ascent_descent(text
);
1046 data_add_text(AttributeNode attr
, Text
*text
)
1051 composite
= data_add_composite(attr
, "text");
1053 str
= text_get_string_copy(text
);
1054 data_add_string(composite_add_attribute(composite
, "string"),
1057 data_add_font(composite_add_attribute(composite
, "font"),
1059 data_add_real(composite_add_attribute(composite
, "height"),
1061 data_add_point(composite_add_attribute(composite
, "pos"),
1063 data_add_color(composite_add_attribute(composite
, "color"),
1065 data_add_enum(composite_add_attribute(composite
, "alignment"),
1071 data_text(AttributeNode text_attr
)
1073 char *string
= NULL
;
1076 Point pos
= {0.0, 0.0};
1080 DataNode composite_node
;
1083 composite_node
= attribute_first_data(text_attr
);
1085 attr
= composite_find_attribute(text_attr
, "string");
1087 string
= data_string(attribute_first_data(attr
));
1090 attr
= composite_find_attribute(text_attr
, "height");
1092 height
= data_real(attribute_first_data(attr
));
1094 attr
= composite_find_attribute(text_attr
, "font");
1096 font
= data_font(attribute_first_data(attr
));
1098 font
= dia_font_new_from_style(DIA_FONT_SANS
,1.0);
1101 attr
= composite_find_attribute(text_attr
, "pos");
1103 data_point(attribute_first_data(attr
), &pos
);
1106 attr
= composite_find_attribute(text_attr
, "color");
1108 data_color(attribute_first_data(attr
), &col
);
1111 attr
= composite_find_attribute(text_attr
, "alignment");
1113 align
= data_enum(attribute_first_data(attr
));
1115 text
= new_text(string
? string
: "", font
, height
, &pos
, &col
, align
);
1116 if (font
) dia_font_unref(font
);
1117 if (string
) g_free(string
);
1122 text_get_attributes(Text
*text
, TextAttributes
*attr
)
1125 old_font
= attr
->font
;
1126 attr
->font
= dia_font_ref(text
->font
);
1127 if (old_font
!= NULL
) dia_font_unref(old_font
);
1128 attr
->height
= text
->height
;
1129 attr
->position
= text
->position
;
1130 attr
->color
= text
->color
;
1131 attr
->alignment
= text
->alignment
;
1135 text_set_attributes(Text
*text
, TextAttributes
*attr
)
1137 if (text
->font
!= attr
->font
) {
1138 DiaFont
*old_font
= text
->font
;
1139 text
->font
= dia_font_ref(attr
->font
);
1140 dia_font_unref(old_font
);
1142 text
->height
= attr
->height
;
1143 text
->position
= attr
->position
;
1144 text
->color
= attr
->color
;
1145 text
->alignment
= attr
->alignment
;
1149 text_change_apply(struct TextObjectChange
*change
, DiaObject
*obj
)
1151 Text
*text
= change
->text
;
1152 switch (change
->type
) {
1153 case TYPE_INSERT_CHAR
:
1154 text
->cursor_pos
= change
->pos
;
1155 text
->cursor_row
= change
->row
;
1156 text_insert_char(text
, change
->ch
);
1158 case TYPE_DELETE_BACKWARD
:
1159 text
->cursor_pos
= change
->pos
+1;
1160 text
->cursor_row
= change
->row
;
1161 text_delete_backward(text
);
1163 case TYPE_DELETE_FORWARD
:
1164 text
->cursor_pos
= change
->pos
;
1165 text
->cursor_row
= change
->row
;
1166 text_delete_forward(text
);
1168 case TYPE_SPLIT_ROW
:
1169 text
->cursor_pos
= change
->pos
;
1170 text
->cursor_row
= change
->row
;
1171 text_split_line(text
);
1174 text_join_lines(text
, change
->row
);
1176 case TYPE_DELETE_ALL
:
1177 set_string(text
, "");
1178 text
->cursor_pos
= 0;
1179 text
->cursor_row
= 0;
1185 text_change_revert(struct TextObjectChange
*change
, DiaObject
*obj
)
1187 Text
*text
= change
->text
;
1188 switch (change
->type
) {
1189 case TYPE_INSERT_CHAR
:
1190 text
->cursor_pos
= change
->pos
;
1191 text
->cursor_row
= change
->row
;
1192 text_delete_forward(text
);
1194 case TYPE_DELETE_BACKWARD
:
1195 text
->cursor_pos
= change
->pos
;
1196 text
->cursor_row
= change
->row
;
1197 text_insert_char(text
, change
->ch
);
1199 case TYPE_DELETE_FORWARD
:
1200 text
->cursor_pos
= change
->pos
;
1201 text
->cursor_row
= change
->row
;
1202 text_insert_char(text
, change
->ch
);
1203 text
->cursor_pos
= change
->pos
;
1204 text
->cursor_row
= change
->row
;
1206 case TYPE_SPLIT_ROW
:
1207 text_join_lines(text
, change
->row
);
1210 text
->cursor_pos
= change
->pos
;
1211 text
->cursor_row
= change
->row
;
1212 text_split_line(text
);
1214 case TYPE_DELETE_ALL
:
1215 set_string(text
, change
->str
);
1216 text
->cursor_pos
= change
->pos
;
1217 text
->cursor_row
= change
->row
;
1223 text_change_free(struct TextObjectChange
*change
) {
1224 g_free(change
->str
);
1227 static ObjectChange
*
1228 text_create_change(Text
*text
, enum change_type type
,
1229 gunichar ch
, int pos
, int row
)
1231 struct TextObjectChange
*change
;
1233 change
= g_new0(struct TextObjectChange
, 1);
1235 change
->obj_change
.apply
= (ObjectChangeApplyFunc
) text_change_apply
;
1236 change
->obj_change
.revert
= (ObjectChangeRevertFunc
) text_change_revert
;
1237 change
->obj_change
.free
= (ObjectChangeFreeFunc
) text_change_free
;
1239 change
->text
= text
;
1240 change
->type
= type
;
1244 if (type
== TYPE_DELETE_ALL
)
1245 change
->str
= text_get_string_copy(text
);
1248 return (ObjectChange
*)change
;
1252 apply_textattr_properties(GPtrArray
*props
,
1253 Text
*text
, const gchar
*textname
,
1254 TextAttributes
*attrs
)
1256 TextProperty
*textprop
=
1257 (TextProperty
*)find_prop_by_name_and_type(props
,textname
,PROP_TYPE_TEXT
);
1260 ((textprop
->common
.experience
& (PXP_LOADED
|PXP_SFO
))==0 )) {
1261 /* most likely we're called after the dialog box has been applied */
1262 text_set_attributes(text
,attrs
);
1269 apply_textstr_properties(GPtrArray
*props
,
1270 Text
*text
, const gchar
*textname
,
1273 TextProperty
*textprop
=
1274 (TextProperty
*)find_prop_by_name_and_type(props
,textname
,PROP_TYPE_TEXT
);
1277 ((textprop
->common
.experience
& (PXP_LOADED
|PXP_SFO
))==0 )) {
1278 /* most likely we're called after the dialog box has been applied */
1279 text_set_string(text
,str
);