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