#define HYPERLINKIFY_MAX_LENGTH (1024*50)
-/*
- * we mark the ampersand with \007 when converting text->html
- * because after text->html we do hyperlink detecting, which
- * could be screwed up by the ampersand
- */
-#define MARK_AMP '\007'
-#define MARK_AMP_STR "\007"
-
/*
* we need these regexps to find URLs in plain text e-mails
const gchar* prefix;
};
+
+/*
+ * we mark the ampersand with \007 when converting text->html
+ * because after text->html we do hyperlink detecting, which
+ * could be screwed up by the ampersand.
+ * ie. 1<3 ==> 1\007lt;3
+ */
+#define MARK_AMP '\007'
+#define MARK_AMP_STR "\007"
+
+/* mark & separately, because they are parts of urls.
+ * ie. a&b => a\006amp;b, but a>b => a\007gt;b
+ *
+ * we need to handle '&' separately, because it can be part of URIs
+ * (as in href="http://foo.bar?a=1&b=1"), so inside those URIs
+ * we need to re-replace \006amp; with '&' again, while outside uri's
+ * it will be '&'
+ *
+ * yes, it's messy, but a consequence of doing text->html first, then hyperlinkify
+ */
+#define MARK_AMP_URI '\006'
+#define MARK_AMP_URI_STR "\006"
+
+
+/* note: match MARK_AMP_URI_STR as well, because after txt->html, a '&' will look like $(MARK_AMP_URI_STR)"amp;" */
#define MAIL_VIEWER_URL_MATCH_PATTERNS { \
- { "(file|rtsp|http|ftp|https)://[-A-Za-z0-9_$.+!*(),;:@%&=?/~#]+[-A-Za-z0-9_$%&=?/~#]",\
+ { "(file|rtsp|http|ftp|https|mms|mmsh|rtsp|rdp|lastfm)://[-a-z0-9_$.+!*(),;:@%=?/~#" MARK_AMP_URI_STR \
+ "]+[-a-z0-9_$%" MARK_AMP_URI_STR "=?/~#]", \
NULL, NULL },\
- { "www\\.[-a-z0-9.]+[-a-z0-9](:[0-9]*)?(/[-A-Za-z0-9_$.+!*(),;:@%&=?/~#]*[^]}\\),?!;:\"]?)?",\
+ { "www\\.[-a-z0-9_$.+!*(),;:@%=?/~#" MARK_AMP_URI_STR "]+[-a-z0-9_$%" MARK_AMP_URI_STR "=?/~#]",\
NULL, "http://" }, \
- { "ftp\\.[-a-z0-9.]+[-a-z0-9](:[0-9]*)?(/[-A-Za-z0-9_$.+!*(),;:@%&=?/~#]*[^]}\\),?!;:\"]?)?",\
+ { "ftp\\.[-a-z0-9_$.+!*(),;:@%=?/~#" MARK_AMP_URI_STR "]+[-a-z0-9_$%" MARK_AMP_URI_STR "=?/~#]",\
NULL, "ftp://" },\
- { "(voipto|callto|chatto|jabberto|xmpp):[-_a-z@0-9.\\+]+", \
+ { "(jabberto|voipto|sipto|sip|chatto|xmpp):[-_a-z@0-9.+]+", \
NULL, NULL}, \
{ "mailto:[-_a-z0-9.\\+]+@[-_a-z0-9.]+", \
NULL, NULL},\
const gchar *to,
const gchar *subject)
{
+ g_return_val_if_fail (sent, NULL);
+
return g_strdup_printf ("%s\n%s %s\n%s %s\n%s %s\n%s %s\n",
FORWARD_STRING,
FROM_STRING, (from)?from:"",
/* replace with special html chars where needed*/
for (i = 0; i != n; ++i) {
- char kar = data[i];
+ guchar kar = data[i];
if (space_seen && kar != ' ') {
g_string_append_c (html, ' ');
/* we artificially insert a breakpoint (newline)
* after 256, to make sure our lines are not so long
* they will DOS the regexping later
+ * Also, check that kar is ASCII to make sure that we
+ * don't break a UTF8 char in two
*/
- if (++break_dist == 256) {
+ if (++break_dist >= 256 && kar < 127) {
g_string_append_c (html, '\n');
break_dist = 0;
}
switch (kar) {
case 0:
- case MARK_AMP: /* this is a temp place holder for '&'; we can only
+ case MARK_AMP:
+ case MARK_AMP_URI:
+ /* this is a temp place holder for '&'; we can only
* set the real '&' after hyperlink translation, otherwise
* we might screw that up */
break; /* ignore embedded \0s and MARK_AMP */
case '<' : g_string_append (html, MARK_AMP_STR "lt;"); break;
case '>' : g_string_append (html, MARK_AMP_STR "gt;"); break;
- case '&' : g_string_append (html, MARK_AMP_STR "amp;"); break;
+ case '&' : g_string_append (html, MARK_AMP_URI_STR "amp;"); break; /* special case */
case '"' : g_string_append (html, MARK_AMP_STR "quot;"); break;
/* don't convert ' --> wpeditor will try to re-convert it... */
int i;
/* replace all our MARK_AMPs with real ones */
for (i = 0; i != html->len; ++i)
- if ((html->str)[i] == MARK_AMP)
+ if ((html->str)[i] == MARK_AMP || (html->str)[i] == MARK_AMP_URI)
(html->str)[i] = '&';
}
return;
}
-#if 0
-GSList *
-modest_text_utils_split_addresses_list (const gchar *addresses)
-{
- gchar *current, *start, *last_blank;
- GSList *result = NULL;
-
- start = (gchar *) addresses;
- current = start;
- last_blank = start;
-
- while (*current != '\0') {
- if ((start == current)&&((*current == ' ')||(*current == ',')||(*current == ';'))) {
- start = g_utf8_next_char (start);
- last_blank = current;
- } else if ((*current == ',')||(*current == ';')) {
- gchar *new_address = NULL;
- new_address = g_strndup (start, current - last_blank);
- result = g_slist_prepend (result, new_address);
- start = g_utf8_next_char (current);
- last_blank = start;
- } else if (*current == '\"') {
- if (current == start) {
- current = g_utf8_next_char (current);
- start = g_utf8_next_char (start);
- }
- while ((*current != '\"')&&(*current != '\0'))
- current = g_utf8_next_char (current);
- }
-
- current = g_utf8_next_char (current);
- }
-
- if (start != current) {
- gchar *new_address = NULL;
- new_address = g_strndup (start, current - last_blank);
- result = g_slist_prepend (result, new_address);
- }
-
- result = g_slist_reverse (result);
- return result;
-
-}
-#endif
-
-
-
GSList *
modest_text_utils_split_addresses_list (const gchar *addresses)
const gchar *my_addrs = addresses;
const gchar *end;
gchar *addr;
+
+ g_return_val_if_fail (addresses, NULL);
/* skip any space, ',', ';' at the start */
while (my_addrs && (my_addrs[0] == ' ' || my_addrs[0] == ',' || my_addrs[0] == ';'))
gsize len;
gchar *attachments_string = NULL;
- /* remaining will store the rest of the line if we have to break it */
q = g_string_new ("\n");
+ if (signature != NULL) {
+ q = g_string_append (q, signature);
+ q = g_string_append_c (q, '\n');
+ }
q = g_string_append (q, cite);
q = g_string_append_c (q, '\n');
+
+ /* remaining will store the rest of the line if we have to break it */
remaining = g_string_new ("");
iter = text;
q = g_string_append (q, attachments_string);
g_free (attachments_string);
- if (signature != NULL) {
- q = g_string_append_c (q, '\n');
- q = g_string_append (q, signature);
- }
-
return g_string_free (q, FALSE);
}
patterns[i].preg = g_slice_new0 (regex_t);
/* this should not happen */
- g_return_val_if_fail (regcomp (patterns[i].preg, patterns[i].regex,
- REG_ICASE|REG_EXTENDED|REG_NEWLINE) == 0, FALSE);
+ if (regcomp (patterns[i].preg, patterns[i].regex,
+ REG_ICASE|REG_EXTENDED|REG_NEWLINE) != 0) {
+ g_warning ("%s: error in regexp:\n%s\n", __FUNCTION__, patterns[i].regex);
+ return FALSE;
+ }
}
return TRUE;
}
+/* replace all occurences of needle in haystack with repl*/
+static gchar*
+replace_string (const gchar *haystack, const gchar *needle, gchar repl)
+{
+ gchar *str, *cursor;
+
+ if (!haystack || !needle || strlen(needle) == 0)
+ return haystack ? g_strdup(haystack) : NULL;
+
+ str = g_strdup (haystack);
+
+ for (cursor = str; cursor && *cursor; ++cursor) {
+ if (g_str_has_prefix (cursor, needle)) {
+ cursor[0] = repl;
+ memmove (cursor + 1,
+ cursor + strlen (needle),
+ strlen (cursor + strlen (needle)) + 1);
+ }
+ }
+
+ return str;
+}
+
static void
hyperlinkify_plain_text (GString *txt)
{
gchar *url = g_strndup (txt->str + match->offset, match->len);
gchar *repl = NULL; /* replacement */
+ /* the string still contains $(MARK_AMP_URI_STR)"amp;" for each
+ * '&' in the original, because of the text->html conversion.
+ * in the href-URL (and only there), we must convert that back to
+ * '&'
+ */
+ gchar *href_url = replace_string (url, MARK_AMP_URI_STR "amp;", '&');
+
/* the prefix is NULL: use the one that is already there */
repl = g_strdup_printf ("<a href=\"%s%s\">%s</a>",
match->prefix ? match->prefix : EMPTY_STRING,
- url, url);
+ href_url, url);
/* replace the old thing with our hyperlink
* replacement thing */
g_free (url);
g_free (repl);
+ g_free (href_url);
g_slice_free (url_match_t, match);
}
/* if it's not case sensitive */
if (!insensitive) {
- /* optimization: short cut if first char is ascii */
+ /* optimization: shortcut if first char is ascii */
if (((s1[0] & 0xf0)== 0) && ((s2[0] & 0xf0) == 0))
return s1[0] - s2[0];
return FALSE; /* it's valid! */
}
+
+gchar *
+modest_text_utils_label_get_selection (GtkLabel *label)
+{
+ gint start, end;
+ gchar *selection;
+
+ if (gtk_label_get_selection_bounds (GTK_LABEL (label), &start, &end)) {
+ const gchar *start_offset;
+ const gchar *end_offset;
+ start_offset = gtk_label_get_text (GTK_LABEL (label));
+ start_offset = g_utf8_offset_to_pointer (start_offset, start);
+ end_offset = gtk_label_get_text (GTK_LABEL (label));
+ end_offset = g_utf8_offset_to_pointer (end_offset, end);
+ selection = g_strndup (start_offset, end_offset - start_offset);
+ return selection;
+ } else {
+ return g_strdup ("");
+ }
+}
+
+static gboolean
+_forward_search_image_char (gunichar ch,
+ gpointer userdata)
+{
+ return (ch == 0xFFFC);
+}
+
+gboolean
+modest_text_utils_buffer_selection_is_valid (GtkTextBuffer *buffer)
+{
+ gboolean result;
+ GtkTextIter start, end;
+
+ g_return_val_if_fail (GTK_IS_TEXT_BUFFER (buffer), FALSE);
+
+ result = gtk_text_buffer_get_has_selection (GTK_TEXT_BUFFER (buffer));
+
+ /* check there are no images in selection */
+ if (result) {
+ gtk_text_buffer_get_selection_bounds (buffer, &start, &end);
+ if (gtk_text_iter_get_char (&start)== 0xFFFC)
+ result = FALSE;
+ else {
+ gtk_text_iter_backward_char (&end);
+ if (gtk_text_iter_forward_find_char (&start, _forward_search_image_char,
+ NULL, &end))
+ result = FALSE;
+ }
+
+ }
+
+ return result;
+}
+
+gchar *
+modest_text_utils_escape_mnemonics (const gchar *text)
+{
+ const gchar *p;
+ GString *result = NULL;
+
+ if (text == NULL)
+ return NULL;
+
+ result = g_string_new ("");
+ for (p = text; *p != '\0'; p++) {
+ if (*p == '_')
+ result = g_string_append (result, "__");
+ else
+ result = g_string_append_c (result, *p);
+ }
+
+ return g_string_free (result, FALSE);
+}