5742fbb9e7437d4aca180090ec44a332e2a10470
[modest] / src / modest-text-utils.c
1 /* Copyright (c) 2006, Nokia Corporation
2  * All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions are
6  * met:
7  *
8  * * Redistributions of source code must retain the above copyright
9  *   notice, this list of conditions and the following disclaimer.
10  * * Redistributions in binary form must reproduce the above copyright
11  *   notice, this list of conditions and the following disclaimer in the
12  *   documentation and/or other materials provided with the distribution.
13  * * Neither the name of the Nokia Corporation nor the names of its
14  *   contributors may be used to endorse or promote products derived from
15  *   this software without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
18  * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
19  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
20  * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
21  * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
22  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
23  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
24  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
25  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
26  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28  */
29
30
31 /* modest-ui.c */
32
33 #include <gtk/gtk.h>
34 #include <string.h>
35 #include <stdlib.h>
36
37 #include "modest-text-utils.h"
38
39
40 #ifdef HAVE_CONFIG_H
41 #include <config.h>
42 #endif /*HAVE_CONFIG_H */
43
44 /* private */
45 static GString *get_next_line (const char *b, const gsize blen, const gchar * iter);
46 static int get_indent_level (const char *l);
47 static void unquote_line (GString * l);
48 static void append_quoted (GString * buf, const int indent,
49                            const GString * str, const int cutpoint);
50 static int get_breakpoint_utf8 (const gchar * s, const gint indent,
51                                 const gint limit);
52 static int get_breakpoint_ascii (const gchar * s, const gint indent,
53                                  const gint limit);
54 static int get_breakpoint (const gchar * s, const gint indent,
55                            const gint limit);
56
57 static GString *
58 get_next_line (const gchar * b, const gsize blen, const gchar * iter)
59 {
60         GString *gs;
61         const gchar *i0;
62         
63         if (iter > b + blen)
64                 return g_string_new("");
65         
66         i0 = iter;
67         while (iter[0]) {
68                 if (iter[0] == '\n')
69                         break;
70                 iter++;
71         }
72         gs = g_string_new_len (i0, iter - i0);
73         return gs;
74 }
75 static int
76 get_indent_level (const char *l)
77 {
78         int indent = 0;
79
80         while (l[0]) {
81                 if (l[0] == '>') {
82                         indent++;
83                         if (l[1] == ' ') {
84                                 l++;
85                         }
86                 } else {
87                         break;
88                 }
89                 l++;
90
91         }
92
93         /*      if we hit the signature marker "-- ", we return -(indent + 1). This
94          *      stops reformatting.
95          */
96         if (strcmp (l, "-- ") == 0) {
97                 return -1 - indent;
98         } else {
99                 return indent;
100         }
101 }
102
103 static void
104 unquote_line (GString * l)
105 {
106         gchar *p;
107
108         p = l->str;
109         while (p[0]) {
110                 if (p[0] == '>') {
111                         if (p[1] == ' ') {
112                                 p++;
113                         }
114                 } else {
115                         break;
116                 }
117                 p++;
118         }
119         g_string_erase (l, 0, p - l->str);
120 }
121
122 static void
123 append_quoted (GString * buf, int indent, const GString * str,
124                const int cutpoint)
125 {
126         int i;
127
128         indent = indent < 0 ? abs (indent) - 1 : indent;
129         for (i = 0; i <= indent; i++) {
130                 g_string_append (buf, "> ");
131         }
132         if (cutpoint > 0) {
133                 g_string_append_len (buf, str->str, cutpoint);
134         } else {
135                 g_string_append (buf, str->str);
136         }
137         g_string_append (buf, "\n");
138 }
139
140 static int
141 get_breakpoint_utf8 (const gchar * s, gint indent, const gint limit)
142 {
143         gint index = 0;
144         const gchar *pos, *last;
145         gunichar *uni;
146
147         indent = indent < 0 ? abs (indent) - 1 : indent;
148
149         last = NULL;
150         pos = s;
151         uni = g_utf8_to_ucs4_fast (s, -1, NULL);
152         while (pos[0]) {
153                 if ((index + 2 * indent > limit) && last) {
154                         g_free (uni);
155                         return last - s;
156                 }
157                 if (g_unichar_isspace (uni[index])) {
158                         last = pos;
159                 }
160                 pos = g_utf8_next_char (pos);
161                 index++;
162         }
163         g_free (uni);
164         return strlen (s);
165 }
166
167 static int
168 get_breakpoint_ascii (const gchar * s, const gint indent, const gint limit)
169 {
170         gint i, last;
171
172         last = strlen (s);
173         if (last + 2 * indent < limit)
174                 return last;
175
176         for (i = strlen (s); i > 0; i--) {
177                 if (s[i] == ' ') {
178                         if (i + 2 * indent <= limit) {
179                                 return i;
180                         } else {
181                                 last = i;
182                         }
183                 }
184         }
185         return last;
186 }
187
188 static int
189 get_breakpoint (const gchar * s, const gint indent, const gint limit)
190 {
191
192         if (g_utf8_validate (s, -1, NULL)) {
193                 return get_breakpoint_utf8 (s, indent, limit);
194         } else {                /* assume ASCII */
195                 //g_warning("invalid UTF-8 in msg");
196                 return get_breakpoint_ascii (s, indent, limit);
197         }
198 }
199
200
201 gchar *
202 modest_text_utils_quote (const gchar * to_quote, const gchar * from,
203                          const time_t sent_date, const int limit)
204 {
205         const gchar *iter;
206         gint indent, breakpoint, rem_indent = 0;
207         gchar sent_str[101];
208         GString *q, *l, *remaining;
209         gsize len;
210
211         /* format sent_date */
212         strftime (sent_str, 100, "%c", localtime (&sent_date));
213         q = g_string_new ("");
214         g_string_printf (q, "On %s, %s wrote:\n", sent_str, from);
215
216         /* remaining will store the rest of the line if we have to break it */
217         remaining = g_string_new ("");
218
219         iter = to_quote;
220         len = strlen(to_quote);
221         do {
222                 l = get_next_line (to_quote, len, iter);
223                 iter = iter + l->len + 1;
224                 indent = get_indent_level (l->str);
225                 unquote_line (l);
226
227                 if (remaining->len) {
228                         if (l->len && indent == rem_indent) {
229                                 g_string_prepend (l, " ");
230                                 g_string_prepend (l, remaining->str);
231                         } else {
232                                 do {
233                                         breakpoint =
234                                                 get_breakpoint (remaining->     str,
235                                                                 rem_indent,
236                                                                 limit);
237                                         append_quoted (q, rem_indent,
238                                                        remaining, breakpoint);
239                                         g_string_erase (remaining, 0,
240                                                         breakpoint);
241                                         if (remaining->str[0] == ' ') {
242                                                 g_string_erase (remaining, 0,
243                                                                 1);
244                                         }
245                                 } while (remaining->len);
246                         }
247                 }
248                 g_string_free (remaining, TRUE);
249                 breakpoint = get_breakpoint (l->str, indent, limit);
250                 remaining = g_string_new (l->str + breakpoint);
251                 if (remaining->str[0] == ' ') {
252                         g_string_erase (remaining, 0, 1);
253                 }
254                 rem_indent = indent;
255                 append_quoted (q, indent, l, breakpoint);
256                 g_string_free (l, TRUE);
257         } while ((iter < to_quote + len) || (remaining->str[0]));
258
259         return g_string_free (q, FALSE);
260 }