2006-12-03 Dimitris Glezos <dimitris@glezos.com>
[dia.git] / lib / text.c
blobd80407aeb1155b885842296cba21335dbed8b4f2
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.
18 #include <config.h>
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <string.h>
23 #include <glib.h>
24 #include <math.h>
26 #include <gdk/gdkkeysyms.h>
28 #include "propinternals.h"
29 #include "text.h"
30 #include "message.h"
31 #include "diarenderer.h"
32 #include "diagramdata.h"
33 #include "objchange.h"
34 #include "textline.h"
36 static int text_key_event(Focus *focus, guint keysym,
37 const gchar *str, int strlen,
38 ObjectChange **change);
40 enum change_type {
41 TYPE_DELETE_BACKWARD,
42 TYPE_DELETE_FORWARD,
43 TYPE_INSERT_CHAR,
44 TYPE_JOIN_ROW,
45 TYPE_SPLIT_ROW,
46 TYPE_DELETE_ALL
49 struct TextObjectChange {
50 ObjectChange obj_change;
52 Text *text;
53 enum change_type type;
54 gunichar ch;
55 int pos;
56 int row;
57 gchar *str;
60 #define CURSOR_HEIGHT_RATIO 20
62 /* *** Encapsulation functions for transferring to text_line *** */
63 gchar *
64 text_get_line(Text *text, int line)
66 return text_line_get_string(text->lines[line]);
69 /** Raw sets one line to a given text, not copying, not freeing.
71 static void
72 text_set_line(Text *text, int line_no, gchar *line)
74 text_line_set_string(text->lines[line_no], line);
77 /** Set the text of a line, freeing, copying and mallocing as required.
78 * Updates strlen and row_width entries, but not max_width.
80 static void
81 text_set_line_text(Text *text, int line_no, gchar *line)
83 text_set_line(text, line_no, g_strdup(line));
84 text->strlen[line_no] = strlen(line);
87 /** Delete the line, freeing appropriately and moving stuff up.
88 * This function circumvents the normal free/alloc cycle of
89 * text_set_line_text. */
90 static void
91 text_delete_line(Text *text, int line_no)
93 int i;
95 g_free(text->lines[line_no]);
96 for (i = line_no; i < text->numlines - 1; i++) {
97 text->lines[i] = text->lines[i+1];
98 text->strlen[i] = text->strlen[i+1];
100 text->numlines -= 1;
101 text->lines = g_realloc(text->lines, sizeof(TextLine *)*text->numlines);
102 text->strlen = g_realloc(text->strlen, sizeof(int)*text->numlines);
105 /** Insert a new (empty) line at line_no.
106 * This function circumvents the normal free/alloc cycle of
107 * text_set_line_text. */
108 static void
109 text_insert_line(Text *text, int line_no)
111 int i;
112 text->numlines += 1;
113 text->lines = g_realloc(text->lines, sizeof(char *)*text->numlines);
114 text->strlen = g_realloc(text->strlen, sizeof(int)*text->numlines);
116 for (i = text->numlines - 1; i > line_no; i--) {
117 text->lines[i] = text->lines[i - 1];
118 text->strlen[i] = text->strlen[i - 1];
120 text->lines[line_no] = text_line_new("", text->font, text->height);;
123 /** Get the in-diagram width of the given line.
124 * @param text The text object;
125 * @param line_no The index of the line in the text object, starting at 0.
126 * @returns The width in cm of the indicated line.
128 real
129 text_get_line_width(Text *text, int line_no)
131 return text_line_get_width(text->lines[line_no]);
134 /** Get the number of characters of the given line.
135 * @param text The text object;
136 * @param line_no The index of the line in the text object, starting at 0.
137 * @returns The number of UTF-8 characters of the indicated line.
140 text_get_line_strlen(Text *text, int line_no)
142 return text->strlen[line_no];
145 real
146 text_get_max_width(Text *text)
148 return text->max_width;
151 /** Get the *average* ascent of this Text object.
152 * @param a Text object
153 * @returns the average of the ascents of each line (height above baseline)
155 real
156 text_get_ascent(Text *text)
158 return text->ascent;
161 /** Get the *average* descent of this Text object.
162 * @param a Text object
163 * @returns the average of the descents of each line (height below baseline)
165 real
166 text_get_descent(Text *text)
168 return text->descent;
171 static ObjectChange *text_create_change(Text *text, enum change_type type,
172 gunichar ch, int pos, int row);
174 static void
175 calc_width(Text *text)
177 real width;
178 int i;
180 width = 0.0;
181 for (i = 0; i < text->numlines; i++) {
182 width = MAX(width, text_get_line_width(text, i));
185 text->max_width = width;
188 static void
189 calc_ascent_descent(Text *text)
191 real sig_a = 0.0,sig_d = 0.0;
192 guint i;
194 for ( i = 0; i < text->numlines; i++) {
195 sig_a += text_line_get_ascent(text->lines[i]);
196 sig_d += text_line_get_descent(text->lines[i]);
199 text->ascent = sig_a / (real)text->numlines;
200 text->descent = sig_d / (real)text->numlines;
203 static void
204 free_string(Text *text)
206 int i;
208 for (i=0;i<text->numlines;i++) {
209 text_line_destroy(text->lines[i]);
212 g_free(text->lines);
213 text->lines = NULL;
215 g_free(text->strlen);
216 text->strlen = NULL;
219 static void
220 set_string(Text *text, const char *string)
222 int numlines, i;
223 const char *s,*s2;
225 s = string;
227 numlines = 1;
228 if (s != NULL)
229 while ( (s = g_utf8_strchr(s, -1, '\n')) != NULL ) {
230 numlines++;
231 if (*s) {
232 s = g_utf8_next_char(s);
235 text->numlines = numlines;
236 text->lines = g_new0(TextLine *, numlines);
237 for (i = 0; i < numlines; i++) {
238 text->lines[i] = text_line_new("", text->font, text->height);
240 text->strlen = g_new(int, numlines);
242 s = string;
244 if (string == NULL) {
245 text_set_line_text(text, 0, "");
246 return;
249 for (i = 0; i < numlines; i++) {
250 gchar *string_line;
251 s2 = g_utf8_strchr(s, -1, '\n');
252 if (s2 == NULL) { /* No newline */
253 s2 = s + strlen(s);
255 string_line = g_strndup(s, s2 - s);
256 text_set_line_text(text, i, string_line);
257 g_free(string_line);
258 s = s2;
259 if (*s) {
260 s = g_utf8_next_char(s);
264 if (text->cursor_row >= text->numlines) {
265 text->cursor_row = text->numlines - 1;
268 if (text->cursor_pos > text_get_line_strlen(text, text->cursor_row)) {
269 text->cursor_pos = text_get_line_strlen(text, text->cursor_row);
273 void
274 text_set_string(Text *text, const char *string)
276 if (text->lines != NULL)
277 free_string(text);
279 set_string(text, string);
282 Text *
283 new_text(const char *string, DiaFont *font, real height,
284 Point *pos, Color *color, Alignment align)
286 Text *text;
288 text = g_new(Text, 1);
290 text->font = dia_font_ref(font);
291 text->height = height;
293 text->position = *pos;
294 text->color = *color;
295 text->alignment = align;
297 text->cursor_pos = 0;
298 text->cursor_row = 0;
300 text->focus.obj = NULL;
301 text->focus.has_focus = FALSE;
302 text->focus.user_data = (void *)text;
303 text->focus.key_event = text_key_event;
305 set_string(text, string);
307 calc_ascent_descent(text);
309 return text;
312 Text *
313 text_copy(Text *text)
315 Text *copy;
316 int i;
318 copy = g_new(Text, 1);
319 copy->numlines = text->numlines;
320 copy->lines = g_new(TextLine *, text->numlines);
321 copy->strlen = g_new(int, text->numlines);
323 copy->font = dia_font_ref(text->font);
324 copy->height = text->height;
325 copy->position = text->position;
326 copy->color = text->color;
327 copy->alignment = text->alignment;
329 for (i=0;i<text->numlines;i++) {
330 TextLine *text_line = text->lines[i];
331 copy->lines[i] = text_line_new(text_line_get_string(text_line),
332 text_line_get_font(text_line),
333 text_line_get_height(text_line));
336 copy->cursor_pos = 0;
337 copy->cursor_row = 0;
338 copy->focus.obj = NULL;
339 copy->focus.has_focus = FALSE;
340 copy->focus.user_data = (void *)copy;
341 copy->focus.key_event = text_key_event;
343 copy->ascent = text->ascent;
344 copy->descent = text->descent;
345 copy->max_width = text->max_width;
347 return copy;
350 void
351 text_destroy(Text *text)
353 free_string(text);
354 dia_font_unref(text->font);
355 g_free(text);
358 void
359 text_set_height(Text *text, real height)
361 int i;
362 text->height = height;
363 for (i = 0; i < text->numlines; i++) {
364 text_line_set_height(text->lines[i], height);
366 calc_width(text);
367 calc_ascent_descent(text);
370 void
371 text_set_font(Text *text, DiaFont *font)
373 DiaFont *old_font = text->font;
374 int i;
376 text->font = dia_font_ref(font);
377 dia_font_unref(old_font);
379 for (i = 0; i < text->numlines; i++) {
380 text_line_set_font(text->lines[i], font);
383 calc_width(text);
384 calc_ascent_descent(text);
387 void
388 text_set_position(Text *text, Point *pos)
390 text->position = *pos;
393 void
394 text_set_color(Text *text, Color *col)
396 text->color = *col;
399 void
400 text_set_alignment(Text *text, Alignment align)
402 text->alignment = align;
405 void
406 text_calc_boundingbox(Text *text, Rectangle *box)
408 calc_width(text);
409 calc_ascent_descent(text);
411 if (box == NULL) return; /* For those who just want the text info
412 updated */
413 box->left = text->position.x;
414 switch (text->alignment) {
415 case ALIGN_LEFT:
416 break;
417 case ALIGN_CENTER:
418 box->left -= text->max_width / 2.0;
419 break;
420 case ALIGN_RIGHT:
421 box->left -= text->max_width;
422 break;
425 box->right = box->left + text->max_width;
427 box->top = text->position.y - text->ascent;
429 box->bottom = box->top + text->height*text->numlines + text->descent;
431 if (text->focus.has_focus) {
432 real height = text->ascent + text->descent;
433 if (text->cursor_pos == 0) {
434 /* Half the cursor width */
435 box->left -= height/(CURSOR_HEIGHT_RATIO*2);
436 } else {
437 /* Half the cursor width. Assume that
438 if it isn't at position zero, it might be
439 at the last position possible. */
440 box->right += height/(CURSOR_HEIGHT_RATIO*2);
443 /* Account for the size of the cursor top and bottom */
444 box->top -= height/(CURSOR_HEIGHT_RATIO*2);
445 box->bottom += height/CURSOR_HEIGHT_RATIO;
449 char *
450 text_get_string_copy(Text *text)
452 int num,i;
453 char *str;
455 num = 0;
456 for (i=0;i<text->numlines;i++) {
457 num += strlen(text_get_line(text, i))+1;
460 str = g_malloc(num);
462 *str = 0;
464 for (i=0;i<text->numlines;i++) {
465 strcat(str, text_get_line(text, i));
466 if (i != (text->numlines-1)) {
467 strcat(str, "\n");
471 return str;
474 real
475 text_distance_from(Text *text, Point *point)
477 real dx, dy;
478 real topy, bottomy;
479 real left, right;
480 int line;
482 topy = text->position.y - text->ascent;
483 bottomy = topy + text->height*text->numlines;
484 if (point->y <= topy) {
485 dy = topy - point->y;
486 line = 0;
487 } else if (point->y >= bottomy) {
488 dy = point->y - bottomy;
489 line = text->numlines - 1;
490 } else {
491 dy = 0.0;
492 line = (int) floor( (point->y - topy) / text->height );
495 left = text->position.x;
496 switch (text->alignment) {
497 case ALIGN_LEFT:
498 break;
499 case ALIGN_CENTER:
500 left -= text_get_line_width(text, line) / 2.0;
501 break;
502 case ALIGN_RIGHT:
503 left -= text_get_line_width(text, line);
504 break;
506 right = left + text_get_line_width(text, line);
508 if (point->x <= left) {
509 dx = left - point->x;
510 } else if (point->x >= right) {
511 dx = point->x - right;
512 } else {
513 dx = 0.0;
516 return dx + dy;
519 void
520 text_draw(Text *text, DiaRenderer *renderer)
522 DIA_RENDERER_GET_CLASS(renderer)->draw_text(renderer, text);
524 if ((renderer->is_interactive) && (text->focus.has_focus)) {
525 real curs_x, curs_y;
526 real str_width_first;
527 real str_width_whole;
528 Point p1, p2;
529 real height = text->ascent+text->descent;
530 curs_y = text->position.y - text->ascent + text->cursor_row*text->height;
532 DIA_RENDERER_GET_CLASS(renderer)->set_font(renderer, text->font, text->height);
534 str_width_first =
535 DIA_RENDERER_GET_CLASS(renderer)->get_text_width(renderer,
536 text_get_line(text, text->cursor_row),
537 text->cursor_pos);
538 str_width_whole =
539 DIA_RENDERER_GET_CLASS(renderer)->get_text_width(renderer,
540 text_get_line(text, text->cursor_row),
541 text_get_line_strlen(text, text->cursor_row));
542 curs_x = text->position.x + str_width_first;
544 switch (text->alignment) {
545 case ALIGN_LEFT:
546 break;
547 case ALIGN_CENTER:
548 curs_x -= str_width_whole / 2.0;
549 break;
550 case ALIGN_RIGHT:
551 curs_x -= str_width_whole;
552 break;
555 p1.x = curs_x;
556 p1.y = curs_y;
557 p2.x = curs_x;
558 p2.y = curs_y + height;
560 DIA_RENDERER_GET_CLASS(renderer)->set_linestyle(renderer, LINESTYLE_SOLID);
561 DIA_RENDERER_GET_CLASS(renderer)->set_linewidth(renderer, height/CURSOR_HEIGHT_RATIO);
562 DIA_RENDERER_GET_CLASS(renderer)->draw_line(renderer, &p1, &p2, &color_black);
566 void
567 text_grab_focus(Text *text, DiaObject *object)
569 text->focus.obj = object;
570 request_focus(&text->focus);
573 void
574 text_set_cursor_at_end( Text* text )
576 text->cursor_row = text->numlines - 1 ;
577 text->cursor_pos = text_get_line_strlen(text, text->cursor_row) ;
580 /* The renderer is only used to determine where the click is, so is not
581 * required when no point is given. */
582 void
583 text_set_cursor(Text *text, Point *clicked_point,
584 DiaRenderer *renderer)
586 real str_width_whole;
587 real str_width_first;
588 real top;
589 real start_x;
590 int row;
591 int i;
593 if (clicked_point != NULL) {
594 top = text->position.y - text->ascent;
596 row = (int)floor((clicked_point->y - top) / text->height);
598 if (row < 0)
599 row = 0;
601 if (row >= text->numlines)
602 row = text->numlines - 1;
604 text->cursor_row = row;
605 text->cursor_pos = 0;
607 if (!renderer->is_interactive) {
608 message_error("Internal error: Select gives non interactive renderer!\n"
609 "val: %d\n", renderer->is_interactive);
610 return;
614 DIA_RENDERER_GET_CLASS(renderer)->set_font(renderer, text->font, text->height);
615 str_width_whole =
616 DIA_RENDERER_GET_CLASS(renderer)->get_text_width(renderer,
617 text_get_line(text, row),
618 text_get_line_strlen(text, row));
619 start_x = text->position.x;
620 switch (text->alignment) {
621 case ALIGN_LEFT:
622 break;
623 case ALIGN_CENTER:
624 start_x -= str_width_whole / 2.0;
625 break;
626 case ALIGN_RIGHT:
627 start_x -= str_width_whole;
628 break;
631 /* Do an ugly linear search for the cursor index:
632 TODO: Change to binary search */
634 for (i=0;i<=text_get_line_strlen(text, row);i++) {
635 str_width_first =
636 DIA_RENDERER_GET_CLASS(renderer)->get_text_width(renderer, text_get_line(text, row), i);
637 if (clicked_point->x - start_x >= str_width_first) {
638 text->cursor_pos = i;
639 } else {
640 return;
643 text->cursor_pos = text_get_line_strlen(text, row);
644 } else {
645 /* No clicked point, leave cursor where it is */
649 static void
650 text_join_lines(Text *text, int first_line)
652 gchar *combined_line;
653 int len1;
655 len1 = text_get_line_strlen(text, first_line);
657 combined_line = g_strconcat(text_get_line(text, first_line),
658 text_get_line(text, first_line + 1), NULL);
659 text_delete_line(text, first_line);
660 text_set_line_text(text, first_line, combined_line);
661 g_free(combined_line);
663 text->max_width = MAX(text->max_width, text_get_line_width(text, first_line));
665 text->cursor_row = first_line;
666 text->cursor_pos = len1;
669 static void
670 text_delete_forward(Text *text)
672 int row;
673 int i;
674 real width;
675 gchar *line;
676 gchar *utf8_before, *utf8_after;
677 gchar *str1, *str;
679 row = text->cursor_row;
681 if (text->cursor_pos >= text_get_line_strlen(text, row)) {
682 if (row + 1 < text->numlines)
683 text_join_lines(text, row);
684 return;
687 line = text_get_line(text, row);
688 utf8_before = g_utf8_offset_to_pointer(line, (glong)(text->cursor_pos));
689 utf8_after = g_utf8_offset_to_pointer(utf8_before, 1);
690 str1 = g_strndup(line, utf8_before - line);
691 str = g_strconcat(str1, utf8_after, NULL);
692 text_set_line_text(text, row, str);
693 g_free(str1);
694 g_free(str);
696 if (text->cursor_pos > text_get_line_strlen(text, text->cursor_row))
697 text->cursor_pos = text_get_line_strlen(text, text->cursor_row);
699 width = 0.0;
700 for (i = 0; i < text->numlines; i++) {
701 width = MAX(width, text_get_line_width(text, i));
703 text->max_width = width;
706 static void
707 text_delete_backward(Text *text)
709 int row;
710 int i;
711 real width;
712 gchar *line;
713 gchar *utf8_before, *utf8_after;
714 gchar *str1, *str;
716 row = text->cursor_row;
718 if (text->cursor_pos <= 0) {
719 if (row > 0)
720 text_join_lines(text, row-1);
721 return;
724 line = text_get_line(text, row);
725 utf8_before = g_utf8_offset_to_pointer(line, (glong)(text->cursor_pos - 1));
726 utf8_after = g_utf8_offset_to_pointer(utf8_before, 1);
727 str1 = g_strndup(line, utf8_before - line);
728 str = g_strconcat(str1, utf8_after, NULL);
729 text_set_line_text(text, row, str);
730 g_free(str);
731 g_free(str1);
733 text->cursor_pos --;
734 if (text->cursor_pos > text_get_line_strlen(text, text->cursor_row))
735 text->cursor_pos = text_get_line_strlen(text, text->cursor_row);
737 width = 0.0;
738 for (i = 0; i < text->numlines; i++) {
739 width = MAX(width, text_get_line_width(text, i));
741 text->max_width = width;
744 static void
745 text_split_line(Text *text)
747 int i;
748 char *line;
749 real width;
750 gchar *utf8_before;
751 gchar *str1, *str2;
753 /* Split the lines at cursor_pos */
754 line = text_get_line(text, text->cursor_row);
755 text_insert_line(text, text->cursor_row);
756 utf8_before = g_utf8_offset_to_pointer(line, (glong)(text->cursor_pos));
757 str1 = g_strndup(line, utf8_before - line);
758 str2 = g_strdup(utf8_before); /* Must copy before dealloc */
759 text_set_line_text(text, text->cursor_row, str1);
760 text_set_line_text(text, text->cursor_row + 1, str2);
761 g_free(str2);
762 g_free(str1);
764 text->cursor_row ++;
765 text->cursor_pos = 0;
767 width = 0.0;
768 for (i=0;i<text->numlines;i++) {
769 width = MAX(width, text_get_line_width(text, i));
771 text->max_width = width;
774 static void
775 text_insert_char(Text *text, gunichar c)
777 gchar ch[7];
778 int unilen;
779 int row;
780 gchar *line, *str;
781 gchar *utf8_before;
782 gchar *str1;
784 /* Make a string of the the char */
785 unilen = g_unichar_to_utf8 (c, ch);
786 ch[unilen] = 0;
788 row = text->cursor_row;
790 /* Copy the before and after parts with the new char in between */
791 line = text_get_line(text, row);
792 utf8_before = g_utf8_offset_to_pointer(line, (glong)(text->cursor_pos));
793 str1 = g_strndup(line, utf8_before - line);
794 str = g_strconcat(str1, ch, utf8_before, NULL);
795 text_set_line_text(text, row, str);
796 g_free(str);
797 g_free(str1);
799 text->cursor_pos++;
800 text->max_width = MAX(text->max_width, text_get_line_width(text, row));
803 static int
804 text_key_event(Focus *focus, guint keyval, const gchar *str, int strlen,
805 ObjectChange **change)
807 Text *text;
808 int return_val = FALSE;
809 int row, i;
810 const char *utf;
811 gunichar c;
813 *change = NULL;
815 text = (Text *)focus->user_data;
817 switch(keyval) {
818 case GDK_Up:
819 text->cursor_row--;
820 if (text->cursor_row<0)
821 text->cursor_row = 0;
823 if (text->cursor_pos > text_get_line_strlen(text, text->cursor_row))
824 text->cursor_pos = text_get_line_strlen(text, text->cursor_row);
826 break;
827 case GDK_Down:
828 text->cursor_row++;
829 if (text->cursor_row >= text->numlines)
830 text->cursor_row = text->numlines - 1;
832 if (text->cursor_pos > text_get_line_strlen(text, text->cursor_row))
833 text->cursor_pos = text_get_line_strlen(text, text->cursor_row);
835 break;
836 case GDK_Left:
837 text->cursor_pos--;
838 if (text->cursor_pos<0)
839 text->cursor_pos = 0;
840 break;
841 case GDK_Right:
842 text->cursor_pos++;
843 if (text->cursor_pos > text_get_line_strlen(text, text->cursor_row))
844 text->cursor_pos = text_get_line_strlen(text, text->cursor_row);
845 break;
846 case GDK_Home:
847 text->cursor_pos = 0;
848 break;
849 case GDK_End:
850 text->cursor_pos = text_get_line_strlen(text, text->cursor_row);
851 break;
852 case GDK_Delete:
853 return_val = TRUE;
854 row = text->cursor_row;
855 if (text->cursor_pos >= text_get_line_strlen(text, row)) {
856 if (row+1 < text->numlines) {
857 *change = text_create_change(text, TYPE_JOIN_ROW, 'Q',
858 text->cursor_pos, row);
859 } else {
860 return_val = FALSE;
861 break;
863 } else {
864 utf = text_get_line(text, row);
865 for (i = 0; i < text->cursor_pos; i++)
866 utf = g_utf8_next_char (utf);
867 c = g_utf8_get_char (utf);
868 *change = text_create_change (text, TYPE_DELETE_FORWARD, c,
869 text->cursor_pos, text->cursor_row);
871 text_delete_forward(text);
872 break;
873 case GDK_BackSpace:
874 return_val = TRUE;
875 row = text->cursor_row;
876 if (text->cursor_pos <= 0) {
877 if (row > 0) {
878 *change = text_create_change(text, TYPE_JOIN_ROW, 'Q',
879 text_get_line_strlen(text, row-1), row-1);
880 } else {
881 return_val = FALSE;
882 break;
884 } else {
885 utf = text_get_line(text, row);
886 for (i = 0; i < (text->cursor_pos - 1); i++)
887 utf = g_utf8_next_char (utf);
888 c = g_utf8_get_char (utf);
889 *change = text_create_change (text, TYPE_DELETE_BACKWARD, c,
890 text->cursor_pos - 1,
891 text->cursor_row);
893 text_delete_backward(text);
894 break;
895 case GDK_Return:
896 return_val = TRUE;
897 *change = text_create_change(text, TYPE_SPLIT_ROW, 'Q',
898 text->cursor_pos, text->cursor_row);
899 text_split_line(text);
900 break;
901 default:
902 if (str || (strlen>0)) {
903 return_val = TRUE;
904 utf = str;
905 for (utf = str; utf && *utf && strlen > 0 ;
906 utf = g_utf8_next_char (utf), strlen--) {
907 c = g_utf8_get_char (utf);
909 *change = text_create_change (text, TYPE_INSERT_CHAR, c,
910 text->cursor_pos, text->cursor_row);
911 text_insert_char (text, c);
914 break;
917 return return_val;
920 int text_is_empty(Text *text)
922 int i;
923 for (i = 0; i < text->numlines; i++) {
924 if (text_get_line_strlen(text, i) != 0) {
925 return FALSE;
928 return TRUE;
932 text_delete_all(Text *text, ObjectChange **change)
934 if (!text_is_empty(text)) {
935 *change = text_create_change(text, TYPE_DELETE_ALL,
936 0, text->cursor_pos, text->cursor_row);
938 text_set_string(text, "");
939 calc_ascent_descent(text);
940 return TRUE;
942 return FALSE;
945 void
946 data_add_text(AttributeNode attr, Text *text)
948 DataNode composite;
949 char *str;
951 composite = data_add_composite(attr, "text");
953 str = text_get_string_copy(text);
954 data_add_string(composite_add_attribute(composite, "string"),
955 str);
956 g_free(str);
957 data_add_font(composite_add_attribute(composite, "font"),
958 text->font);
959 data_add_real(composite_add_attribute(composite, "height"),
960 text->height);
961 data_add_point(composite_add_attribute(composite, "pos"),
962 &text->position);
963 data_add_color(composite_add_attribute(composite, "color"),
964 &text->color);
965 data_add_enum(composite_add_attribute(composite, "alignment"),
966 text->alignment);
970 Text *
971 data_text(AttributeNode text_attr)
973 char *string = NULL;
974 DiaFont *font;
975 real height;
976 Point pos = {0.0, 0.0};
977 Color col;
978 Alignment align;
979 AttributeNode attr;
980 DataNode composite_node;
981 Text *text;
983 composite_node = attribute_first_data(text_attr);
985 attr = composite_find_attribute(text_attr, "string");
986 if (attr != NULL)
987 string = data_string(attribute_first_data(attr));
989 height = 1.0;
990 attr = composite_find_attribute(text_attr, "height");
991 if (attr != NULL)
992 height = data_real(attribute_first_data(attr));
994 attr = composite_find_attribute(text_attr, "font");
995 if (attr != NULL) {
996 font = data_font(attribute_first_data(attr));
997 } else {
998 font = dia_font_new_from_style(DIA_FONT_SANS,1.0);
1001 attr = composite_find_attribute(text_attr, "pos");
1002 if (attr != NULL)
1003 data_point(attribute_first_data(attr), &pos);
1005 col = color_black;
1006 attr = composite_find_attribute(text_attr, "color");
1007 if (attr != NULL)
1008 data_color(attribute_first_data(attr), &col);
1010 align = ALIGN_LEFT;
1011 attr = composite_find_attribute(text_attr, "alignment");
1012 if (attr != NULL)
1013 align = data_enum(attribute_first_data(attr));
1015 text = new_text(string ? string : "", font, height, &pos, &col, align);
1016 if (font) dia_font_unref(font);
1017 if (string) g_free(string);
1018 return text;
1021 void
1022 text_get_attributes(Text *text, TextAttributes *attr)
1024 DiaFont *old_font;
1025 old_font = attr->font;
1026 attr->font = dia_font_ref(text->font);
1027 if (old_font != NULL) dia_font_unref(old_font);
1028 attr->height = text->height;
1029 attr->position = text->position;
1030 attr->color = text->color;
1031 attr->alignment = text->alignment;
1034 void
1035 text_set_attributes(Text *text, TextAttributes *attr)
1037 if (text->font != attr->font) {
1038 text_set_font(text, attr->font);
1040 text_set_height(text, attr->height);
1041 text->position = attr->position;
1042 text->color = attr->color;
1043 text->alignment = attr->alignment;
1046 static void
1047 text_change_apply(struct TextObjectChange *change, DiaObject *obj)
1049 Text *text = change->text;
1050 switch (change->type) {
1051 case TYPE_INSERT_CHAR:
1052 text->cursor_pos = change->pos;
1053 text->cursor_row = change->row;
1054 text_insert_char(text, change->ch);
1055 break;
1056 case TYPE_DELETE_BACKWARD:
1057 text->cursor_pos = change->pos+1;
1058 text->cursor_row = change->row;
1059 text_delete_backward(text);
1060 break;
1061 case TYPE_DELETE_FORWARD:
1062 text->cursor_pos = change->pos;
1063 text->cursor_row = change->row;
1064 text_delete_forward(text);
1065 break;
1066 case TYPE_SPLIT_ROW:
1067 text->cursor_pos = change->pos;
1068 text->cursor_row = change->row;
1069 text_split_line(text);
1070 break;
1071 case TYPE_JOIN_ROW:
1072 text_join_lines(text, change->row);
1073 break;
1074 case TYPE_DELETE_ALL:
1075 set_string(text, "");
1076 text->cursor_pos = 0;
1077 text->cursor_row = 0;
1078 break;
1082 static void
1083 text_change_revert(struct TextObjectChange *change, DiaObject *obj)
1085 Text *text = change->text;
1086 switch (change->type) {
1087 case TYPE_INSERT_CHAR:
1088 text->cursor_pos = change->pos;
1089 text->cursor_row = change->row;
1090 text_delete_forward(text);
1091 break;
1092 case TYPE_DELETE_BACKWARD:
1093 text->cursor_pos = change->pos;
1094 text->cursor_row = change->row;
1095 text_insert_char(text, change->ch);
1096 break;
1097 case TYPE_DELETE_FORWARD:
1098 text->cursor_pos = change->pos;
1099 text->cursor_row = change->row;
1100 text_insert_char(text, change->ch);
1101 text->cursor_pos = change->pos;
1102 text->cursor_row = change->row;
1103 break;
1104 case TYPE_SPLIT_ROW:
1105 text_join_lines(text, change->row);
1106 break;
1107 case TYPE_JOIN_ROW:
1108 text->cursor_pos = change->pos;
1109 text->cursor_row = change->row;
1110 text_split_line(text);
1111 break;
1112 case TYPE_DELETE_ALL:
1113 set_string(text, change->str);
1114 text->cursor_pos = change->pos;
1115 text->cursor_row = change->row;
1116 break;
1120 static void
1121 text_change_free(struct TextObjectChange *change) {
1122 g_free(change->str);
1125 static ObjectChange *
1126 text_create_change(Text *text, enum change_type type,
1127 gunichar ch, int pos, int row)
1129 struct TextObjectChange *change;
1131 change = g_new0(struct TextObjectChange, 1);
1133 change->obj_change.apply = (ObjectChangeApplyFunc) text_change_apply;
1134 change->obj_change.revert = (ObjectChangeRevertFunc) text_change_revert;
1135 change->obj_change.free = (ObjectChangeFreeFunc) text_change_free;
1137 change->text = text;
1138 change->type = type;
1139 change->ch = ch;
1140 change->pos = pos;
1141 change->row = row;
1142 if (type == TYPE_DELETE_ALL)
1143 change->str = text_get_string_copy(text);
1144 else
1145 change->str = NULL;
1146 return (ObjectChange *)change;
1149 gboolean
1150 apply_textattr_properties(GPtrArray *props,
1151 Text *text, const gchar *textname,
1152 TextAttributes *attrs)
1154 TextProperty *textprop =
1155 (TextProperty *)find_prop_by_name_and_type(props,textname,PROP_TYPE_TEXT);
1157 if ((!textprop) ||
1158 ((textprop->common.experience & (PXP_LOADED|PXP_SFO))==0 )) {
1159 /* most likely we're called after the dialog box has been applied */
1160 text_set_attributes(text,attrs);
1161 return TRUE;
1163 return FALSE;
1166 gboolean
1167 apply_textstr_properties(GPtrArray *props,
1168 Text *text, const gchar *textname,
1169 const gchar *str)
1171 TextProperty *textprop =
1172 (TextProperty *)find_prop_by_name_and_type(props,textname,PROP_TYPE_TEXT);
1174 if ((!textprop) ||
1175 ((textprop->common.experience & (PXP_LOADED|PXP_SFO))==0 )) {
1176 /* most likely we're called after the dialog box has been applied */
1177 text_set_string(text,str);
1178 return TRUE;
1180 return FALSE;