3039cedef672931259414a9ef1a6a51708c87b6c
[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         while (l[0]) {
57                 if (l[0] == '>') {
58                         indent++;
59                         if (l[1] == ' ') {
60                                 l++;
61                         }
62                 } else {
63                         break;
64                 }
65                 l++;
66                 
67         }
68         return indent;
69 }
70
71 static void
72 unquote_line(GString *l) {
73         GString *r;
74         gchar *p;
75         
76         p = l->str;
77         while (p[0]) {
78                 if (p[0] == '>') {
79                         if (p[1] == ' ') {
80                                 p++;
81                         }
82                 } else {
83                         break;
84                 }
85                 p++;
86         }
87         g_string_erase (l, 0, p - l->str);
88 }
89
90 static void
91 append_quoted(GString *buf, const int indent, const GString *str, const int cutpoint) {
92         int i;
93         
94         for (i=0; i<=indent; i++) {
95                 g_string_append(buf, "> ");
96         }
97         if (cutpoint > 0) {
98                 g_string_append_len(buf, str->str, cutpoint);
99         } else {
100                 g_string_append(buf, str->str);
101         }
102         g_string_append(buf, "\n");
103 }
104
105 static int
106 get_breakpoint_utf8(const gchar *s, const gint indent, const gint limit) {
107         gint index = 0;
108         const gchar *pos, *last;
109         gunichar *uni;
110         
111         last = NULL;
112         pos = s;
113         uni = g_utf8_to_ucs4_fast(s, -1, NULL);
114         while (pos[0]) {
115                 if ((index + 2 * indent > limit) && last) {
116                         g_free(uni);
117                         return last - s;
118                 }
119                 if (g_unichar_isspace(uni[index])) {
120                         last = pos;
121                 }
122                 pos = g_utf8_next_char(pos);
123                 index++;
124         }
125         g_free(uni);
126         return index;
127 }
128
129 static int
130 get_breakpoint_ascii(const gchar *s, const gint indent, const gint limit) {
131         gint i, last;
132         
133         last = strlen(s);
134         if (last + 2 * indent < limit)
135                 return last;
136         
137         for ( i=strlen(s) ; i>0; i-- ) {
138                 if (s[i] == ' ') {
139                         if (i + 2 * indent <= limit) {
140                                 return i;
141                         } else {
142                                 last = i;
143                         }
144                 }
145         }
146         return last;
147 }
148
149 static int
150 get_breakpoint(const gchar *s, const gint indent, const gint limit) {
151         
152         if (g_utf8_validate(s, -1, NULL)) {
153                 return get_breakpoint_utf8(s, indent, limit);
154         } else { /* assume ASCII */
155                 g_warning("invalid UTF-8 in msg");
156                 return get_breakpoint_ascii(s, indent, limit);
157         }
158 }       
159
160 gchar *
161 modest_text_utils_quote(GtkTextBuffer *buf, const gchar *from, const time_t sent_date, const int limit)
162 {
163         GtkTextIter iter;
164         gint indent, breakpoint, rem_indent;
165         gchar sent_str[101];
166         GString *q, *l, *remaining; /* quoted msg, line */
167         
168
169         /* format sent_date */
170         strftime(sent_str, 100, "%c", localtime(&sent_date));
171         q = g_string_new("");
172         g_string_printf(q, "On %s, %s wrote:\n", sent_str, from);
173         
174         /* remaining will store the rest of the line if we have to break it */
175         remaining = g_string_new("");
176         gtk_text_buffer_get_iter_at_line(buf, &iter, 0);
177         do {
178                 l = get_next_line(buf, &iter);
179                 indent = get_indent_level(l->str);
180                 unquote_line(l);
181                 
182                 if (remaining->len) {
183                         if (l->len && indent == rem_indent) {
184                                 g_string_prepend(l, " ");
185                                 g_string_prepend(l, remaining->str);
186                         } else {
187                                 do {
188                                         breakpoint = get_breakpoint(remaining->str, rem_indent, limit);
189                                         append_quoted(q, rem_indent, remaining, breakpoint);
190                                         g_string_erase(remaining, 0, breakpoint);
191                                         if (remaining->str[0] == ' ') {
192                                                 g_string_erase(remaining, 0, 1);
193                                         }               
194                                 } while (remaining->len);
195                         }
196                 }
197                 g_string_free(remaining, TRUE);
198                 breakpoint = get_breakpoint(l->str, indent, limit);
199                 remaining = g_string_new(l->str + breakpoint);
200                 if (remaining->str[0] == ' ') {
201                         g_string_erase(remaining, 0, 1);
202                 }
203                 rem_indent = indent;
204                 append_quoted(q, indent, l, breakpoint);
205                 g_string_free(l, TRUE);
206         } while (!gtk_text_iter_is_end(&iter));
207         
208         return g_string_free(q, FALSE);
209 }