* (quote) utf8 actually works now
[modest] / src / modest-text-utils.c
1 /* modest-ui.c */
2
3 /* insert (c)/licensing information) */
4
5 #include <gtk/gtk.h>
6 #include <string.h>
7
8
9 #ifdef HAVE_CONFIG_H
10 #include <config.h>
11 #endif /*HAVE_CONFIG_H*/
12
13
14 /* private */
15 static GString *
16 get_next_line(GtkTextBuffer *b, GtkTextIter *iter);
17
18 static int
19 get_indent_level(const char *l);
20
21 static void
22 unquote_line(GString *l);
23
24 static void
25 append_quoted(GString *buf, const int indent, const GString *str, const int cutpoint);
26
27 static int
28 get_breakpoint_utf8(const gchar *s, const gint indent, const gint limit);
29
30 static int
31 get_breakpoint_ascii(const gchar *s, const gint indent, const gint limit);
32
33 static int
34 get_breakpoint(const gchar *s, const gint indent, const gint limit);
35
36 static GString *
37 get_next_line(GtkTextBuffer *b, GtkTextIter *iter)
38 {
39         GtkTextIter iter2;
40         gchar *tmp;
41         
42         gtk_text_buffer_get_iter_at_line_offset(b,
43                         &iter2,
44                         gtk_text_iter_get_line(iter),
45                         gtk_text_iter_get_chars_in_line(iter) -1
46                 );
47         tmp = gtk_text_buffer_get_text(b, &iter2, iter, FALSE);
48         gtk_text_iter_forward_line(iter);
49         return g_string_new(tmp);
50 }
51
52 static int
53 get_indent_level(const char *l)
54 {
55         int indent = 0;
56         
57         while (l[0]) {
58                 if (l[0] == '>') {
59                         indent++;
60                         if (l[1] == ' ') {
61                                 l++;
62                         }
63                 } else {
64                         break;
65                 }
66                 l++;
67                 
68         }
69         if (strcmp("-- ", l) == 0) {
70                 return -1-indent;
71         } else {
72                 return indent;
73         }
74 }
75
76 static void
77 unquote_line(GString *l) {
78         GString *r;
79         gchar *p;
80         
81         p = l->str;
82         while (p[0]) {
83                 if (p[0] == '>') {
84                         if (p[1] == ' ') {
85                                 p++;
86                         }
87                 } else {
88                         break;
89                 }
90                 p++;
91         }
92         g_string_erase (l, 0, p - l->str);
93 }
94
95 static void
96 append_quoted(GString *buf, int indent, const GString *str, const int cutpoint) {
97         int i;
98         
99         indent = indent < 0? abs(indent) -1 : indent;
100         for (i=0; i<=indent; i++) {
101                 g_string_append(buf, "> ");
102         }
103         if (cutpoint > 0) {
104                 g_string_append_len(buf, str->str, cutpoint);
105         } else {
106                 g_string_append(buf, str->str);
107         }
108         g_string_append(buf, "\n");
109 }
110
111 static int
112 get_breakpoint_utf8(const gchar *s, gint indent, const gint limit) {
113         gint index = 0;
114         const gchar *pos, *last;
115         gunichar *uni;
116         
117         indent = indent < 0? abs(indent) -1 : indent;
118         
119         last = NULL;
120         pos = s;
121         uni = g_utf8_to_ucs4_fast(s, -1, NULL);
122         while (pos[0]) {
123                 if ((index + 2 * indent > limit) && last) {
124                         g_free(uni);
125                         return last - s;
126                 }
127                 if (g_unichar_isspace(uni[index])) {
128                         last = pos;
129                 }
130                 pos = g_utf8_next_char(pos);
131                 index++;
132         }
133         g_free(uni);
134         return strlen(s);
135 }
136
137 static int
138 get_breakpoint_ascii(const gchar *s, const gint indent, const gint limit) {
139         gint i, last;
140         
141         last = strlen(s);
142         if (last + 2 * indent < limit)
143                 return last;
144         
145         for ( i=strlen(s) ; i>0; i-- ) {
146                 if (s[i] == ' ') {
147                         if (i + 2 * indent <= limit) {
148                                 return i;
149                         } else {
150                                 last = i;
151                         }
152                 }
153         }
154         return last;
155 }
156
157 static int
158 get_breakpoint(const gchar *s, const gint indent, const gint limit) {
159         
160         if (g_utf8_validate(s, -1, NULL)) {
161                 return get_breakpoint_utf8(s, indent, limit);
162         } else { /* assume ASCII */
163                 g_warning("invalid UTF-8 in msg");
164                 return get_breakpoint_ascii(s, indent, limit);
165         }
166 }       
167
168 gchar *
169 modest_text_utils_quote(GtkTextBuffer *buf, const gchar *from, const time_t sent_date, const int limit)
170 {
171         GtkTextIter iter;
172         gint indent, breakpoint, rem_indent;
173         gchar sent_str[101];
174         GString *q, *l, *remaining; /* quoted msg, line */
175         
176
177         /* format sent_date */
178         strftime(sent_str, 100, "%c", localtime(&sent_date));
179         q = g_string_new("");
180         g_string_printf(q, "On %s, %s wrote:\n", sent_str, from);
181         
182         /* remaining will store the rest of the line if we have to break it */
183         remaining = g_string_new("");
184         gtk_text_buffer_get_iter_at_line(buf, &iter, 0);
185         do {
186                 l = get_next_line(buf, &iter);
187                 indent = get_indent_level(l->str);
188                 unquote_line(l);
189                 
190                 if (remaining->len) {
191                         if (l->len && indent == rem_indent) {
192                                 g_string_prepend(l, " ");
193                                 g_string_prepend(l, remaining->str);
194                         } else {
195                                 do {
196                                         breakpoint = get_breakpoint(remaining->str, rem_indent, limit);
197                                         append_quoted(q, rem_indent, remaining, breakpoint);
198                                         g_string_erase(remaining, 0, breakpoint);
199                                         if (remaining->str[0] == ' ') {
200                                                 g_string_erase(remaining, 0, 1);
201                                         }               
202                                 } while (remaining->len);
203                         }
204                 }
205                 g_string_free(remaining, TRUE);
206                 breakpoint = get_breakpoint(l->str, indent, limit);
207                 remaining = g_string_new(l->str + breakpoint);
208                 if (remaining->str[0] == ' ') {
209                         g_string_erase(remaining, 0, 1);
210                 }
211                 rem_indent = indent;
212                 append_quoted(q, indent, l, breakpoint);
213                 g_string_free(l, TRUE);
214         } while (!gtk_text_iter_is_end(&iter));
215         
216         return g_string_free(q, FALSE);
217 }