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